Update async component definitions

This commit is contained in:
Ramon Wenger 2022-03-24 17:31:18 +01:00
parent 5bc0c29ea0
commit 39e7d27587
73 changed files with 4697 additions and 4276 deletions

View File

@ -1,30 +1,36 @@
<template> <template>
<div class="add-content"> <div class="add-content">
<a class="add-content__button" @click="addContent"> <a
class="add-content__button"
@click="addContent"
>
<add-pointer class="add-content__icon" /> <add-pointer class="add-content__icon" />
</a> </a>
</div> </div>
</template> </template>
<script> <script>
import { CREATE_CONTENT_BLOCK_AFTER_PAGE, CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE } from '@/router/module.names'; import {
CREATE_CONTENT_BLOCK_AFTER_PAGE,
CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
} from '@/router/module.names';
import {defineAsyncComponent} from 'vue';
const AddPointer = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddPointer'); const AddPointer = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddPointer'));
export default { export default {
props: { props: {
where: { where: {
type: Object, type: Object,
validator(prop) { validator(prop) {
return ( return Object.prototype.hasOwnProperty.call(prop, 'after' )
Object.prototype.hasOwnProperty.call(prop, 'after') || Object.prototype.hasOwnProperty.call(prop, 'parent') || Object.prototype.hasOwnProperty.call(prop, 'parent');
); }
},
}, },
}, },
components: { components: {
AddPointer, AddPointer
}, },
computed: { computed: {
@ -39,8 +45,9 @@ export default {
}, },
slug() { slug() {
return this.$route.params.slug; return this.$route.params.slug;
}
}, },
},
methods: { methods: {
addContent() { addContent() {
@ -53,26 +60,26 @@ export default {
name: CREATE_CONTENT_BLOCK_AFTER_PAGE, name: CREATE_CONTENT_BLOCK_AFTER_PAGE,
params: { params: {
after: this.after.id, after: this.after.id,
slug: this.slug, slug: this.slug
}, }
}; };
} else { } else {
route = { route = {
name: CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE, name: CREATE_CONTENT_BLOCK_UNDER_PARENT_PAGE,
params: { params: {
parent: this.parent.id, parent: this.parent.id
}, }
}; };
} }
this.$router.push(route); this.$router.push(route);
} }
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.add-content { .add-content {
display: none; display: none;

View File

@ -1,23 +1,27 @@
<template> <template>
<div class="add-content-element" @click="$emit('add-element', index)"> <div
class="add-content-element"
@click="$emit('add-element', index)"
>
<add-icon class="add-content-element__icon" /> <add-icon class="add-content-element__icon" />
</div> </div>
</template> </template>
<script> <script>
const AddIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddIcon'); import {defineAsyncComponent} from 'vue';
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon'));
export default { export default {
props: ['index'], props: ['index'],
components: { components: {
AddIcon, AddIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
.add-content-element { .add-content-element {
display: flex; display: flex;

View File

@ -11,27 +11,27 @@
</template> </template>
<script> <script>
const AddIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddIcon.vue'); import {defineAsyncComponent} from 'vue';
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon.vue'));
export default { export default {
props: { props: {
route: { route: {
type: String, type: String,
default: null, default: null
}, },
reverse: { reverse: { // use reverse colors
// use reverse colors
type: Boolean, type: Boolean,
default: false, default: false
}, },
click: { click: {
type: Function, type: Function,
default: null, default: null
}, }
}, },
components: { components: {
AddIcon, AddIcon
}, },
computed: { computed: {
@ -40,19 +40,17 @@ export default {
return this.route ? 'router-link' : 'a'; return this.route ? 'router-link' : 'a';
}, },
properties() { properties() {
return this.route return this.route ? {
? {
to: this.route, to: this.route,
tag: 'div', tag: 'div'
} : {};
} }
: {};
},
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.add-widget { .add-widget {
display: none; display: none;

View File

@ -12,8 +12,9 @@
import { MODULE_PAGE } from '@/router/module.names'; import { MODULE_PAGE } from '@/router/module.names';
import { ROOMS_PAGE } from '@/router/room.names'; import { ROOMS_PAGE } from '@/router/room.names';
import { PROJECTS_PAGE } from '@/router/portfolio.names'; import { PROJECTS_PAGE } from '@/router/portfolio.names';
import {defineAsyncComponent} from 'vue';
const ChevronLeft = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronLeft'); const ChevronLeft = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronLeft'));
export default { export default {
props: { props: {

View File

@ -8,47 +8,54 @@
:key="index" :key="index"
@click="$emit('input', color.name)" @click="$emit('input', color.name)"
> >
<div :class="'color-chooser__color--' + color.name" class="color-chooser__color"> <div
<tick class="color-chooser__selected-icon" v-if="selectedColor === color.name" /> :class="'color-chooser__color--' + color.name"
class="color-chooser__color"
>
<tick
class="color-chooser__selected-icon"
v-if="selectedColor === color.name"
/>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
const Tick = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Tick'); import {defineAsyncComponent} from 'vue';
const Tick = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Tick'));
export default { export default {
props: ['selectedColor'], props: ['selectedColor'],
components: { components: {
Tick, Tick
}, },
data() { data() {
return { return {
colors: [ colors: [
{ {
name: 'yellow', name: 'yellow'
}, },
{ {
name: 'blue', name: 'blue'
}, },
{ {
name: 'red', name: 'red'
}, },
{ {
name: 'green', name: 'green'
}, }
], ]
}; };
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
@import '@/styles/_mixins.scss'; @import "@/styles/_mixins.scss";
.color-chooser { .color-chooser {
display: flex; display: flex;
@ -75,7 +82,7 @@ export default {
display: flex; display: flex;
justify-content: center; justify-content: center;
@supports (display: grid) { @supports (display: grid) {
display: grid; display: grid
} }
justify-items: center; justify-items: center;
align-items: center; align-items: center;

View File

@ -3,18 +3,36 @@
:class="{'hideable-element--greyed-out': hidden}" :class="{'hideable-element--greyed-out': hidden}"
class="content-block__container hideable-element content-list__parent" class="content-block__container hideable-element content-list__parent"
> >
<div :class="specialClass" :style="instrumentStyle" class="content-block" data-cy="content-block"> <div
<div class="block-actions" v-if="canEditModule && !isInstrumentBlock"> :class="specialClass"
<user-widget v-bind="me" class="block-actions__user-widget content-block__user-widget" v-if="isMine" /> :style="instrumentStyle"
class="content-block"
data-cy="content-block"
>
<div
class="block-actions"
v-if="canEditModule && !isInstrumentBlock"
>
<user-widget
v-bind="me"
class="block-actions__user-widget content-block__user-widget"
v-if="isMine"
/>
<more-options-widget> <more-options-widget>
<li class="popover-links__link" v-if="!isInstrumentBlock"> <li
class="popover-links__link"
v-if="!isInstrumentBlock"
>
<popover-link <popover-link
data-cy="duplicate-content-block-link" data-cy="duplicate-content-block-link"
text="Duplizieren" text="Duplizieren"
@link-action="duplicateContentBlock(contentBlock)" @link-action="duplicateContentBlock(contentBlock)"
/> />
</li> </li>
<li class="popover-links__link" v-if="isMine"> <li
class="popover-links__link"
v-if="isMine"
>
<popover-link <popover-link
data-cy="delete-content-block-link" data-cy="delete-content-block-link"
text="Löschen" text="Löschen"
@ -22,13 +40,22 @@
/> />
</li> </li>
<li class="popover-links__link" v-if="isMine"> <li
<popover-link text="Bearbeiten" @link-action="editContentBlock(contentBlock)" /> class="popover-links__link"
v-if="isMine"
>
<popover-link
text="Bearbeiten"
@link-action="editContentBlock(contentBlock)"
/>
</li> </li>
</more-options-widget> </more-options-widget>
</div> </div>
<div class="content-block__visibility"> <div class="content-block__visibility">
<visibility-action :block="contentBlock" v-if="canEditModule" /> <visibility-action
:block="contentBlock"
v-if="canEditModule"
/>
</div> </div>
<h3 <h3
@ -39,7 +66,10 @@
> >
{{ instrumentLabel }} {{ instrumentLabel }}
</h3> </h3>
<h4 class="content-block__title" v-if="!contentBlock.indent"> <h4
class="content-block__title"
v-if="!contentBlock.indent"
>
{{ contentBlock.title }} {{ contentBlock.title }}
</h4> </h4>
@ -55,7 +85,10 @@
/> />
</div> </div>
<add-content-button :where="{ after: contentBlock }" v-if="canEditModule" /> <add-content-button
:where="{after: contentBlock}"
v-if="canEditModule"
/>
</div> </div>
</template> </template>
@ -76,10 +109,11 @@ import { CONTENT_TYPE } from '@/consts/types';
import PopoverLink from '@/components/ui/PopoverLink'; import PopoverLink from '@/components/ui/PopoverLink';
import {insertAtIndex, removeAtIndex} from '@/graphql/immutable-operations'; import {insertAtIndex, removeAtIndex} from '@/graphql/immutable-operations';
import {EDIT_CONTENT_BLOCK_PAGE} from '@/router/module.names'; import {EDIT_CONTENT_BLOCK_PAGE} from '@/router/module.names';
import {defineAsyncComponent} from 'vue';
import {instrumentCategory} from '@/helpers/instrumentType'; import {instrumentCategory} from '@/helpers/instrumentType';
const ContentComponent = () => const ContentComponent = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentComponent'));
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentComponent');
export default { export default {
name: 'ContentBlock', name: 'ContentBlock',
@ -123,15 +157,14 @@ export default {
instrumentStyle() { instrumentStyle() {
if (this.isInstrumentBlock) { if (this.isInstrumentBlock) {
return { return {
backgroundColor: this.contentBlock.instrumentCategory.background, backgroundColor: this.contentBlock.instrumentCategory.background
}; };
} }
return {}; return {};
}, },
instrumentLabel() { instrumentLabel() {
const contentType = this.contentBlock.type.toLowerCase(); const contentType = this.contentBlock.type.toLowerCase();
if (contentType.startsWith('base')) { if (contentType.startsWith('base')) { // all legacy instruments start with `base`
// all legacy instruments start with `base`
return instrumentCategory(contentType); return instrumentCategory(contentType);
} }
if (this.isInstrumentBlock) { if (this.isInstrumentBlock) {
@ -143,7 +176,7 @@ export default {
instrumentLabelStyle() { instrumentLabelStyle() {
if (this.isInstrumentBlock) { if (this.isInstrumentBlock) {
return { return {
color: this.contentBlock.instrumentCategory.foreground, color: this.contentBlock.instrumentCategory.foreground
}; };
} }
return {}; return {};
@ -174,8 +207,7 @@ export default {
// collect content_list_items // collect content_list_items
if (content.type === 'content_list_item') { if (content.type === 'content_list_item') {
contentList = [...contentList, content]; contentList = [...contentList, content];
if (index === this.contentBlock.contents.length - 1) { if (index === this.contentBlock.contents.length - 1) { // content is last element of contents array
// content is last element of contents array
let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)]; let updatedContent = [...newContents, ...this.createContentListOrBlocks(contentList)];
return updatedContent; return updatedContent;
} }
@ -217,21 +249,14 @@ export default {
id, id,
}, },
}, },
update( update(store, {data: {duplicateContentBlock: {contentBlock}}}) {
store,
{
data: {
duplicateContentBlock: { contentBlock },
},
}
) {
if (contentBlock) { if (contentBlock) {
const query = CHAPTER_QUERY; const query = CHAPTER_QUERY;
const variables = { const variables = {
id: parent.id, id: parent.id,
}; };
const {chapter} = store.readQuery({query, variables}); const {chapter} = store.readQuery({query, variables});
const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id); const index = chapter.contentBlocks.findIndex(contentBlock => contentBlock.id === id);
const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock); const contentBlocks = insertAtIndex(chapter.contentBlocks, index, contentBlock);
const data = { const data = {
chapter: { chapter: {
@ -243,6 +268,7 @@ export default {
} }
}, },
}); });
}, },
editContentBlock(contentBlock) { editContentBlock(contentBlock) {
const route = { const route = {
@ -254,9 +280,7 @@ export default {
this.$router.push(route); this.$router.push(route);
}, },
deleteContentBlock(contentBlock) { deleteContentBlock(contentBlock) {
this.$modal this.$modal.open('confirm').then(() => {
.open('confirm')
.then(() => {
this.doDeleteContentBlock(contentBlock); this.doDeleteContentBlock(contentBlock);
}) })
.catch(); .catch();
@ -271,21 +295,14 @@ export default {
id, id,
}, },
}, },
update( update(store, {data: {deleteContentBlock: {success}}}) {
store,
{
data: {
deleteContentBlock: { success },
},
}
) {
if (success) { if (success) {
const query = CHAPTER_QUERY; const query = CHAPTER_QUERY;
const variables = { const variables = {
id: parent.id, id: parent.id,
}; };
const {chapter} = store.readQuery({query, variables}); const {chapter} = store.readQuery({query, variables});
const index = chapter.contentBlocks.findIndex((contentBlock) => contentBlock.id === id); const index = chapter.contentBlocks.findIndex(contentBlock => contentBlock.id === id);
const contentBlocks = removeAtIndex(chapter.contentBlocks, index); const contentBlocks = removeAtIndex(chapter.contentBlocks, index);
const data = { const data = {
chapter: { chapter: {
@ -299,20 +316,18 @@ export default {
}); });
}, },
createContentListOrBlocks(contentList) { createContentListOrBlocks(contentList) {
return [ return [{
{
type: 'content_list', type: 'content_list',
contents: contentList, contents: contentList,
id: contentList[0].id, id: contentList[0].id,
}, }];
];
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.content-block { .content-block {
margin-bottom: $section-spacing; margin-bottom: $section-spacing;
@ -383,7 +398,7 @@ export default {
@include content-box-base; @include content-box-base;
} }
:deep(p) { /deep/ p {
line-height: 1.5; line-height: 1.5;
margin-bottom: 1em; margin-bottom: 1em;
@ -392,7 +407,7 @@ export default {
} }
} }
:deep(.text-block) { /deep/ .text-block {
ul { ul {
@include list-parent; @include list-parent;
} }
@ -402,5 +417,6 @@ export default {
line-height: 1.5; line-height: 1.5;
} }
} }
} }
</style> </style>

View File

@ -1,21 +1,23 @@
<template> <template>
<modal :fullscreen="true"> <modal :fullscreen="true">
<component :value="value" :is="type" /> <component
:value="value"
:is="type"
/>
</modal> </modal>
</template> </template>
<script> <script>
import Modal from '@/components/Modal'; import Modal from '@/components/Modal';
const InfogramBlock = () => import {defineAsyncComponent} from 'vue';
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/InfogramBlock'); const InfogramBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/InfogramBlock'));
const GeniallyBlock = () => const GeniallyBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/GeniallyBlock'));
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/GeniallyBlock');
export default { export default {
components: { components: {
Modal, Modal,
InfogramBlock, InfogramBlock,
GeniallyBlock, GeniallyBlock
}, },
computed: { computed: {
@ -27,9 +29,9 @@ export default {
}, },
value() { value() {
return { return {
id: this.id, id: this.id
}; };
}, }
}, }
}; };
</script> </script>

View File

@ -1,4 +1,10 @@
<a class="header-bar__sidebar-link" data-cy="open-sidebar-link" @click.stop="openSidebar('navigation')"> <template>
<header class="header-bar">
<a
class="header-bar__sidebar-link"
data-cy="open-sidebar-link"
@click.stop="openSidebar('navigation')"
>
<hamburger class="header-bar__sidebar-icon" /> <hamburger class="header-bar__sidebar-icon" />
</a> </a>
<content-navigation class="header-bar__content-navigation" /> <content-navigation class="header-bar__content-navigation" />
@ -28,8 +34,9 @@ import CurrentClass from '@/components/school-class/CurrentClass';
import openSidebar from '@/mixins/open-sidebar'; import openSidebar from '@/mixins/open-sidebar';
import me from '@/mixins/me'; import me from '@/mixins/me';
import {defineAsyncComponent} from 'vue';
const Hamburger = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Hamburger'); const Hamburger = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Hamburger'));
export default { export default {
mixins: [openSidebar, me], mixins: [openSidebar, me],
@ -44,7 +51,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.header-bar { .header-bar {
display: flex; display: flex;

View File

@ -10,20 +10,21 @@
</template> </template>
<script> <script>
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon'); import {defineAsyncComponent} from 'vue';
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
export default { export default {
props: ['text'], props: ['text'],
components: { components: {
InfoIcon, InfoIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
@import '@/styles/_mixins.scss'; @import "@/styles/_mixins.scss";
.helpful-tooltip { .helpful-tooltip {
position: relative; position: relative;
@ -65,6 +66,7 @@ export default {
height: 10px; height: 10px;
transform: rotate(-45deg) translateY(-50%); transform: rotate(-45deg) translateY(-50%);
} }
} }
&:hover &__tooltip { &:hover &__tooltip {

View File

@ -1,38 +1,45 @@
<template> <template>
<button :disabled="loading || disabled" class="loading-button button button--primary button--big"> <button
:disabled="loading || disabled"
class="loading-button button button--primary button--big"
>
<template v-if="!loading"> <template v-if="!loading">
{{ label }} {{ label }}
</template> </template>
<loading-icon class="loading-button__icon" v-else /> <loading-icon
class="loading-button__icon"
v-else
/>
</button> </button>
</template> </template>
<script> <script>
const LoadingIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LoadingIcon'); import {defineAsyncComponent} from 'vue';
const LoadingIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LoadingIcon'));
export default { export default {
props: { props: {
loading: { loading: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
label: { label: {
type: String, type: String,
default: '', default: ''
}, }
}, },
components: { components: {
LoadingIcon, LoadingIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.loading-button { .loading-button {
height: 52px; height: 52px;

View File

@ -4,11 +4,17 @@
<hamburger class="mobile-header__hamburger" /> <hamburger class="mobile-header__hamburger" />
</a> </a>
<router-link to="/" data-cy="mobile-home-link"> <router-link
to="/"
data-cy="mobile-home-link"
>
<logo /> <logo />
</router-link> </router-link>
<user-widget v-bind="me" @click.stop="openSidebar('profile')" /> <user-widget
v-bind="me"
@click.stop="openSidebar('profile')"
/>
</div> </div>
</template> </template>
@ -17,9 +23,10 @@ import UserWidget from '@/components/UserWidget';
import me from '@/mixins/me'; import me from '@/mixins/me';
import openSidebar from '@/mixins/open-sidebar'; import openSidebar from '@/mixins/open-sidebar';
import {defineAsyncComponent} from 'vue';
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo'); const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
const Hamburger = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Hamburger'); const Hamburger = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Hamburger'));
export default { export default {
mixins: [me, openSidebar], mixins: [me, openSidebar],
@ -39,7 +46,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.mobile-header { .mobile-header {
justify-content: space-between; justify-content: space-between;

View File

@ -1,11 +1,7 @@
<template> <template>
<div class="modal__backdrop"> <div class="modal__backdrop">
<div <div
:class="{ :class="{'modal--hide-header': hideHeader || fullscreen, 'modal--fullscreen': fullscreen, 'modal--small': small}"
'modal--hide-header': hideHeader || fullscreen,
'modal--fullscreen': fullscreen,
'modal--small': small,
}"
class="modal" class="modal"
> >
<div class="modal__header"> <div class="modal__header">
@ -13,14 +9,20 @@
</div> </div>
<div class="modal__body"> <div class="modal__body">
<slot /> <slot />
<div class="modal__close-button" @click="hideModal"> <div
class="modal__close-button"
@click="hideModal"
>
<cross class="modal__close-icon" /> <cross class="modal__close-icon" />
</div> </div>
</div> </div>
<div class="modal__footer"> <div class="modal__footer">
<slot name="footer"> <slot name="footer">
<!--<a class="button button&#45;&#45;active">Speichern</a>--> <!--<a class="button button&#45;&#45;active">Speichern</a>-->
<a class="button" @click="hideModal">Abbrechen</a> <a
class="button"
@click="hideModal"
>Abbrechen</a>
</slot> </slot>
</div> </div>
</div> </div>
@ -28,38 +30,39 @@
</template> </template>
<script> <script>
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon'); import {defineAsyncComponent} from 'vue';
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
export default { export default {
props: { props: {
hideHeader: { hideHeader: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
fullscreen: { fullscreen: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
small: { small: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
components: { components: {
Cross, Cross
}, },
methods: { methods: {
hideModal() { hideModal() {
this.$store.dispatch('hideModal'); this.$store.dispatch('hideModal');
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
.modal { .modal {
align-self: center; align-self: center;
@ -75,7 +78,7 @@ export default {
display: grid; display: grid;
} }
grid-template-rows: auto 1fr 65px; grid-template-rows: auto 1fr 65px;
grid-template-areas: 'header' 'body' 'footer'; grid-template-areas: "header" "body" "footer";
-ms-grid-rows: auto 1fr 65px; -ms-grid-rows: auto 1fr 65px;
position: relative; position: relative;
@ -133,7 +136,7 @@ export default {
&--hide-header { &--hide-header {
grid-template-rows: 1fr 65px; grid-template-rows: 1fr 65px;
grid-template-areas: 'body' 'footer'; grid-template-areas: "body" "footer";
#{$parent}__header { #{$parent}__header {
display: none; display: none;
@ -142,6 +145,7 @@ export default {
#{$parent}__body { #{$parent}__body {
padding: $default-padding; padding: $default-padding;
} }
} }
&--fullscreen { &--fullscreen {
@ -149,7 +153,7 @@ export default {
height: auto; height: auto;
grid-template-rows: 1fr; grid-template-rows: 1fr;
-ms-grid-rows: 1fr; -ms-grid-rows: 1fr;
grid-template-areas: 'body'; grid-template-areas: "body";
overflow: hidden; overflow: hidden;
#{$parent}__footer { #{$parent}__footer {

View File

@ -1,9 +1,17 @@
<template> <template>
<div class="more-options"> <div class="more-options">
<a class="more-options__more-link" data-cy="more-options-link" @click.stop="showMenu = !showMenu"> <a
class="more-options__more-link"
data-cy="more-options-link"
@click.stop="showMenu = !showMenu"
>
<ellipses class="more-options__ellipses" /> <ellipses class="more-options__ellipses" />
</a> </a>
<widget-popover class="more-options__popover" v-if="showMenu" @hide-me="showMenu = false"> <widget-popover
class="more-options__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<slot /> <slot />
</widget-popover> </widget-popover>
</div> </div>
@ -11,25 +19,26 @@
<script> <script>
import WidgetPopover from '@/components/ui/WidgetPopover'; import WidgetPopover from '@/components/ui/WidgetPopover';
import {defineAsyncComponent} from 'vue';
const Ellipses = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Ellipses.vue'); const Ellipses = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses.vue'));
export default { export default {
components: { components: {
WidgetPopover, WidgetPopover,
Ellipses, Ellipses
}, },
data() { data() {
return { return {
showMenu: false, showMenu: false
}; };
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.more-options { .more-options {
display: flex; display: flex;

View File

@ -11,7 +11,8 @@
</template> </template>
<script> <script>
const ArrowUp = () => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowUp'); import {defineAsyncComponent} from 'vue';
const ArrowUp = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowUp'));
export default { export default {
components: { components: {

View File

@ -1,14 +1,18 @@
<template> <template>
<div class="submission-document"> <div class="submission-document">
<p class="submission-document__content content" v-if="document && document.length > 0"> <p
class="submission-document__content content"
v-if="document && document.length > 0"
>
<document-icon class="content__icon" /><span class="content__text">{{ filename }}</span> <document-icon class="content__icon" /><span class="content__text">{{ filename }}</span>
</p> </p>
</div> </div>
</template> </template>
<script> <script>
import {defineAsyncComponent} from 'vue';
import filenameFromUrl from '@/helpers/urls'; import filenameFromUrl from '@/helpers/urls';
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon'); const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
export default { export default {
name: 'StudentSubmissionDocument', name: 'StudentSubmissionDocument',
@ -18,7 +22,7 @@ export default {
computed: { computed: {
filename() { filename() {
return filenameFromUrl(this.document); return filenameFromUrl(this.document);
}, }
}, },
}; };
</script> </script>

View File

@ -1,5 +1,8 @@
<template> <template>
<nav :class="{ 'content-navigation--sidebar': isSidebar }" class="content-navigation"> <nav
:class="{'content-navigation--sidebar': isSidebar}"
class="content-navigation"
>
<div class="content-navigation__primary"> <div class="content-navigation__primary">
<div class="content-navigation__item"> <div class="content-navigation__item">
<router-link <router-link
@ -12,7 +15,9 @@
{{ $flavor.textTopics }} {{ $flavor.textTopics }}
</router-link> </router-link>
<topic-navigation v-if="isSidebar" /> <topic-navigation
v-if="isSidebar"
/>
</div> </div>
<div class="content-navigation__item"> <div class="content-navigation__item">
@ -40,7 +45,12 @@
</div> </div>
</div> </div>
<router-link to="/" class="content-navigation__logo" data-cy="home-link" v-if="!isSidebar"> <router-link
to="/"
class="content-navigation__logo"
data-cy="home-link"
v-if="!isSidebar"
>
<logo class="content-navigation__logo-icon" /> <logo class="content-navigation__logo-icon" />
</router-link> </router-link>
@ -57,7 +67,10 @@
</router-link> </router-link>
</div> </div>
<div class="content-navigation__item content-navigation__item--secondary" v-if="showPortfolio"> <div
class="content-navigation__item content-navigation__item--secondary"
v-if="showPortfolio"
>
<router-link <router-link
to="/portfolio" to="/portfolio"
active-class="content-navigation__link--active" active-class="content-navigation__link--active"
@ -67,7 +80,10 @@
Portfolio Portfolio
</router-link> </router-link>
</div> </div>
<div class="content-navigation__item content-navigation__item--secondary" v-if="isSidebar"> <div
class="content-navigation__item content-navigation__item--secondary"
v-if="isSidebar"
>
<a <a
:href="$flavor.supportLink" :href="$flavor.supportLink"
target="_blank" target="_blank"
@ -85,27 +101,28 @@ import TopicNavigation from '@/components/book-navigation/TopicNavigation';
import sidebarMixin from '@/mixins/sidebar'; import sidebarMixin from '@/mixins/sidebar';
import meMixin from '@/mixins/me'; import meMixin from '@/mixins/me';
import {defineAsyncComponent} from 'vue';
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo'); const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
export default { export default {
props: { props: {
isSidebar: { isSidebar: {
default: false, default: false
}, }
}, },
mixins: [sidebarMixin, meMixin], mixins: [sidebarMixin, meMixin],
components: { components: {
TopicNavigation, TopicNavigation,
Logo, Logo
}, },
computed: { computed: {
showPortfolio() { showPortfolio() {
return this.$flavor.showPortfolio; return this.$flavor.showPortfolio;
}, }
}, },
methods: { methods: {
@ -117,14 +134,14 @@ export default {
}, },
close() { close() {
this.closeSidebar('navigation'); this.closeSidebar('navigation');
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
@import '@/styles/_mixins.scss'; @import "@/styles/_mixins.scss";
.content-navigation { .content-navigation {
display: flex; display: flex;
@ -135,8 +152,7 @@ export default {
@include navigation-link; @include navigation-link;
} }
&__primary, &__primary, &__secondary {
&__secondary {
display: none; display: none;
flex-direction: row; flex-direction: row;
@ -146,7 +162,7 @@ export default {
} }
&__logo { &__logo {
color: #17a887; color: #17A887;
font-size: 36px; font-size: 36px;
font-weight: 800; font-weight: 800;
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
@ -181,8 +197,7 @@ export default {
&--sidebar { &--sidebar {
flex-direction: column; flex-direction: column;
#{$parent}__primary, #{$parent}__primary, #{$parent}__secondary {
#{$parent}__secondary {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
@ -198,6 +213,7 @@ export default {
&:only-child { &:only-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
#{$parent}__item { #{$parent}__item {

View File

@ -23,8 +23,9 @@
import ContentNavigation from '@/components/book-navigation/ContentNavigation'; import ContentNavigation from '@/components/book-navigation/ContentNavigation';
import sidebarMixin from '@/mixins/sidebar'; import sidebarMixin from '@/mixins/sidebar';
import {defineAsyncComponent} from 'vue';
const Cross = () => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'); const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
export default { export default {
mixins: [sidebarMixin], mixins: [sidebarMixin],

View File

@ -1,44 +1,55 @@
<template> <template>
<div :class="{ 'sub-navigation-item--active': show }" class="sub-navigation-item" v-click-outside="close"> <div
<div class="sub-navigation-item__title" @click="show = !show"> :class="{ 'sub-navigation-item--active': show}"
class="sub-navigation-item"
v-click-outside="close"
>
<div
class="sub-navigation-item__title"
@click="show = !show"
>
{{ title }} {{ title }}
<chevron-down class="sub-navigation-item__icon sub-navigation-item__chevron-down" /> <chevron-down class="sub-navigation-item__icon sub-navigation-item__chevron-down" />
<chevron-up class="sub-navigation-item__icon sub-navigation-item__chevron-up" /> <chevron-up class="sub-navigation-item__icon sub-navigation-item__chevron-up" />
</div> </div>
<div class="sub-navigation-item__nav-items book-subnavigation" v-if="show"> <div
class="sub-navigation-item__nav-items book-subnavigation"
v-if="show"
>
<slot /> <slot />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
const ChevronDown = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronDown'); import {defineAsyncComponent} from 'vue';
const ChevronUp = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronUp'); const ChevronDown = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronDown'));
const ChevronUp = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronUp'));
export default { export default {
props: ['title'], props: ['title'],
components: { components: {
ChevronDown, ChevronDown,
ChevronUp, ChevronUp
}, },
data() { data() {
return { return {
show: false, show: false
}; };
}, },
watch: { watch: {
$route() { $route() {
this.show = false; this.show = false;
}, }
}, },
methods: { methods: {
close() { close() {
this.show = false; this.show = false;
}, }
}, }
}; };
</script> </script>

View File

@ -26,10 +26,14 @@
:class="['content-element__component']" :class="['content-element__component']"
v-bind="element" v-bind="element"
:is="component" :is="component"
@change-text="changeText" @change-text="changeText"
@link-change-url="changeUrl" @link-change-url="changeUrl"
@change-url="changeUrl" @change-url="changeUrl"
@switch-to-document="switchToDocument" @switch-to-document="switchToDocument"
@assignment-change-title="changeAssignmentTitle" @assignment-change-title="changeAssignmentTitle"
@assignment-change-assignment="changeAssignmentAssignment" @assignment-change-assignment="changeAssignmentAssignment"
/> />
@ -41,32 +45,27 @@
<script> <script>
import ContentFormSection from '@/components/content-block-form/ContentFormSection'; import ContentFormSection from '@/components/content-block-form/ContentFormSection';
import ContentElementActions from '@/components/content-block-form/ContentElementActions'; import ContentElementActions from '@/components/content-block-form/ContentElementActions';
import {defineAsyncComponent} from 'vue';
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon'); const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
const ContentBlockElementChooserWidget = () => const ContentBlockElementChooserWidget = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ContentBlockElementChooserWidget'));
import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ContentBlockElementChooserWidget'); const LinkForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/LinkForm'));
const LinkForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/LinkForm'); const VideoForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/VideoForm'));
const VideoForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/VideoForm'); const ImageForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ImageForm'));
const ImageForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/ImageForm'); const DocumentForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/DocumentForm'));
const DocumentForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/DocumentForm'); const AssignmentForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/AssignmentForm'));
const AssignmentForm = () => const TextForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/TipTap.vue'));
import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/AssignmentForm'); const SubtitleForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/SubtitleForm'));
const TextForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/TipTap.vue');
const SubtitleForm = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-forms/SubtitleForm');
// readonly blocks // readonly blocks
const Assignment = () => const Assignment = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/assignment/Assignment'));
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/assignment/Assignment'); const SurveyBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/SurveyBlock'));
const SurveyBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/SurveyBlock'); const Solution = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/Solution'));
const Solution = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Solution'); const ImageBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ImageBlock'));
const ImageBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ImageBlock'); const Instruction = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/Instruction'));
const Instruction = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/Instruction'); const ModuleRoomSlug = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ModuleRoomSlug'));
const ModuleRoomSlug = () => const CmsDocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/CmsDocumentBlock'));
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ModuleRoomSlug'); const ThinglinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/ThinglinkBlock'));
const CmsDocumentBlock = () => const InfogramBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-blocks/InfogramBlock'));
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/CmsDocumentBlock');
const ThinglinkBlock = () =>
import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/ThinglinkBlock');
const InfogramBlock = () => import(/* webpackChunkName: "content-forms" */ '@/components/content-blocks/InfogramBlock');
const CHOOSER = 'content-block-element-chooser-widget'; const CHOOSER = 'content-block-element-chooser-widget';
@ -111,7 +110,7 @@ export default {
CmsDocumentBlock, CmsDocumentBlock,
InfogramBlock, InfogramBlock,
ThinglinkBlock, ThinglinkBlock,
Assignment, Assignment
}, },
computed: { computed: {
@ -217,12 +216,12 @@ export default {
case 'thinglink_block': case 'thinglink_block':
return { return {
component: 'thinglink-block', component: 'thinglink-block',
title: 'Interaktive Grafik', title: 'Interaktive Grafik'
}; };
case 'infogram_block': case 'infogram_block':
return { return {
component: 'infogram-block', component: 'infogram-block',
title: 'Interaktive Grafik', title: 'Interaktive Grafik'
}; };
} }
return { return {
@ -296,12 +295,9 @@ export default {
case 'document_block': case 'document_block':
el = { el = {
...el, ...el,
value: Object.assign( value: Object.assign({
{
url: '', url: '',
}, }, value),
value
),
}; };
break; break;
case 'image_url_block': case 'image_url_block':

View File

@ -18,11 +18,27 @@
/> />
</template> </template>
<add-content-element :index="-1" class="contents-form__add" @add-element="addElement" /> <add-content-element
<div class="contents-form__element" v-for="(element, index) in localContentBlock.contents" :key="index"> :index="-1"
<content-element :element="element" @update="update(index, $event)" @remove="remove(index)" /> class="contents-form__add"
@add-element="addElement"
/>
<div
class="contents-form__element"
v-for="(element, index) in localContentBlock.contents"
:key="index"
>
<content-element
:element="element"
@update="update(index, $event)"
@remove="remove(index)"
/>
<add-content-element :index="index" class="contents-form__add" @add-element="addElement" /> <add-content-element
:index="index"
class="contents-form__add"
@add-element="addElement"
/>
</div> </div>
<template #footer> <template #footer>
@ -32,24 +48,26 @@
class="button button--primary" class="button button--primary"
data-cy="modal-save-button" data-cy="modal-save-button"
@click="save" @click="save"
>Speichern</a >Speichern</a>
> <a
<a class="button" @click="$emit('hide')">Abbrechen</a> class="button"
@click="$emit('hide')"
>Abbrechen</a>
</div> </div>
</template> </template>
</modal> </modal>
</template> </template>
<script> <script>
import {defineAsyncComponent} from 'vue';
import {meQuery} from '@/graphql/queries'; import {meQuery} from '@/graphql/queries';
const ModalInput = () => import(/* webpackChunkName: "content-forms" */ '@/components/ModalInput'); const ModalInput = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/ModalInput'));
const AddContentElement = () => import(/* webpackChunkName: "content-forms" */ '@/components/AddContentElement'); const AddContentElement = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/AddContentElement'));
const ContentElement = () => const ContentElement = defineAsyncComponent(() => import(/* webpackChunkName: "content-forms" */'@/components/content-block-form/ContentElement'));
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/ContentElement');
const Modal = () => import('@/components/Modal.vue'); const Modal = defineAsyncComponent(() => import('@/components/Modal'));
const Checkbox = () => import('@/components/ui/Checkbox.vue'); const Checkbox = defineAsyncComponent(() => import('@/components/ui/Checkbox'));
export default { export default {
props: { props: {
@ -79,15 +97,12 @@ export default {
data() { data() {
return { return {
error: false, error: false,
localContentBlock: Object.assign( localContentBlock: Object.assign({}, {
{},
{
title: this.contentBlock.title, title: this.contentBlock.title,
contents: [...this.contentBlock.contents], contents: [...this.contentBlock.contents],
id: this.contentBlock.id || undefined, id: this.contentBlock.id || undefined,
isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task', isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task',
} }),
),
me: {}, me: {},
}; };
}, },
@ -133,17 +148,19 @@ export default {
remove(index) { remove(index) {
this.localContentBlock.contents.splice(index, 1); this.localContentBlock.contents.splice(index, 1);
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.contents-form { .contents-form {
/* top level does not exist, because of the modal */ /* top level does not exist, because of the modal */
&__element { &__element {
} }
&__element-component { &__element-component {

View File

@ -1,40 +1,43 @@
<template> <template>
<content-list :items="contentBlocks"> <content-list
:items="contentBlocks"
>
<template #default="{ item }"> <template #default="{ item }">
<content-block :content-block="item" :parent="parent" /> <content-block
:content-block="item"
:parent="parent"
/>
</template> </template>
</content-list> </content-list>
</template> </template>
<script> <script>
import ContentList from '@/components/content-blocks/ContentList.vue'; import {defineAsyncComponent} from 'vue';
const ContentList = defineAsyncComponent(() => import('@/components/content-blocks/ContentList'));
const ContentBlock = defineAsyncComponent(() => import('@/components/ContentBlock'));
export default { export default {
name: 'ContentBlockList', name: 'ContentBlockList',
props: ['contents', 'parent'], props: ['contents', 'parent'],
components: { components: {
ContentList, ContentList,
// https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components ContentBlock
ContentBlock: () => import('@/components/ContentBlock.vue'),
}, },
computed: { computed: {
contentBlocks() { contentBlocks() {
return this.contents.map((contentBlock) => { return this.contents.map(contentBlock => {
const contents = contentBlock.value ? [...contentBlock.value] : []; const contents = contentBlock.value ? [...contentBlock.value] : [];
return Object.assign({}, contentBlock, { return Object.assign({}, contentBlock, {
contents, contents,
indent: true, indent: true,
bookmarks: this.parent.bookmarks, bookmarks: this.parent.bookmarks,
notes: this.parent.notes, notes: this.parent.notes,
root: this.parent.id, root: this.parent.id
}); });
}); });
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss">
@import '~styles/helpers';
</style>

View File

@ -1,16 +1,25 @@
<template> <template>
<div class="document-block"> <div class="document-block">
<document-icon class="document-block__icon" /> <document-icon class="document-block__icon" />
<a :href="value.url" class="document-block__link" target="_blank">{{ urlName }}</a> <a
<a class="document-block__remove" v-if="showTrashIcon" @click="$emit('trash')"> :href="value.url"
class="document-block__link"
target="_blank"
>{{ urlName }}</a>
<a
class="document-block__remove"
v-if="showTrashIcon"
@click="$emit('trash')"
>
<trash-icon class="document-block__trash-icon" /> <trash-icon class="document-block__trash-icon" />
</a> </a>
</div> </div>
</template> </template>
<script> <script>
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon'); import {defineAsyncComponent} from 'vue';
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon'); const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
export default { export default {
props: { props: {
@ -30,13 +39,13 @@ export default {
return parts[parts.length - 1]; return parts[parts.length - 1];
} }
return null; return null;
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.document-block { .document-block {
display: grid; display: grid;

View File

@ -1,13 +1,20 @@
<template> <template>
<div class="instruction" v-if="me.isTeacher"> <div
class="instruction"
v-if="me.isTeacher"
>
<bulb-icon class="instruction__icon" /> <bulb-icon class="instruction__icon" />
<a :href="url" class="instruction__link">{{ text }}</a> <a
:href="url"
class="instruction__link"
>{{ text }}</a>
</div> </div>
</template> </template>
<script> <script>
import me from '@/mixins/me'; import me from '@/mixins/me';
const BulbIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon'); import {defineAsyncComponent} from 'vue';
const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/BulbIcon'));
export default { export default {
props: ['value'], props: ['value'],
@ -15,7 +22,7 @@ export default {
mixins: [me], mixins: [me],
components: { components: {
BulbIcon, BulbIcon
}, },
computed: { computed: {
@ -24,13 +31,13 @@ export default {
}, },
url() { url() {
return this.value.document ? this.value.document.url : this.value.url; return this.value.document ? this.value.document.url : this.value.url;
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_mixins.scss'; @import "@/styles/_mixins.scss";
.instruction { .instruction {
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@ -1,31 +1,39 @@
<template> <template>
<div :class="{ 'link-block--no-margin': noMargin }" class="link-block"> <div
:class="{ 'link-block--no-margin': noMargin}"
class="link-block"
>
<link-icon class="link-block__icon" /> <link-icon class="link-block__icon" />
<a :href="href" class="link-block__link" target="_blank">{{ value.text }}</a> <a
:href="href"
class="link-block__link"
target="_blank"
>{{ value.text }}</a>
</div> </div>
</template> </template>
<script> <script>
const LinkIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LinkIcon'); import {defineAsyncComponent} from 'vue';
const LinkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LinkIcon'));
export default { export default {
props: { props: {
value: Object, value: Object,
noMargin: { noMargin: {
default: false, default: false
}, }
}, },
components: { components: {
LinkIcon, LinkIcon
}, },
computed: { computed: {
href() { href() {
const url = this.value.url; const url = this.value.url;
return url.startsWith('http') ? this.value.url : `http://${this.value.url}`; return url.startsWith('http') ? this.value.url : `http://${this.value.url}`;
}, }
}, }
}; };
</script> </script>

View File

@ -1,9 +1,19 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div :data-scrollto="value.id" class="assignment"> <div
<p class="assignment__main-text" data-cy="assignment-main-text" v-html="assignment.assignment" /> :data-scrollto="value.id"
class="assignment"
>
<p
class="assignment__main-text"
data-cy="assignment-main-text"
v-html="assignment.assignment"
/>
<solution :value="solution" v-if="assignment.solution" /> <solution
:value="solution"
v-if="assignment.solution"
/>
<template v-if="isStudent"> <template v-if="isStudent">
<submission-form <submission-form
@ -23,13 +33,24 @@
@spellcheck="spellcheck" @spellcheck="spellcheck"
/> />
<spell-check :corrections="corrections" :text="submission.text" /> <spell-check
:corrections="corrections"
:text="submission.text"
/>
<p class="assignment__feedback" v-if="assignment.submission.submissionFeedback" v-html="feedbackText" /> <p
class="assignment__feedback"
v-if="assignment.submission.submissionFeedback"
v-html="feedbackText"
/>
</template> </template>
<template v-if="!isStudent"> <template v-if="!isStudent">
<router-link :to="{ name: 'submissions', params: { id: assignment.id } }" class="button button--primary"> <router-link
Zu den Ergebnissen :to="{name: 'submissions', params: { id: assignment.id }}"
class="button button--primary"
>
Zu den
Ergebnissen
</router-link> </router-link>
</template> </template>
</div> </div>
@ -45,12 +66,11 @@ import SPELL_CHECK_MUTATION from '@/graphql/gql/mutations/spellCheck.gql';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import {sanitize} from '@/helpers/text'; import {sanitize} from '@/helpers/text';
import {defineAsyncComponent} from 'vue';
const SubmissionForm = () => const SubmissionForm = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/SubmissionForm'));
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SubmissionForm'); const Solution = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Solution'));
const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution'); const SpellCheck = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/assignment/SpellCheck'));
const SpellCheck = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SpellCheck');
export default { export default {
props: ['value'], props: ['value'],
@ -107,8 +127,7 @@ export default {
...mapActions(['scrollToAssignmentReady']), ...mapActions(['scrollToAssignmentReady']),
_save: debounce(function (submission) { _save: debounce(function (submission) {
this.saving++; this.saving++;
this.$apollo this.$apollo.mutate({
.mutate({
mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS, mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS,
variables: { variables: {
input: { input: {
@ -119,14 +138,7 @@ export default {
}, },
}, },
}, },
update( update(store, {data: {updateAssignment: {successful, updatedAssignment}}}) {
store,
{
data: {
updateAssignment: { successful, updatedAssignment },
},
}
) {
try { try {
if (successful) { if (successful) {
const query = ASSIGNMENT_QUERY; const query = ASSIGNMENT_QUERY;
@ -137,7 +149,7 @@ export default {
submission, submission,
}); });
const data = { const data = {
assignment, assignment
}; };
store.writeQuery({query, variables, data}); store.writeQuery({query, variables, data});
} }
@ -146,8 +158,7 @@ export default {
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing // Query did not exist in the cache, and apollo throws a generic Error. Do nothing
} }
}, },
}) }).then(() => {
.then(() => {
this.saving--; this.saving--;
if (this.saving === 0) { if (this.saving === 0) {
this.unsaved = false; this.unsaved = false;
@ -211,8 +222,7 @@ export default {
spellcheck() { spellcheck() {
let self = this; let self = this;
this.spellcheckLoading = true; this.spellcheckLoading = true;
this.$apollo this.$apollo.mutate({
.mutate({
mutation: SPELL_CHECK_MUTATION, mutation: SPELL_CHECK_MUTATION,
variables: { variables: {
input: { input: {
@ -220,18 +230,10 @@ export default {
text: this.assignment.submission.text, text: this.assignment.submission.text,
}, },
}, },
update( update(store, {data: {spellCheck: {results}}}) {
store,
{
data: {
spellCheck: { results },
},
}
) {
self.corrections = results; self.corrections = results;
}, },
}) }).then(() => {
.then(() => {
this.spellcheckLoading = false; this.spellcheckLoading = false;
}); });
}, },
@ -276,11 +278,11 @@ export default {
} }
&__main-text { &__main-text {
:deep(ul) { /deep/ ul{
@include list-parent; @include list-parent
} }
:deep(li) { /deep/ li {
@include list-child; @include list-child;
} }
} }
@ -311,5 +313,7 @@ export default {
&__feedback { &__feedback {
@include regular-text; @include regular-text;
} }
} }
</style> </style>

View File

@ -1,37 +1,47 @@
<template> <template>
<div class="final-submission" data-cy="final-submission"> <div
<document-block :value="{ url: userInput.document }" class="final-submission__document" v-if="userInput.document" /> class="final-submission"
data-cy="final-submission"
>
<document-block
:value="{url: userInput.document}"
class="final-submission__document"
v-if="userInput.document"
/>
<div class="final-submission__explanation"> <div class="final-submission__explanation">
<info-icon class="final-submission__explanation-icon" /> <info-icon class="final-submission__explanation-icon" />
<span class="final-submission__explanation-text">{{ sharedMsg }}</span> <span class="final-submission__explanation-text">{{ sharedMsg }}</span>
<a class="final-submission__reopen" data-cy="final-submission-reopen" v-if="showReopen" @click="$emit('reopen')" <a
>Bearbeiten</a class="final-submission__reopen"
> data-cy="final-submission-reopen"
v-if="showReopen"
@click="$emit('reopen')"
>Bearbeiten</a>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {newLineToParagraph} from '@/helpers/text'; import {newLineToParagraph} from '@/helpers/text';
const DocumentBlock = () => import {defineAsyncComponent} from 'vue';
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock'); const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon'); const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
export default { export default {
props: { props: {
userInput: { userInput: {
type: Object, type: Object,
default: () => ({}), default: () => ({})
}, },
showReopen: { showReopen: {
type: Boolean, type: Boolean,
default: true, default: true
}, },
sharedMsg: { sharedMsg: {
type: String, type: String,
default: '', default: ''
}, }
}, },
components: { components: {
@ -42,13 +52,13 @@ export default {
computed: { computed: {
text() { text() {
return newLineToParagraph(this.userInput.text); return newLineToParagraph(this.userInput.text);
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.final-submission { .final-submission {
&__text { &__text {

View File

@ -10,7 +10,10 @@
/> />
</div> </div>
<div class="submission-form-container__actions" v-if="!isFinalOrReadOnly"> <div
class="submission-form-container__actions"
v-if="!isFinalOrReadOnly"
>
<button <button
class="submission-form-container__submit button button--primary button--white-bg" class="submission-form-container__submit button button--primary button--white-bg"
data-cy="submission-form-submit" data-cy="submission-form-submit"
@ -26,7 +29,11 @@
> >
{{ spellcheckText }} {{ spellcheckText }}
</button> </button>
<file-upload :document="userInput.document" v-if="allowsDocuments" @change-document-url="changeDocumentUrl" /> <file-upload
:document="userInput.document"
v-if="allowsDocuments"
@change-document-url="changeDocumentUrl"
/>
<slot /> <slot />
</div> </div>
@ -41,9 +48,11 @@
</template> </template>
<script> <script>
const SubmissionInput = () => import('@/components/content-blocks/assignment/SubmissionInput.vue'); import {defineAsyncComponent} from 'vue';
const FinalSubmission = () => import('@/components/content-blocks/assignment/FinalSubmission.vue'); const SubmissionInput = defineAsyncComponent(() => import('@/components/content-blocks/assignment/SubmissionInput'));
const FileUpload = () => import('@/components/ui/file-upload/FileUpload.vue'); const FinalSubmission = defineAsyncComponent(() => import('@/components/content-blocks/assignment/FinalSubmission'));
const FileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/FileUpload'));
export default { export default {
props: { props: {
@ -107,6 +116,7 @@ export default {
this.$emit('changeDocumentUrl', documentUrl); this.$emit('changeDocumentUrl', documentUrl);
}, },
}, },
}; };
</script> </script>
@ -149,4 +159,5 @@ export default {
display: inline-block; display: inline-block;
} }
} }
</style> </style>

View File

@ -11,18 +11,25 @@
v-auto-grow v-auto-grow
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
/> />
<div class="submission-form__save-status submission-form__save-status--saved" v-if="saved"> <div
class="submission-form__save-status submission-form__save-status--saved"
v-if="saved"
>
<tick-circle-icon class="submission-form__save-status-icon" /> <tick-circle-icon class="submission-form__save-status-icon" />
</div> </div>
<div class="submission-form__save-status submission-form__save-status--unsaved" v-if="!saved"> <div
class="submission-form__save-status submission-form__save-status--unsaved"
v-if="!saved"
>
<loading-icon class="submission-form__save-status-icon submission-form__saving-icon" /> <loading-icon class="submission-form__save-status-icon submission-form__saving-icon" />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
const TickCircleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TickCircleIcon'); import {defineAsyncComponent} from 'vue';
const LoadingIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LoadingIcon'); const TickCircleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TickCircleIcon'));
const LoadingIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LoadingIcon'));
export default { export default {
props: { props: {
@ -31,18 +38,18 @@ export default {
readonly: Boolean, readonly: Boolean,
placeholder: { placeholder: {
type: String, type: String,
default: 'Ergebnis erfassen', default: 'Ergebnis erfassen'
}, }
}, },
components: { components: {
TickCircleIcon, TickCircleIcon,
LoadingIcon, LoadingIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.submission-form { .submission-form {
display: flex; display: flex;

View File

@ -5,7 +5,7 @@
class="assignment-form__title skillbox-input" class="assignment-form__title skillbox-input"
placeholder="Aufgabentitel" placeholder="Aufgabentitel"
@input="$emit('assignment-change-title', $event.target.value, index)" @input="$emit('assignment-change-title', $event.target.value, index)"
/> >
<textarea <textarea
:value="value.assignment" :value="value.assignment"
class="assignment-form__exercise-text skillbox-textarea" class="assignment-form__exercise-text skillbox-textarea"
@ -20,18 +20,19 @@
</template> </template>
<script> <script>
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon'); import {defineAsyncComponent} from 'vue';
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
export default { export default {
props: ['value', 'index'], props: ['value', 'index'],
components: { components: {
InfoIcon, InfoIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.assignment-form { .assignment-form {
display: grid; display: grid;

View File

@ -1,12 +1,28 @@
<template> <template>
<div class="document-form" ref="documentform"> <div
<div v-if="!value.url" ref="uploadcare-panel" /> class="document-form"
<div class="document-form__spinner" v-if="loading"> ref="documentform"
>
<div
v-if="!value.url"
ref="uploadcare-panel"
/>
<div
class="document-form__spinner"
v-if="loading"
>
<loading-icon class="document-form__loading-icon" /> <loading-icon class="document-form__loading-icon" />
</div> </div>
<div class="document-form__uploaded" v-if="value.url"> <div
class="document-form__uploaded"
v-if="value.url"
>
<document-icon class="document-form__icon" /> <document-icon class="document-form__icon" />
<a :href="previewUrl" class="document-form__link" target="_blank">{{ previewLink }}</a> <a
:href="previewUrl"
class="document-form__link"
target="_blank"
>{{ previewLink }}</a>
</div> </div>
</div> </div>
</template> </template>
@ -14,8 +30,9 @@
<script> <script>
import {uploadcare} from '@/helpers/uploadcare'; import {uploadcare} from '@/helpers/uploadcare';
import LoadingIcon from '@/components/icons/LoadingIcon'; import LoadingIcon from '@/components/icons/LoadingIcon';
import {defineAsyncComponent} from 'vue';
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon'); const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
export default { export default {
props: ['value', 'index'], props: ['value', 'index'],
@ -48,22 +65,19 @@ export default {
}, },
mounted() { mounted() {
uploadcare( uploadcare(this, url => {
this,
(url) => {
this.$emit('change-url', url, this.index); this.$emit('change-url', url, this.index);
this.loading = false; this.loading = false;
}, }, () => {
() => {
this.loading = true; this.loading = true;
}
); });
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.document-form { .document-form {
&__uploaded { &__uploaded {

View File

@ -1,11 +1,21 @@
<template> <template>
<div> <div>
<div class="video-form" v-if="!isVimeo && !isYoutube && !isSrf"> <div
class="video-form"
v-if="!isVimeo && !isYoutube && !isSrf"
>
<info-icon class="video-form__help-icon help-text__icon" /> <info-icon class="video-form__help-icon help-text__icon" />
<p class="video-form__help-description help-text__description"> <p class="video-form__help-description help-text__description">
Sie können Videos auf Sie können Videos auf <a
<a class="video-form__platform-link help-text__link" href="https://youtube.com/" target="_blank">Youtube</a> class="video-form__platform-link help-text__link"
oder <a class="video-form__platform-link help-text__link" href="https://vimeo.com/" target="_blank">Vimeo</a> href="https://youtube.com/"
target="_blank"
>Youtube</a>
oder <a
class="video-form__platform-link help-text__link"
href="https://vimeo.com/"
target="_blank"
>Vimeo</a>
hochladen und anschliessen einen Link hier einfügen. hochladen und anschliessen einen Link hier einfügen.
</p> </p>
@ -14,7 +24,7 @@
class="video-form__video-link skillbox-input" class="video-form__video-link skillbox-input"
placeholder="Bsp: https://www.youtube.com/watch?v=dQw4w9WgXcQ" placeholder="Bsp: https://www.youtube.com/watch?v=dQw4w9WgXcQ"
@input="$emit('change-url', $event.target.value, index)" @input="$emit('change-url', $event.target.value, index)"
/> >
</div> </div>
<div v-if="isYoutube"> <div v-if="isYoutube">
@ -34,7 +44,8 @@ import YoutubeEmbed from '@/components/videos/YoutubeEmbed';
import VimeoEmbed from '@/components/videos/VimeoEmbed'; import VimeoEmbed from '@/components/videos/VimeoEmbed';
import SrfEmbed from '@/components/videos/SrfEmbed'; import SrfEmbed from '@/components/videos/SrfEmbed';
import {isVimeoUrl, isYoutubeUrl, isSrfUrl} from '@/helpers/video'; import {isVimeoUrl, isYoutubeUrl, isSrfUrl} from '@/helpers/video';
const InfoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon'); import {defineAsyncComponent} from 'vue';
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/InfoIcon'));
export default { export default {
props: ['value', 'index'], props: ['value', 'index'],
@ -43,7 +54,7 @@ export default {
InfoIcon, InfoIcon,
YoutubeEmbed, YoutubeEmbed,
VimeoEmbed, VimeoEmbed,
SrfEmbed, SrfEmbed
}, },
computed: { computed: {
@ -55,14 +66,14 @@ export default {
}, },
isSrf() { isSrf() {
return isSrfUrl(this.value.url); return isSrfUrl(this.value.url);
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
@import '@/styles/_functions.scss'; @import "@/styles/_functions.scss";
.video-form { .video-form {
display: grid; display: grid;
@ -73,9 +84,11 @@ export default {
align-items: center; align-items: center;
&__help-icon { &__help-icon {
} }
&__help-description { &__help-description {
} }
&__platform-link { &__platform-link {
@ -87,7 +100,7 @@ export default {
&__video-link { &__video-link {
grid-column: 1 / span 2; grid-column: 1 / span 2;
width: $modal-input-width; width: $modal-input-width
} }
} }
</style> </style>

View File

@ -1,16 +1,28 @@
<template> <template>
<a :class="typeClass" class="filter-entry" data-cy="filter-entry" :style="categoryStyle"> <a
:class="typeClass"
class="filter-entry"
data-cy="filter-entry"
:style="categoryStyle"
>
<span class="filter-entry__text">{{ text }}</span> <span class="filter-entry__text">{{ text }}</span>
<span :style="activeStyle" class="filter-entry__icon-wrapper"> <span
<chevron-right :style="{ fill: category.foreground }" class="filter-entry__icon" /> :style="activeStyle"
class="filter-entry__icon-wrapper"
>
<chevron-right
:style="{fill: category.foreground}"
class="filter-entry__icon"
/>
</span> </span>
</a> </a>
</template> </template>
<script> <script>
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql'; import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
import {defineAsyncComponent} from 'vue';
const ChevronRight = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronRight'); const ChevronRight = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight'));
export default { export default {
props: { props: {

View File

@ -1,11 +1,12 @@
const Modal = () => import(/* webpackChunkName: "modals" */ '@/components/Modal'); import {defineAsyncComponent} from 'vue';
const FullscreenImage = () => import(/* webpackChunkName: "modals" */ '@/components/FullscreenImage'); const Modal = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/Modal'));
const FullscreenInfographic = () => import(/* webpackChunkName: "modals" */ '@/components/FullscreenInfographic'); const FullscreenImage = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/FullscreenImage'));
const FullscreenVideo = () => import(/* webpackChunkName: "modals" */ '@/components/FullscreenVideo'); const FullscreenInfographic = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/FullscreenInfographic'));
const DeactivatePerson = () => import(/* webpackChunkName: "modals" */ '@/components/profile/DeactivatePerson'); const FullscreenVideo = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/FullscreenVideo'));
const SnapshotCreated = () => import(/* webpackChunkName: "modals" */ '@/components/modules/SnapshotCreated'); const DeactivatePerson = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/profile/DeactivatePerson'));
const ChangeVisibility = () => import(/* webpackChunkName: "modals" */ '@/components/rooms/ChangeVisibility'); const SnapshotCreated = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/modules/SnapshotCreated'));
const Confirm = () => import(/* webpackChunkName: "modals" */ '@/components/modals/Confirm'); const ChangeVisibility = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/rooms/ChangeVisibility'));
const Confirm = defineAsyncComponent(() => import(/* webpackChunkName: "modals" */'@/components/modals/Confirm'));
export default { export default {
Modal, Modal,

View File

@ -1,5 +1,8 @@
<template> <template>
<div class="bookmark-actions" v-if="!editMode"> <div
class="bookmark-actions"
v-if="!editMode"
>
<a <a
:class="{'bookmark-actions__action--bookmarked': bookmarked}" :class="{'bookmark-actions__action--bookmarked': bookmarked}"
class="bookmark-actions__action bookmark-actions__bookmark" class="bookmark-actions__action bookmark-actions__bookmark"
@ -29,35 +32,36 @@
</template> </template>
<script> <script>
const BookmarkIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/BookmarkIcon'); import {defineAsyncComponent} from 'vue';
const AddNoteIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddNoteIcon'); const BookmarkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/BookmarkIcon'));
const NoteIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/NoteIcon'); const AddNoteIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddNoteIcon'));
const NoteIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/NoteIcon'));
export default { export default {
props: { props: {
bookmarked: { bookmarked: {
type: Boolean, type: Boolean,
default: false, default: false
}, },
note: { note: {
type: [Object, Boolean], type: [Object, Boolean],
default: false, default: false
}, },
editMode: { editMode: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
components: { components: {
BookmarkIcon, BookmarkIcon,
AddNoteIcon, AddNoteIcon,
NoteIcon, NoteIcon
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.bookmark-actions { .bookmark-actions {
height: 100%; height: 100%;
@ -84,8 +88,7 @@ export default {
display: flex; display: flex;
justify-content: center; justify-content: center;
&--bookmarked, &--bookmarked, &--noted {
&--noted {
opacity: 1; opacity: 1;
} }
} }
@ -98,4 +101,5 @@ export default {
} }
} }
} }
</style> </style>

View File

@ -6,7 +6,10 @@
placeholder="Lernziel erfassen..." placeholder="Lernziel erfassen..."
@input="$emit('input', $event)" @input="$emit('input', $event)"
/> />
<a class="icon-button" @click="$emit('delete')"> <a
class="icon-button"
@click="$emit('delete')"
>
<trash-icon class="icon-button__icon icon-button__icon--subtle" /> <trash-icon class="icon-button__icon icon-button__icon--subtle" />
</a> </a>
</div> </div>
@ -14,20 +17,21 @@
<script> <script>
import ModalInput from '@/components/ModalInput'; import ModalInput from '@/components/ModalInput';
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon'); import {defineAsyncComponent} from 'vue';
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
export default { export default {
props: ['objective'], props: ['objective'],
components: { components: {
ModalInput, ModalInput,
TrashIcon, TrashIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
.objective-form { .objective-form {
display: grid; display: grid;

View File

@ -1,12 +1,16 @@
<template> <template>
<a class="add-project-entry" @click="addProjectEntry"> <a
class="add-project-entry"
@click="addProjectEntry"
>
<plus-icon class="add-project-entry__icon" /> <plus-icon class="add-project-entry__icon" />
<span class="add-project-entry__text">Beitrag erfassen</span> <span class="add-project-entry__text">Beitrag erfassen</span>
</a> </a>
</template> </template>
<script> <script>
const PlusIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PlusIcon'); import {defineAsyncComponent} from 'vue';
const PlusIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PlusIcon'));
export default { export default {
props: ['project'], props: ['project'],
components: {PlusIcon}, components: {PlusIcon},
@ -14,13 +18,13 @@ export default {
methods: { methods: {
addProjectEntry() { addProjectEntry() {
this.$store.dispatch('addProjectEntry', this.project); this.$store.dispatch('addProjectEntry', this.project);
}, }
}, }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '~styles/helpers'; @import "~styles/helpers";
.add-project-entry { .add-project-entry {
display: flex; display: flex;

View File

@ -1,9 +1,25 @@
<template> <template>
<div class="portfolio-onboarding"> <div class="portfolio-onboarding">
<h1 class="portfolio-onboarding__heading" data-cy="page-title">Portfolio</h1> <h1
<portfolio-illustration data-cy="portfolio-onboarding-illustration" class="portfolio-onboarding__illustration" /> class="portfolio-onboarding__heading"
<h2 class="portfolio-onboarding__subheading" data-cy="portfolio-onboarding-subtitle">Woran denken Sie gerade?</h2> data-cy="page-title"
<p class="portfolio-onboarding__text" data-cy="portfolio-onboarding-text"> >
Portfolio
</h1>
<portfolio-illustration
data-cy="portfolio-onboarding-illustration"
class="portfolio-onboarding__illustration"
/>
<h2
class="portfolio-onboarding__subheading"
data-cy="portfolio-onboarding-subtitle"
>
Woran denken Sie gerade?
</h2>
<p
class="portfolio-onboarding__text"
data-cy="portfolio-onboarding-text"
>
Hier können Sie Projekte erstellen, um Ihre Gedanken festzuhalten oder Ihre Arbeit zu dokumentieren. Hier können Sie Projekte erstellen, um Ihre Gedanken festzuhalten oder Ihre Arbeit zu dokumentieren.
</p> </p>
@ -12,8 +28,9 @@
</template> </template>
<script> <script>
const PortfolioIllustration = () => import('@/components/illustrations/PortfolioIllustration.vue'); import {defineAsyncComponent} from 'vue';
const CreateProjectButton = () => import('@/components/portfolio/CreateProjectButton.vue'); const PortfolioIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/PortfolioIllustration'));
const CreateProjectButton = defineAsyncComponent(() => import('@/components/portfolio/CreateProjectButton'));
export default { export default {
components: {CreateProjectButton, PortfolioIllustration}, components: {CreateProjectButton, PortfolioIllustration},
}; };

View File

@ -1,20 +1,48 @@
<template> <template>
<div class="project-actions" data-cy="project-actions"> <div
<a class="project-actions__more-link" @click.stop="toggleMenu"> class="project-actions"
data-cy="project-actions"
>
<a
class="project-actions__more-link"
@click.stop="toggleMenu"
>
<ellipses /> <ellipses />
</a> </a>
<widget-popover class="project-actions__popover" v-if="showMenu" @hide-me="showMenu = false"> <widget-popover
class="project-actions__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<li class="popover-links__link"> <li class="popover-links__link">
<a data-cy="delete-project" @click="deleteProject(slug)">Projekt löschen</a> <a
data-cy="delete-project"
@click="deleteProject(slug)"
>Projekt löschen</a>
</li> </li>
<li class="popover-links__link"> <li class="popover-links__link">
<a data-cy="edit-project" @click="editProject(slug)">Projekt bearbeiten</a> <a
data-cy="edit-project"
@click="editProject(slug)"
>Projekt bearbeiten</a>
</li> </li>
<li class="popover-links__link" v-if="!final && shareButtons"> <li
<a data-cy="share-project" @click="updateProjectShareState(slug, true)">Projekt teilen</a> class="popover-links__link"
v-if="!final && shareButtons"
>
<a
data-cy="share-project"
@click="updateProjectShareState(slug, true)"
>Projekt teilen</a>
</li> </li>
<li class="popover-links__link" v-if="final && shareButtons"> <li
<a data-cy="unshare-project" @click="updateProjectShareState(slug, false)">Projekt nicht mehr teilen</a> class="popover-links__link"
v-if="final && shareButtons"
>
<a
data-cy="unshare-project"
@click="updateProjectShareState(slug, false)"
>Projekt nicht mehr teilen</a>
</li> </li>
</widget-popover> </widget-popover>
</div> </div>
@ -28,7 +56,8 @@ import PROJECTS_QUERY from '@/graphql/gql/queries/allProjects.gql';
import updateProjectShareState from '@/mixins/update-project-share-state'; import updateProjectShareState from '@/mixins/update-project-share-state';
import {removeAtIndex} from '@/graphql/immutable-operations.ts'; import {removeAtIndex} from '@/graphql/immutable-operations.ts';
const Ellipses = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Ellipses.vue'); import {defineAsyncComponent} from 'vue';
const Ellipses = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses.vue'));
export default { export default {
props: { props: {
@ -67,38 +96,30 @@ export default {
this.$router.push({name: 'edit-project', params: {slug}}); this.$router.push({name: 'edit-project', params: {slug}});
}, },
deleteProject(slug) { deleteProject(slug) {
this.$apollo this.$apollo.mutate({
.mutate({
mutation: DELETE_PROJECT_MUTATION, mutation: DELETE_PROJECT_MUTATION,
variables: { variables: {
input: { input: {
slug, slug,
}, },
}, },
update( update(store, {data: {deleteProject: {success}}}) {
store,
{
data: {
deleteProject: { success },
},
}
) {
if (success) { if (success) {
const {projects: prevProjects} = store.readQuery({query: PROJECTS_QUERY}); const {projects: prevProjects} = store.readQuery({query: PROJECTS_QUERY});
if (prevProjects) { if (prevProjects) {
let index = prevProjects.findIndex((project) => project.slug === slug); let index = prevProjects.findIndex(project => project.slug === slug);
const projects = removeAtIndex(prevProjects, index); const projects = removeAtIndex(prevProjects, index);
const data = { const data = {
projects, projects
}; };
store.writeQuery({query: PROJECTS_QUERY, data}); store.writeQuery({query: PROJECTS_QUERY, data});
} }
} }
}, },
}) }).then(() => {
.then(() => {
this.$router.push('/portfolio'); this.$router.push('/portfolio');
}); });
}, },
@ -107,7 +128,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/_helpers.scss'; @import "~styles/_helpers.scss";
.project-actions { .project-actions {
position: relative; position: relative;

View File

@ -1,21 +1,43 @@
<template> <template>
<div class="project-entry" data-cy="project-entry"> <div
<more-options-widget class="project-entry__more" data-cy="project-entry-more" v-if="!readOnly"> class="project-entry"
data-cy="project-entry"
>
<more-options-widget
class="project-entry__more"
data-cy="project-entry-more"
v-if="!readOnly"
>
<li class="popover-links__link"> <li class="popover-links__link">
<a data-cy="edit-project-entry" @click="editProjectEntry()">Eintrag bearbeiten</a> <a
data-cy="edit-project-entry"
@click="editProjectEntry()"
>Eintrag bearbeiten</a>
</li> </li>
<li class="popover-links__link"> <li class="popover-links__link">
<a data-cy="delete-project-entry" @click="deleteProjectEntry()">Eintrag löschen</a> <a
data-cy="delete-project-entry"
@click="deleteProjectEntry()"
>Eintrag löschen</a>
</li> </li>
</more-options-widget> </more-options-widget>
<h3 class="project-entry__heading" data-cy="project-entry-date"> <h3
class="project-entry__heading"
data-cy="project-entry-date"
>
{{ createdDateTime }} {{ createdDateTime }}
</h3> </h3>
<p class="project-entry__paragraph" data-cy="project-entry-activity"> <p
class="project-entry__paragraph"
data-cy="project-entry-activity"
>
{{ description }} {{ description }}
</p> </p>
<p class="project-entry__paragraph" v-if="documentUrl"> <p
class="project-entry__paragraph"
v-if="documentUrl"
>
<document-block :value="{url: documentUrl}" /> <document-block :value="{url: documentUrl}" />
</p> </p>
<div class="project-entry__date"> <div class="project-entry__date">
@ -31,9 +53,9 @@ import DELETE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/deleteProject
import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql'; import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql';
import {dateFilter, dateTimeFilter} from '@/filters/date-filter'; import {dateFilter, dateTimeFilter} from '@/filters/date-filter';
import {removeAtIndex} from '@/graphql/immutable-operations.ts'; import {removeAtIndex} from '@/graphql/immutable-operations.ts';
import {defineAsyncComponent} from 'vue';
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
const DocumentBlock = () =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock');
export default { export default {
props: ['description', 'documentUrl', 'created', 'id', 'readOnly'], props: ['description', 'documentUrl', 'created', 'id', 'readOnly'],
@ -64,14 +86,7 @@ export default {
id: this.id, id: this.id,
}, },
}, },
update( update(store, {data: {deleteProjectEntry: {success}}}) {
store,
{
data: {
deleteProjectEntry: { success },
},
}
) {
if (success) { if (success) {
const query = PROJECT_QUERY; const query = PROJECT_QUERY;
const variables = { const variables = {
@ -79,7 +94,7 @@ export default {
}; };
const {project} = store.readQuery({query, variables}); const {project} = store.readQuery({query, variables});
if (project) { if (project) {
const index = project.entries.findIndex((entry) => entry.id === projectEntry.id); const index = project.entries.findIndex(entry => entry.id === projectEntry.id);
const entries = removeAtIndex(project.entries, index); const entries = removeAtIndex(project.entries, index);
const data = { const data = {
project: { project: {
@ -98,7 +113,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.project-entry { .project-entry {
background-color: $color-white; background-color: $color-white;
@ -136,5 +151,6 @@ export default {
display: block; display: block;
} }
} }
} }
</style> </style>

View File

@ -1,7 +1,12 @@
<template> <template>
<modal :hide-header="false"> <modal :hide-header="false">
<template #header> <template #header>
<h2 class="project-entry-modal__heading" data-cy="modal-title">Beitrag erfassen</h2> <h2
class="project-entry-modal__heading"
data-cy="modal-title"
>
Beitrag erfassen
</h2>
</template> </template>
<div class="project-entry-modal"> <div class="project-entry-modal">
@ -30,26 +35,6 @@
</div> </div>
</div> </div>
</div> </div>
<<<<<<< HEAD
<div slot="footer">
<a class="button button--primary" data-cy="modal-save-button" @click="$emit('save', localProjectEntry)"
>Speichern</a
>
<a class="button" @click="$emit('hide')">Abbrechen</a>
</div>
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
<div slot="footer">
<a
class="button button--primary"
data-cy="modal-save-button"
@click="$emit('save', localProjectEntry)"
>Speichern</a>
<a
class="button"
@click="$emit('hide')"
>Abbrechen</a>
</div>
=======
<template #footer> <template #footer>
<a <a
class="button button--primary" class="button button--primary"
@ -61,7 +46,6 @@
@click="$emit('hide')" @click="$emit('hide')"
>Abbrechen</a> >Abbrechen</a>
</template> </template>
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
</modal> </modal>
</template> </template>
@ -69,17 +53,10 @@
import Modal from '@/components/Modal'; import Modal from '@/components/Modal';
import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText'; import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText';
<<<<<<< HEAD
import { PROJECT_ENTRY_TEMPLATE } from '@/consts/strings.consts';
const FileUpload = () => import('@/components/ui/file-upload/FileUpload.vue');
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
import {PROJECT_ENTRY_TEMPLATE} from '@/consts/strings.consts';
const FileUpload = () => import('@/components/ui/file-upload/FileUpload');
=======
import {PROJECT_ENTRY_TEMPLATE} from '@/consts/strings.consts'; import {PROJECT_ENTRY_TEMPLATE} from '@/consts/strings.consts';
const FileUpload = () => import('@/components/ui/file-upload/FileUpload'); import {defineAsyncComponent} from 'vue';
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3) const FileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/FileUpload'));
export default { export default {
props: { props: {
@ -97,12 +74,9 @@ export default {
data() { data() {
return { return {
localProjectEntry: Object.assign( localProjectEntry: Object.assign({}, {
{},
{
...this.projectEntry, ...this.projectEntry,
} }),
),
}; };
}, },
@ -118,7 +92,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '~styles/helpers'; @import "~styles/helpers";
.project-entry-modal { .project-entry-modal {
display: flex; display: flex;
@ -161,5 +135,7 @@ export default {
@include heading-3; @include heading-3;
margin-bottom: 0; margin-bottom: 0;
} }
} }
</style> </style>

View File

@ -9,7 +9,8 @@
</template> </template>
<script> <script>
const ShareIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ShareIcon'); import {defineAsyncComponent} from 'vue';
const ShareIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ShareIcon'));
export default { export default {
props: { props: {

View File

@ -7,27 +7,31 @@
<slot /> <slot />
</div> </div>
<div class="activity-entry__link" @click="$emit('link')"> <div
class="activity-entry__link"
@click="$emit('link')"
>
<chevron-right class="activity-entry__icon" /> <chevron-right class="activity-entry__icon" />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
const ChevronRight = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronRight'); import {defineAsyncComponent} from 'vue';
const ChevronRight = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight'));
export default { export default {
props: ['title'], props: ['title'],
components: { components: {
ChevronRight, ChevronRight
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
@import '@/styles/_mixins.scss'; @import "@/styles/_mixins.scss";
.activity-entry { .activity-entry {
padding: $small-spacing 0; padding: $small-spacing 0;
@ -59,7 +63,7 @@ export default {
width: 30px; width: 30px;
} }
:deep(p) { /deep/ p {
@include regular-text; @include regular-text;
} }
} }

View File

@ -35,9 +35,10 @@
<script> <script>
import TOGGLE_SIDEBAR from '@/graphql/gql/local/mutations/toggleSidebar.gql'; import TOGGLE_SIDEBAR from '@/graphql/gql/local/mutations/toggleSidebar.gql';
import {defineAsyncComponent} from 'vue';
const DefaultAvatar = () => import(/* webpackChunkName: "icons" */'@/components/icons/DefaultAvatar'); const DefaultAvatar = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DefaultAvatar'));
const PenIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'); const PenIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'));
export default { export default {
props: { props: {

View File

@ -1,9 +1,15 @@
<template> <template>
<div class="content-bookmark module-activity-entry"> <div class="content-bookmark module-activity-entry">
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div v-if="content.type === 'text_block'" v-html="text" /> <div
v-if="content.type === 'text_block'"
v-html="text"
/>
<div v-else-if="content.type === 'link_block'"> <div v-else-if="content.type === 'link_block'">
<link-block :value="content.value" :no-margin="true" /> <link-block
:value="content.value"
:no-margin="true"
/>
</div> </div>
<p v-else> <p v-else>
{{ type }} {{ type }}
@ -12,7 +18,8 @@
</template> </template>
<script> <script>
const LinkBlock = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/LinkBlock'); import {defineAsyncComponent} from 'vue';
const LinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/LinkBlock'));
export default { export default {
props: ['bookmark'], props: ['bookmark'],
@ -20,8 +27,8 @@ export default {
computed: { computed: {
content() { content() {
return this.bookmark.contentBlock return this.bookmark.contentBlock
? this.bookmark.contentBlock.contents.find((e) => e.id === this.bookmark.uuid) ? this.bookmark.contentBlock.contents.find(e => e.id === this.bookmark.uuid)
: this.bookmark.instrument.contents.find((e) => e.id === this.bookmark.uuid); : this.bookmark.instrument.contents.find(e => e.id === this.bookmark.uuid);
}, },
text() { text() {
return this.content.value.text ? this.content.value.text : 'TO BE DEFINED'; return this.content.value.text ? this.content.value.text : 'TO BE DEFINED';
@ -39,7 +46,7 @@ export default {
default: default:
return this.content.type; return this.content.type;
} }
}, }
}, }
}; };
</script> </script>

View File

@ -1,21 +1,26 @@
<template> <template>
<a class="edit-group-name" data-cy="edit-group-name-link" @click="$emit('edit')"> <a
class="edit-group-name"
data-cy="edit-group-name-link"
@click="$emit('edit')"
>
<pen-icon class="edit-group-name__icon" /> <pen-icon class="edit-group-name__icon" />
</a> </a>
</template> </template>
<script> <script>
const PenIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PenIcon'); import {defineAsyncComponent} from 'vue';
const PenIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'));
export default { export default {
components: { components: {
PenIcon, PenIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/_variables.scss'; @import "~styles/_variables.scss";
.edit-group-name { .edit-group-name {
&__icon { &__icon {

View File

@ -1,15 +1,26 @@
<template> <template>
<div class="profile"> <div class="profile">
<h1 class="profile__header">Profilbild</h1> <h1 class="profile__header">
<div class="profile-avatar" v-if="me.avatarUrl"> Profilbild
</h1>
<div
class="profile-avatar"
v-if="me.avatarUrl"
>
<div class="profile-avatar__image"> <div class="profile-avatar__image">
<avatar :avatar-url="me.avatarUrl" /> <avatar :avatar-url="me.avatarUrl" />
</div> </div>
<a class="profile-avatar__remove icon-button" @click="deleteAvatar()"> <a
class="profile-avatar__remove icon-button"
@click="deleteAvatar()"
>
<trash-icon class="profile-avatar__remove-icon icon-button__icon icon-button__icon--subtle" /> <trash-icon class="profile-avatar__remove-icon icon-button__icon icon-button__icon--subtle" />
</a> </a>
</div> </div>
<avatar-upload-form v-else @avatarUpdate="updateAvatar" /> <avatar-upload-form
v-else
@avatarUpdate="updateAvatar"
/>
</div> </div>
</template> </template>
@ -18,20 +29,21 @@ import UPDATE_AVATAR_QUERY from '@/graphql/gql/mutations/updateAvatarUrl.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql'; import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import AvatarUploadForm from '@/components/profile/AvatarUploadForm'; import AvatarUploadForm from '@/components/profile/AvatarUploadForm';
import Avatar from '@/components/profile/Avatar'; import Avatar from '@/components/profile/Avatar';
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon'); import {defineAsyncComponent} from 'vue';
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
export default { export default {
components: { components: {
AvatarUploadForm, AvatarUploadForm,
Avatar, Avatar,
TrashIcon, TrashIcon
}, },
data() { data() {
return { return {
me: { me: {
avatarUrl: '', avatarUrl: ''
}, }
}; };
}, },
apollo: { apollo: {
@ -44,46 +56,37 @@ export default {
this.updateAvatar(''); this.updateAvatar('');
}, },
updateAvatar (url) { updateAvatar (url) {
this.$apollo this.$apollo.mutate({
.mutate({
mutation: UPDATE_AVATAR_QUERY, mutation: UPDATE_AVATAR_QUERY,
variables: { variables: {
input: { input: {
avatarUrl: url, avatarUrl: url
},
},
update(
store,
{
data: {
updateAvatar: { success },
},
} }
) { },
update(store, {data: {updateAvatar: {success}}}) {
if (success) { if (success) {
const {me} = store.readQuery({query: ME_QUERY}); const {me} = store.readQuery({query: ME_QUERY});
if (me) { if (me) {
const data = { const data = {
me: { me: {
...me, ...me,
avatarUrl: url, avatarUrl: url
}, }
}; };
store.writeQuery({query: ME_QUERY, data}); store.writeQuery({query: ME_QUERY, data});
} }
} }
}, }
}) }).catch((error) => {
.catch((error) => {
console.warn('UploadError', error); console.warn('UploadError', error);
}); });
}, }
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/_variables.scss'; @import "@/styles/_variables.scss";
.profile-avatar { .profile-avatar {
display: flex; display: flex;
@ -98,4 +101,5 @@ export default {
.profile-avatar { .profile-avatar {
margin-bottom: $large-spacing; margin-bottom: $large-spacing;
} }
</style> </style>

View File

@ -84,7 +84,8 @@
import me from '@/mixins/me'; import me from '@/mixins/me';
import LogoutWidget from '@/components/LogoutWidget'; import LogoutWidget from '@/components/LogoutWidget';
import {MY_TEAM} from '@/router/me.names'; import {MY_TEAM} from '@/router/me.names';
const Cross = () => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'); import {defineAsyncComponent} from 'vue';
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
export default { export default {

View File

@ -1,13 +1,18 @@
<template> <template>
<router-link class="add-room-entry-button" data-cy="add-room-entry-button" :to="addRoomEntryRoute"> <router-link
class="add-room-entry-button"
data-cy="add-room-entry-button"
:to="addRoomEntryRoute"
>
<plus-icon class="add-room-entry-button__icon" /> <plus-icon class="add-room-entry-button__icon" />
<span class="add-room-entry-button__text">Beitrag erfassen</span> <span class="add-room-entry-button__text">Beitrag erfassen</span>
</router-link> </router-link>
</template> </template>
<script> <script>
import {defineAsyncComponent} from 'vue';
const PlusIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PlusIcon'));
import { ADD_ROOM_ENTRY_PAGE } from '@/router/room.names'; import { ADD_ROOM_ENTRY_PAGE } from '@/router/room.names';
const PlusIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PlusIcon');
export default { export default {
props: ['parent'], props: ['parent'],
@ -27,7 +32,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.add-room-entry-button { .add-room-entry-button {
border: 2px solid $color-white; border: 2px solid $color-white;

View File

@ -1,15 +1,14 @@
<template> <template>
<div class="entry-count-widget"> <div class="entry-count-widget">
<component :is="icon" /> <component :is="icon" />
<span data-cy="entry-count" <span data-cy="entry-count">{{ entryCount }} <template v-if="verbose">{{ entryCount === 1 ? 'Beitrag' : 'Beiträge' }}</template></span>
>{{ entryCount }} <template v-if="verbose">{{ entryCount === 1 ? 'Beitrag' : 'Beiträge' }}</template></span
>
</div> </div>
</template> </template>
<script> <script>
import {defineAsyncComponent} from 'vue';
import SpeechBubbleIcon from '@/components/icons/SpeechBubbleIcon'; import SpeechBubbleIcon from '@/components/icons/SpeechBubbleIcon';
const Cards = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Cards.vue'); const Cards = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Cards.vue'));
export default { export default {
props: { props: {
@ -22,8 +21,8 @@ export default {
}, },
icon: { icon: {
type: String, type: String,
default: 'cards', default: 'cards'
}, }
}, },
components: { components: {
@ -35,7 +34,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.entry-count-widget { .entry-count-widget {
display: flex; display: flex;
@ -53,4 +52,5 @@ export default {
@include room-widget-text-style; @include room-widget-text-style;
} }
} }
</style> </style>

View File

@ -8,7 +8,11 @@
> >
<ellipses /> <ellipses />
</a> </a>
<widget-popover class="more-actions__popover" v-if="showMenu" @hide-me="showMenu = false"> <widget-popover
class="more-actions__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<slot :toggle="toggleMenu" /> <slot :toggle="toggleMenu" />
</widget-popover> </widget-popover>
</div> </div>
@ -16,14 +20,15 @@
<script> <script>
import WidgetPopover from '@/components/ui/WidgetPopover'; import WidgetPopover from '@/components/ui/WidgetPopover';
const Ellipses = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Ellipses'); import {defineAsyncComponent} from 'vue';
const Ellipses = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses'));
export default { export default {
props: { props: {
background: { background: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
components: { components: {

View File

@ -8,19 +8,20 @@
</template> </template>
<script> <script>
const Group = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Group.vue'); import {defineAsyncComponent} from 'vue';
const Group = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Group.vue'));
export default { export default {
props: ['name'], props: ['name'],
components: { components: {
Group, Group
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.room-group-widget { .room-group-widget {
display: flex; display: flex;
@ -34,7 +35,7 @@ export default {
} }
& > span { & > span {
@include room-widget-text-style; @include room-widget-text-style;;
} }
} }
</style> </style>

View File

@ -12,26 +12,28 @@
</template> </template>
<script> <script>
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon');
const ClosedEyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ClosedEyeIcon'); import {defineAsyncComponent} from 'vue';
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
const ClosedEyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ClosedEyeIcon'));
export default { export default {
props: { props: {
restricted: { restricted: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
components: { components: {
ClosedEyeIcon, ClosedEyeIcon,
EyeIcon, EyeIcon
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.room-visibility-widget { .room-visibility-widget {
display: flex; display: flex;
@ -42,11 +44,11 @@ export default {
width: 30px; width: 30px;
fill: $color-charcoal-dark; fill: $color-charcoal-dark;
margin-right: 15px; margin-right: 15px;
flex-shrink: 0;
} }
& > span { & > span {
@include room-widget-text-style; @include room-widget-text-style;
} }
} }
</style> </style>

View File

@ -1,12 +1,25 @@
<template> <template>
<div class="rooms-onboarding"> <div class="rooms-onboarding">
<h1 class="rooms-onboarding__heading" data-cy="page-title">Räume</h1> <h1
class="rooms-onboarding__heading"
data-cy="page-title"
>
Räume
</h1>
<rooms-illustration class="rooms-onboarding__illustration" /> <rooms-illustration class="rooms-onboarding__illustration" />
<p data-cy="rooms-onboarding-text" class="rooms-onboarding__text"> <p
data-cy="rooms-onboarding-text"
class="rooms-onboarding__text"
>
Hier können Sie Räume erstellen, damit SchülerInnen zusammenarbeiten und Beiträge teilen können. Hier können Sie Räume erstellen, damit SchülerInnen zusammenarbeiten und Beiträge teilen können.
</p> </p>
<div class="rooms-onboarding__button"> <div class="rooms-onboarding__button">
<router-link :to="newRoomRoute" class="button button--primary" data-cy="create-room-button" v-if="isTeacher"> <router-link
:to="newRoomRoute"
class="button button--primary"
data-cy="create-room-button"
v-if="isTeacher"
>
Raum erstellen Raum erstellen
</router-link> </router-link>
</div> </div>
@ -15,8 +28,8 @@
<script> <script>
import {NEW_ROOM_PAGE} from '@/router/room.names'; import {NEW_ROOM_PAGE} from '@/router/room.names';
const RoomsIllustration = () => import {defineAsyncComponent} from 'vue';
import(/* webpackChunkName: "illustrations" */ '@/components/illustrations/RoomsIllustration'); const RoomsIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/RoomsIllustration'));
export default { export default {
props: { props: {

View File

@ -1,5 +1,8 @@
<template> <template>
<div class="class-selection" v-if="currentClassSelection"> <div
class="class-selection"
v-if="currentClassSelection"
>
<div <div
data-cy="class-selection" data-cy="class-selection"
class="class-selection__selected-class selected-class" class="class-selection__selected-class selected-class"
@ -8,7 +11,12 @@
<current-class class="selected-class__text" /> <current-class class="selected-class__text" />
<chevron-down class="selected-class__dropdown-icon" /> <chevron-down class="selected-class__dropdown-icon" />
</div> </div>
<widget-popover :mobile="mobile" class="class-selection__popover" v-if="showPopover" @hide-me="showPopover = false"> <widget-popover
:mobile="mobile"
class="class-selection__popover"
v-if="showPopover"
@hide-me="showPopover = false"
>
<li <li
:label="schoolClass.name" :label="schoolClass.name"
:item="schoolClass" :item="schoolClass"
@ -26,13 +34,25 @@
v-if="me.isTeacher && !me.readOnly" v-if="me.isTeacher && !me.readOnly"
@click="closeSidebar" @click="closeSidebar"
> >
<router-link :to="{ name: 'create-class' }" tag="span" class="popover-links__link-with-icon"> <router-link
:to="{name: 'create-class'}"
tag="span"
class="popover-links__link-with-icon"
>
<add-icon class="popover-links__icon" /> <add-icon class="popover-links__icon" />
<span>Klasse erfassen</span> <span>Klasse erfassen</span>
</router-link> </router-link>
</li> </li>
<li class="popover-links__link popover-links__link--large popover-links__divider" @click="closeSidebar"> <li
<router-link :to="{ name: 'old-classes' }" tag="span"> Alte Klassen anzeigen </router-link> class="popover-links__link popover-links__link--large popover-links__divider"
@click="closeSidebar"
>
<router-link
:to="{name: 'old-classes'}"
tag="span"
>
Alte Klassen anzeigen
</router-link>
</li> </li>
</widget-popover> </widget-popover>
</div> </div>
@ -45,15 +65,17 @@ import CurrentClass from '@/components/school-class/CurrentClass';
import updateSelectedClassMixin from '@/mixins/update-selected-class'; import updateSelectedClassMixin from '@/mixins/update-selected-class';
import sidebarMixin from '@/mixins/sidebar'; import sidebarMixin from '@/mixins/sidebar';
import meMixin from '@/mixins/me'; import meMixin from '@/mixins/me';
const ChevronDown = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronDown'); import {defineAsyncComponent} from 'vue';
const AddIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/AddIcon'); const ChevronDown = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronDown'));
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon'));
export default { export default {
props: { props: {
mobile: { mobile: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
mixins: [updateSelectedClassMixin, sidebarMixin, meMixin], mixins: [updateSelectedClassMixin, sidebarMixin, meMixin],
@ -61,22 +83,22 @@ export default {
WidgetPopover, WidgetPopover,
ChevronDown, ChevronDown,
CurrentClass, CurrentClass,
AddIcon, AddIcon
}, },
data() { data() {
return { return {
showPopover: false, showPopover: false
}; };
}, },
computed: { computed: {
currentClassSelection() { currentClassSelection() {
let currentClass = this.me.schoolClasses.find((schoolClass) => { let currentClass = this.me.schoolClasses.find(schoolClass => {
return schoolClass.id === this.me.selectedClass.id; return schoolClass.id === this.me.selectedClass.id;
}); });
return currentClass || this.me.schoolClasses[0]; return currentClass || this.me.schoolClasses[0];
}, }
}, },
methods: { methods: {
@ -84,13 +106,14 @@ export default {
this.updateSelectedClass(selectedClass); this.updateSelectedClass(selectedClass);
this.showPopover = false; this.showPopover = false;
this.closeSidebar('profile'); this.closeSidebar('profile');
}
}, },
},
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.class-selection { .class-selection {
position: relative; position: relative;
@ -104,6 +127,7 @@ export default {
left: 0; left: 0;
transform: translateY($small-spacing); transform: translateY($small-spacing);
} }
} }
.selected-class { .selected-class {

View File

@ -6,39 +6,47 @@
class="base-input-container__input" class="base-input-container__input"
data-cy="base-input-input" data-cy="base-input-input"
@change.prevent="$emit('input', $event.target.checked, item)" @change.prevent="$emit('input', $event.target.checked, item)"
/> >
<span <span
:class="{ :class="{'base-input-container__checkbox': type==='checkbox', 'base-input-container__radiobutton': type === 'radiobutton'}"
'base-input-container__checkbox': type === 'checkbox',
'base-input-container__radiobutton': type === 'radiobutton',
}"
class="base-input-container__icon checkbox" class="base-input-container__icon checkbox"
> >
<tick v-if="type === 'checkbox'" /> <tick v-if="type === 'checkbox'" />
<circle-icon data-cy="circle-icon" v-if="type === 'radiobutton'" /> <circle-icon
data-cy="circle-icon"
v-if="type === 'radiobutton'"
/>
</span> </span>
<span class="base-input-container__label" data-cy="base-input-label" v-if="label">{{ label }}</span> <span
<slot class="base-input-container__label" v-if="!label" /> class="base-input-container__label"
data-cy="base-input-label"
v-if="label"
>{{ label }}</span>
<slot
class="base-input-container__label"
v-if="!label"
/>
</label> </label>
</template> </template>
<script> <script>
const Tick = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Tick'); import {defineAsyncComponent} from 'vue';
const CircleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CircleIcon'); const Tick = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Tick'));
const CircleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CircleIcon'));
export default { export default {
props: { props: {
label: String, label: String,
checked: { checked: {
type: Boolean, type: Boolean
}, },
item: Object, item: Object,
type: String, type: String
}, },
components: { components: {
Tick, Tick,
CircleIcon, CircleIcon
}, }
}; };
</script> </script>

View File

@ -1,16 +1,25 @@
<template> <template>
<li class="popover-links__link"> <li
<a class="popover-link" @click="$emit('link-action')"> class="popover-links__link"
<component class="popover-link__icon" :is="icon" /> >
<a
class="popover-link"
@click="$emit('link-action')"
>
<component
class="popover-link__icon"
:is="icon"
/>
<span class="popover-link__text">{{ text }}</span> <span class="popover-link__text">{{ text }}</span>
</a> </a>
</li> </li>
</template> </template>
<script> <script>
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'); import {defineAsyncComponent} from 'vue';
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon'); const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
const PenIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/PenIcon'); const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
const PenIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/PenIcon'));
export default { export default {
props: { props: {

View File

@ -1,7 +1,11 @@
<template> <template>
<div class="file-upload"> <div class="file-upload">
<template v-if="document"> <template v-if="document">
<document-block :value="{ url: document }" show-trash-icon @trash="$emit('change-document-url', '')" /> <document-block
:value="{url: document}"
show-trash-icon
@trash="$emit('change-document-url', '')"
/>
</template> </template>
<template v-else> <template v-else>
<simple-file-upload <simple-file-upload
@ -14,8 +18,9 @@
</template> </template>
<script> <script>
const SimpleFileUpload = () => import('@/components/ui/file-upload/SimpleFileUpload.vue'); import {defineAsyncComponent} from 'vue';
const DocumentBlock = () => import('@/components/content-blocks/DocumentBlock.vue'); const SimpleFileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUpload'));
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
export default { export default {
props: { props: {
@ -25,8 +30,8 @@ export default {
}, },
withText: { withText: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
components: {SimpleFileUpload, DocumentBlock}, components: {SimpleFileUpload, DocumentBlock},
}; };
@ -34,4 +39,5 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import '~styles/helpers';
</style> </style>

View File

@ -1,51 +1,43 @@
<template> <template>
<div class="simple-file-upload"> <div class="simple-file-upload">
<<<<<<< HEAD
<component :is="button" @click.native="clickUploadCare" />
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
<component
:is="button"
@click.native="clickUploadCare"
/>
=======
<component <component
:is="button" :is="button"
@click="clickUploadCare" @click="clickUploadCare"
/> />
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
<simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" /> <simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" />
</div> </div>
</template> </template>
<script> <script>
const SimpleFileUploadHiddenInput = () => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput.vue'); import {defineAsyncComponent} from 'vue';
const SimpleFileUploadIcon = () => import('@/components/ui/file-upload/SimpleFileUploadIcon.vue'); const SimpleFileUploadHiddenInput = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput'));
const SimpleFileUploadIconAndText = () => import('@/components/ui/file-upload/SimpleFileUploadIconAndText.vue'); const SimpleFileUploadIcon = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadIcon'));
const DocumentIcon = () => import('@/components/icons/DocumentIcon.vue'); const SimpleFileUploadIconAndText = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadIconAndText'));
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
export default { export default {
props: { props: {
value: { value: {
type: String, type: String,
default: '', default: ''
}, },
withText: { withText: {
type: Boolean, type: Boolean,
default: false, default: false
}, }
}, },
components: { components: {
SimpleFileUploadHiddenInput, SimpleFileUploadHiddenInput,
DocumentIcon, DocumentIcon,
SimpleFileUploadIcon, SimpleFileUploadIcon,
SimpleFileUploadIconAndText, SimpleFileUploadIconAndText
}, },
computed: { computed: {
button() { button() {
return this.withText ? 'simple-file-upload-icon-and-text' : 'simple-file-upload-icon'; return this.withText ? 'simple-file-upload-icon-and-text' : 'simple-file-upload-icon';
}, }
}, },
methods: { methods: {
@ -53,13 +45,13 @@ export default {
// workaround for styling the uploadcare widget // workaround for styling the uploadcare widget
let button = this.$el.querySelector('.uploadcare--widget__button'); let button = this.$el.querySelector('.uploadcare--widget__button');
button.click(); button.click();
}, }
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/_helpers'; @import "~styles/_helpers";
.simple-file-upload { .simple-file-upload {
height: 25px; height: 25px;
@ -74,7 +66,7 @@ export default {
} }
} }
:deep(.uploadcare--widget) { /deep/ .uploadcare--widget {
display: none; display: none;
} }
</style> </style>

View File

@ -5,7 +5,8 @@
</template> </template>
<script> <script>
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon'); import {defineAsyncComponent} from 'vue';
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
export default { export default {
components: {DocumentIcon}, components: {DocumentIcon},

View File

@ -12,8 +12,9 @@
</template> </template>
<script> <script>
const SimpleFileUploadHiddenInput = () => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput.vue'); import {defineAsyncComponent} from 'vue';
const ButtonWithIconAndText = () => import('@/components/ui/ButtonWithIconAndText.vue'); const SimpleFileUploadHiddenInput = defineAsyncComponent(() => import('@/components/ui/file-upload/SimpleFileUploadHiddenInput'));
const ButtonWithIconAndText = defineAsyncComponent(() => import('@/components/ui/ButtonWithIconAndText'));
export default { export default {
props: ['value'], props: ['value'],
@ -34,7 +35,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/_helpers'; @import "~styles/_helpers";
.simple-file-upload { .simple-file-upload {
width: 25px; width: 25px;
@ -54,7 +55,7 @@ export default {
} }
} }
:deep(.uploadcare--widget) { /deep/ .uploadcare--widget {
display: none; display: none;
} }
</style> </style>

View File

@ -1,18 +1,17 @@
const LinkIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/LinkIcon'); import {defineAsyncComponent} from 'vue';
const VideoIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/VideoIcon'); const LinkIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LinkIcon'));
const ImageIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ImageIcon'); const VideoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/VideoIcon'));
const TextIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TextIcon'); const ImageIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ImageIcon'));
const SpeechBubbleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/SpeechBubbleIcon'); const TextIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TextIcon'));
const DocumentIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon'); const SpeechBubbleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/SpeechBubbleIcon'));
const TitleIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TitleIcon'); const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon'));
const DocumentWithLinesIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentWithLinesIcon'); const TitleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TitleIcon'));
const DocumentWithLinesIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentWithLinesIcon'));
const ArrowThinBottom = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinBottom'); const ArrowThinBottom = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinBottom'));
const ArrowThinDown = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinDown'); const ArrowThinDown = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinDown'));
const ArrowThinTop = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinTop'); const ArrowThinTop = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinTop'));
const ArrowThinUp = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ArrowThinUp'); const ArrowThinUp = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ArrowThinUp'));
const TrashIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TrashIcon'));
const TrashIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/TrashIcon');
/* /*
for icons with a single word, leave the *-icon name, to prevent conflicts for icons with a single word, leave the *-icon name, to prevent conflicts
@ -31,5 +30,5 @@ export default {
ArrowThinDown, ArrowThinDown,
ArrowThinTop, ArrowThinTop,
ArrowThinUp, ArrowThinUp,
TrashIcon, TrashIcon
}; };

View File

@ -1,8 +1,18 @@
<template> <template>
<div class="visibility-action"> <div class="visibility-action">
<a class="visibility-action__action-button" v-if="canManageContent" @click="toggleVisibility()"> <a
<closed-eye-icon class="visibility-action__action-icon action-icon" v-if="hidden" /> class="visibility-action__action-button"
<eye-icon class="visibility-action__action-icon action-icon" v-else /> v-if="canManageContent"
@click="toggleVisibility()"
>
<closed-eye-icon
class="visibility-action__action-icon action-icon"
v-if="hidden"
/>
<eye-icon
class="visibility-action__action-icon action-icon"
v-else
/>
</a> </a>
</div> </div>
</template> </template>
@ -13,36 +23,37 @@ import me from '@/mixins/me';
import {TYPES, CONTENT_TYPE} from '@/consts/types'; import {TYPES, CONTENT_TYPE} from '@/consts/types';
import {createVisibilityMutation, hidden} from '@/helpers/visibility'; import {createVisibilityMutation, hidden} from '@/helpers/visibility';
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'); import {defineAsyncComponent} from 'vue';
const ClosedEyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/ClosedEyeIcon'); const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
const ClosedEyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ClosedEyeIcon'));
export default { export default {
props: { props: {
block: { block: {
type: Object, type: Object,
default: () => ({}), default: () => ({})
}, },
type: { type: {
type: String, type: String,
default: CONTENT_TYPE, default: CONTENT_TYPE,
validator: (value) => { validator: value => {
// value must be one of TYPES // value must be one of TYPES
return TYPES.indexOf(value) !== -1; return TYPES.indexOf(value) !== -1;
}, }
}, }
}, },
mixins: [me], mixins: [me],
components: { components: {
EyeIcon, EyeIcon,
ClosedEyeIcon, ClosedEyeIcon
}, },
computed: { computed: {
hidden() { hidden() {
return hidden({type: this.type, block: this.block, schoolClass: this.schoolClass}); return hidden({type: this.type, block: this.block, schoolClass: this.schoolClass});
}, }
}, },
methods: { methods: {
@ -50,18 +61,16 @@ export default {
const hidden = !this.hidden; const hidden = !this.hidden;
const schoolClassId = this.schoolClass.id; const schoolClassId = this.schoolClass.id;
const visibility = [ const visibility = [{
{
schoolClassId, schoolClassId,
hidden, hidden
}, }];
];
const {mutation, variables} = createVisibilityMutation(this.type, this.block.id, visibility); const {mutation, variables} = createVisibilityMutation(this.type, this.block.id, visibility);
this.$apollo.mutate({ this.$apollo.mutate({
mutation, mutation,
variables, variables
}); });
}, },
}, },

View File

@ -1,6 +1,12 @@
<template> <template>
<div :class="specialContainerClass" class="container layout layout--fullscreen"> <div
<div class="close-button" @click="back"> :class="specialContainerClass"
class="container layout layout--fullscreen"
>
<div
class="close-button"
@click="back"
>
<cross class="close-button__icon" /> <cross class="close-button__icon" />
</div> </div>
@ -9,29 +15,30 @@
</template> </template>
<script> <script>
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon'); import {defineAsyncComponent} from 'vue';
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
export default { export default {
components: { components: {
Cross, Cross
}, },
computed: { computed: {
specialContainerClass() { specialContainerClass() {
let cls = this.$store.state.specialContainerClass; let cls = this.$store.state.specialContainerClass;
return [cls ? `skillbox--${cls}` : '']; return [cls ? `skillbox--${cls}` : ''];
}, }
}, },
methods: { methods: {
back() { back() {
this.$router.go(-1); this.$router.go(-1);
}, }
}, }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/styles/_default-layout.scss'; @import "@/styles/_default-layout.scss";
.close-button { .close-button {
margin-top: $medium-spacing; margin-top: $medium-spacing;
@ -42,4 +49,5 @@ export default {
display:flex; display:flex;
justify-content:flex-end; justify-content:flex-end;
} }
</style> </style>

View File

@ -1,22 +1,31 @@
<template> <template>
<div :class="{ 'layout--full-width': $route.meta.fullWidth }" class="skillbox layout layout--simple"> <div
<div class="close-button" @click="back"> :class="{'layout--full-width': $route.meta.fullWidth}"
class="skillbox layout layout--simple"
>
<div
class="close-button"
@click="back"
>
<cross class="close-button__icon" /> <cross class="close-button__icon" />
</div> </div>
<router-view class="layout__content" /> <router-view class="layout__content" />
<simple-footer class="layout__footer" v-if="enableFooter" /> <simple-footer
class="layout__footer"
v-if="enableFooter"
/>
</div> </div>
</template> </template>
<script> <script>
import SimpleFooter from '@/layouts/SimpleFooter'; import SimpleFooter from '@/layouts/SimpleFooter';
import {defineAsyncComponent} from 'vue';
const Cross = () => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon'); const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/CrossIcon'));
export default { export default {
components: { components: {
Cross, Cross,
SimpleFooter, SimpleFooter
}, },
computed: { computed: {
@ -25,19 +34,19 @@ export default {
return false; return false;
} }
return this.$flavor.showFooter; return this.$flavor.showFooter;
}, }
}, },
methods: { methods: {
back() { back() {
this.$router.go(-1); this.$router.go(-1);
}, }
}, }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '~styles/helpers'; @import "~styles/helpers";
.layout { .layout {
&--simple { &--simple {

View File

@ -10,20 +10,21 @@
</template> </template>
<script> <script>
import {defineAsyncComponent} from 'vue';
import flavorValues from '@/helpers/app-flavor'; import flavorValues from '@/helpers/app-flavor';
const ContentsIllustration = () => const ContentsIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/ContentsIllustration'));
import(/* webpackChunkName: "illustrations" */ '@/components/illustrations/ContentsIllustration'); const PortfolioIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/PortfolioIllustration'));
const PortfolioIllustration = () => const RoomsIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/RoomsIllustration'));
import(/* webpackChunkName: "illustrations" */ '@/components/illustrations/PortfolioIllustration'); const HelloIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/HelloIllustration'));
const RoomsIllustration = () => const HelloMyKVIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/HelloMyKVIllustration'));
import(/* webpackChunkName: "illustrations" */ '@/components/illustrations/RoomsIllustration'); const Hello = flavorValues.appFlavor === 'my-kv' ? HelloMyKVIllustration : HelloIllustration;
export default { export default {
components: { components: {
contents: ContentsIllustration, contents: ContentsIllustration,
portfolio: PortfolioIllustration, portfolio: PortfolioIllustration,
rooms: RoomsIllustration, rooms: RoomsIllustration,
hello: flavorValues.helloIllustration, hello: Hello
}, },
computed: { computed: {
@ -31,16 +32,14 @@ export default {
return this.$route.meta.illustration; return this.$route.meta.illustration;
}, },
illustrationAlignment() { illustrationAlignment() {
return this.$route.meta.illustrationAlign return this.$route.meta.illustrationAlign ? `split-view__illustration--${this.$route.meta.illustrationAlign}` : '';
? `split-view__illustration--${this.$route.meta.illustrationAlign}` }
: '';
},
}, },
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.split-view { .split-view {
background-color: $color-brand; background-color: $color-brand;

View File

@ -37,13 +37,14 @@
import ADD_COMMENT_MUTATION from 'gql/mutations/addComment.gql'; import ADD_COMMENT_MUTATION from 'gql/mutations/addComment.gql';
import CommentInput from '@/components/rooms/CommentInput'; import CommentInput from '@/components/rooms/CommentInput';
import Comment from '@/components/rooms/Comment'; import Comment from '@/components/rooms/Comment';
const TextBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/TextBlock'); import {defineAsyncComponent} from 'vue';
const ImageBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageBlock'); const TextBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/TextBlock'));
const ImageUrlBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageUrlBlock'); const ImageBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageBlock'));
const VideoBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/VideoBlock'); const ImageUrlBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ImageUrlBlock'));
const LinkBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/LinkBlock'); const VideoBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/VideoBlock'));
const DocumentBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'); const LinkBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/LinkBlock'));
const SubtitleBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SubtitleBlock'); const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'));
const SubtitleBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/SubtitleBlock'));
export default { export default {
components: { components: {

View File

@ -1,39 +1,64 @@
<template> <template>
<div class="hello" data-cy="hello-page"> <div
class="hello"
data-cy="hello-page"
>
<div class="about"> <div class="about">
<div class="about__logos logos"> <div class="about__logos logos">
<a href="https://www.hep-verlag.ch/" target="_blank"> <a
href="https://www.hep-verlag.ch/"
target="_blank"
>
<hep-logo-no-claim class="logos__logo" /> <hep-logo-no-claim class="logos__logo" />
</a> </a>
<a href="https://www.ehb.swiss/" target="_blank" v-if="$flavor.showEHB"> <a
href="https://www.ehb.swiss/"
target="_blank"
v-if="$flavor.showEHB"
>
<ehb-logo class="logos__logo" /> <ehb-logo class="logos__logo" />
</a> </a>
</div> </div>
<p class="about__text"> <p class="about__text">
<template v-if="$flavor.showEHB"> <template v-if="$flavor.showEHB">
{{ $flavor.textAppName }} ist ein Angebot des hep Verlags in Zusammenarbeit mit der Eidgenössischen Hochschule {{ $flavor.textAppName }} ist ein Angebot des hep Verlags in
für Berufsbildung (EHB). Zusammenarbeit mit der Eidgenössischen Hochschule für Berufsbildung (EHB).
</template>
<template v-else>
{{ $flavor.textAppName }} ist ein Angebot des hep Verlags.
</template> </template>
<template v-else> {{ $flavor.textAppName }} ist ein Angebot des hep Verlags. </template>
</p> </p>
</div> </div>
<logo class="logo" /> <logo class="logo" />
<div class="login-actions"> <div class="login-actions">
<h2 class="login-actions__title" data-cy="hello-title"> <h2
class="login-actions__title"
data-cy="hello-title"
>
Wollen Sie {{ $flavor.textAppName }} im Unterricht verwenden? Wollen Sie {{ $flavor.textAppName }} im Unterricht verwenden?
</h2> </h2>
<a class="button button--primary button--big actions__submit" href="/api/oauth/login/" data-cy="oauth-login" <a
>Mit hep Konto anmelden</a class="button button--primary button--big actions__submit"
> href="/api/oauth/login/"
data-cy="oauth-login"
>Mit hep Konto anmelden</a>
<div class="login-actions__register register"> <div class="login-actions__register register">
<p>Haben Sie noch kein hep Konto?</p> <p>Haben Sie noch kein hep Konto?</p>
<a class="hep-link" href="/api/oauth/login/" data-cy="oauth-login">Jetzt registrieren</a> <a
class="hep-link"
href="/api/oauth/login/"
data-cy="oauth-login"
>Jetzt registrieren</a>
</div> </div>
</div> </div>
<div class="information"> <div class="information">
<p>Was ist ein hep Konto und wie kann ich mich dafür registrieren?</p> <p>Was ist ein hep Konto und wie kann ich mich dafür registrieren?</p>
<a class="hep-link" href="https://myskillbox.ch/anleitung" data-cy="oauth-login">Anleitung anschauen</a> <a
class="hep-link"
href="https://myskillbox.ch/anleitung"
data-cy="oauth-login"
>Anleitung anschauen</a>
</div> </div>
<div class="links"> <div class="links">
<ul class="links__list"> <ul class="links__list">
@ -52,29 +77,31 @@
</template> </template>
<script> <script>
const HepLogoNoClaim = () => import(/* webpackChunkName: "icons" */ '@/components/icons/HepLogoNoClaim'); import {defineAsyncComponent} from 'vue';
const EhbLogo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EhbLogo'); const HepLogoNoClaim = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/HepLogoNoClaim'));
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo'); const EhbLogo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EhbLogo'));
const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
export default { export default {
components: { components: {
HepLogoNoClaim, HepLogoNoClaim,
EhbLogo, EhbLogo,
Logo, Logo
}, },
data() { data() {
return { return {
email: '', email: '',
submitted: false, submitted: false,
loading: false, loading: false
}; };
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
$hello-block-margin: 2*$medium-spacing; $hello-block-margin: 2*$medium-spacing;
@ -136,8 +163,7 @@ $hello-block-margin: 2 * $medium-spacing;
&__register { &__register {
margin-top: $large-spacing; margin-top: $large-spacing;
> p, > p, a {
a {
@include regular-text; @include regular-text;
} }
} }
@ -145,8 +171,7 @@ $hello-block-margin: 2 * $medium-spacing;
.information { .information {
margin-top: $hello-block-margin; margin-top: $hello-block-margin;
> p, > p, a {
a {
@include regular-text; @include regular-text;
} }
} }
@ -156,6 +181,7 @@ $hello-block-margin: 2 * $medium-spacing;
display: flex; display: flex;
&__list-item { &__list-item {
color: $color-silver-dark; color: $color-silver-dark;
> a { > a {
@ -179,4 +205,5 @@ $hello-block-margin: 2 * $medium-spacing;
} }
} }
} }
</style> </style>

View File

@ -1,11 +1,18 @@
<template> <template>
<div class="instrument"> <div class="instrument">
<h1 class="instrument__title" data-cy="instrument-title"> <h1
class="instrument__title"
data-cy="instrument-title"
>
{{ instrument.title }} {{ instrument.title }}
</h1> </h1>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="instrument__intro intro" data-cy="instrument-intro" v-html="instrument.intro" /> <div
class="instrument__intro intro"
data-cy="instrument-intro"
v-html="instrument.intro"
/>
<content-component <content-component
:component="component" :component="component"
@ -22,8 +29,8 @@
<script> <script>
import INSTRUMENT_QUERY from '@/graphql/gql/queries/instrumentQuery.gql'; import INSTRUMENT_QUERY from '@/graphql/gql/queries/instrumentQuery.gql';
const ContentComponent = () => import {defineAsyncComponent} from 'vue';
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/ContentComponent'); const ContentComponent = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/ContentComponent'));
export default { export default {
apollo: { apollo: {
@ -31,26 +38,26 @@ export default {
return { return {
query: INSTRUMENT_QUERY, query: INSTRUMENT_QUERY,
variables: { variables: {
slug: this.$route.params.slug, slug: this.$route.params.slug
}, }
}; };
}, }
}, },
components: { components: {
ContentComponent, ContentComponent
}, },
data() { data() {
return { return {
instrument: {}, instrument: {}
}; };
}, }
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.instrument { .instrument {
&__title { &__title {
@ -59,7 +66,7 @@ export default {
line-height: $default-heading-line-height; line-height: $default-heading-line-height;
} }
& :deep() { & /deep/ {
& p { & p {
margin-bottom: $large-spacing; margin-bottom: $large-spacing;
} }

View File

@ -2,11 +2,14 @@
<div class="license-activation public-page"> <div class="license-activation public-page">
<header class="info-header"> <header class="info-header">
<p class="info-header__text small-emph"> <p class="info-header__text small-emph">
Für <span class="info-header__emph">{{ me.email }}</span> haben wir keine gültige Lizenz gefunden Für <span class="info-header__emph">{{ me.email }}</span> haben wir keine
gültige Lizenz gefunden
</p> </p>
</header> </header>
<section class="coupon"> <section class="coupon">
<ValidationObserver v-slot="{ handleSubmit }"> <ValidationObserver
v-slot="{handleSubmit}"
>
<form <form
class="license-activation__form license-activation-form" class="license-activation__form license-activation-form"
novalidate novalidate
@ -30,7 +33,12 @@
data-cy="coupon-button" data-cy="coupon-button"
label="Coupon abschicken" label="Coupon abschicken"
/> />
<a class="button button--big" data-cy="license-activation-cancel" @click="logout">Abmelden </a> <a
class="button button--big"
data-cy="license-activation-cancel"
@click="logout"
>Abmelden
</a>
</div> </div>
</form> </form>
</ValidationObserver> </ValidationObserver>
@ -39,10 +47,16 @@
<h2>Oder, kaufen Sie eine Lizenz</h2> <h2>Oder, kaufen Sie eine Lizenz</h2>
<ul class="license-links"> <ul class="license-links">
<li class="license-links__item"> <li class="license-links__item">
<a :href="teacherEditionUrl" class="hep-link">{{ $flavor.textAppName }} für Lehrpersonen</a> <a
:href="teacherEditionUrl"
class="hep-link"
>{{ $flavor.textAppName }} für Lehrpersonen</a>
</li> </li>
<li class="license-links__item"> <li class="license-links__item">
<a :href="studentEditionUrl" class="hep-link">{{ $flavor.textAppName }} für Lernende</a> <a
:href="studentEditionUrl"
class="hep-link"
>{{ $flavor.textAppName }} für Lernende</a>
</li> </li>
</ul> </ul>
</section> </section>
@ -50,6 +64,7 @@
</template> </template>
<script> <script>
import REDEEM_COUPON from '@/graphql/gql/mutations/redeemCoupon.gql'; import REDEEM_COUPON from '@/graphql/gql/mutations/redeemCoupon.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql'; import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import LoadingButton from '@/components/LoadingButton'; import LoadingButton from '@/components/LoadingButton';
@ -58,7 +73,8 @@ import { ValidationObserver } from 'vee-validate';
import me from '@/mixins/me'; import me from '@/mixins/me';
import logout from '@/mixins/logout'; import logout from '@/mixins/logout';
const ValidatedInput = () => import('@/components/validation/ValidatedInput.vue'); import {defineAsyncComponent} from 'vue';
const ValidatedInput = defineAsyncComponent(() => import('@/components/validation/ValidatedInput'));
export default { export default {
mixins: [me, logout], mixins: [me, logout],
@ -86,33 +102,32 @@ export default {
validateBeforeSubmit() { validateBeforeSubmit() {
this.submitted = true; this.submitted = true;
this.loading = true; this.loading = true;
this.$apollo this.$apollo.mutate({
.mutate({
mutation: REDEEM_COUPON, mutation: REDEEM_COUPON,
variables: { variables: {
input: { input: {
couponCode: this.coupon, couponCode: this.coupon,
}, },
}, },
update: (store, { data: { coupon } }) => { update: (
store,
{
data: {coupon},
},
) => {
if (coupon.success) { if (coupon.success) {
this.couponErrors = []; this.couponErrors = [];
this.$apollo this.$apollo.query({
.query({
query: ME_QUERY, query: ME_QUERY,
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}) }).then(() => this.$router.push('/'));
.then(() => this.$router.push('/'));
} }
}, },
}) }).catch(({message}) => {
.catch(({ message }) => {
if (message.indexOf('invalid_coupon') > -1) { if (message.indexOf('invalid_coupon') > -1) {
this.couponErrors = ['Der angegebene Coupon-Code ist ungültig.']; this.couponErrors = ['Der angegebene Coupon-Code ist ungültig.'];
} else { } else {
this.couponErrors = [ this.couponErrors = ['Es ist ein Fehler aufgetreten. Bitte versuchen Sie es nochmals oder kontaktieren Sie den Administrator.'];
'Es ist ein Fehler aufgetreten. Bitte versuchen Sie es nochmals oder kontaktieren Sie den Administrator.',
];
} }
}) })
.finally(() => { .finally(() => {
@ -124,7 +139,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.text-link { .text-link {
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
@ -139,7 +154,7 @@ export default {
} }
.get-license { .get-license {
margin-top: $large-spacing; margin-top: $large-spacing
} }
.license-links { .license-links {
@ -147,4 +162,5 @@ export default {
margin-bottom: $medium-spacing; margin-bottom: $medium-spacing;
} }
} }
</style> </style>

View File

@ -1,6 +1,8 @@
<template> <template>
<div class="module-visibility"> <div class="module-visibility">
<h1 class="module-visibility__page-title">Sichtbarkeit</h1> <h1 class="module-visibility__page-title">
Sichtbarkeit
</h1>
<div class="module-visibility__section"> <div class="module-visibility__section">
<p class="module-visibility__paragraph"> <p class="module-visibility__paragraph">
Wollen Sie die angepasste Sichtbarkeit ( Wollen Sie die angepasste Sichtbarkeit (
@ -16,15 +18,28 @@
class="skillbox-input skillbox-dropdown module-visibility__dropdown" class="skillbox-input skillbox-dropdown module-visibility__dropdown"
@change="select($event.target.value)" @change="select($event.target.value)"
> >
<option value="" selected>-</option> <option
<option :value="schoolClass.id" v-for="schoolClass in schoolClasses" :key="schoolClass.id"> value=""
selected
>
-
</option>
<option
:value="schoolClass.id"
v-for="schoolClass in schoolClasses"
:key="schoolClass.id"
>
{{ schoolClass.name }} {{ schoolClass.name }}
</option> </option>
</select> </select>
für {{ currentClassName }} übernehmen. für {{ currentClassName }} übernehmen.
</div> </div>
<div class="module-visibility__section"> <div class="module-visibility__section">
<a class="button button--primary" data-cy="save-visibility-button" @click="sync">Anpassungen übernehmen</a> <a
class="button button--primary"
data-cy="save-visibility-button"
@click="sync"
>Anpassungen übernehmen</a>
</div> </div>
</div> </div>
</template> </template>
@ -33,12 +48,14 @@
import me from '@/mixins/me'; import me from '@/mixins/me';
import SYNC_VISIBILITY_MUTATION from '@/graphql/gql/mutations/syncModuleVisibility.gql'; import SYNC_VISIBILITY_MUTATION from '@/graphql/gql/mutations/syncModuleVisibility.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql'; import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery';
import {MODULE_PAGE} from '@/router/module.names'; import {MODULE_PAGE} from '@/router/module.names';
const EyeIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'); import {defineAsyncComponent} from 'vue';
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EyeIcon'));
export default { export default {
mixins: [me], mixins: [me],
components: { components: {
EyeIcon, EyeIcon,
@ -52,11 +69,11 @@ export default {
computed: { computed: {
schoolClasses() { schoolClasses() {
return this.me.schoolClasses.filter((schoolClass) => schoolClass.id !== this.me.selectedClass.id); return this.me.schoolClasses.filter(schoolClass => schoolClass.id !== this.me.selectedClass.id);
}, },
slug() { slug() {
return this.$route.params.slug; return this.$route.params.slug;
}, }
}, },
methods: { methods: {
@ -66,8 +83,7 @@ export default {
sync() { sync() {
if (this.selectedClassId) { if (this.selectedClassId) {
const slug = this.slug; const slug = this.slug;
this.$apollo this.$apollo.mutate({
.mutate({
mutation: SYNC_VISIBILITY_MUTATION, mutation: SYNC_VISIBILITY_MUTATION,
variables: { variables: {
input: { input: {
@ -84,13 +100,13 @@ export default {
}, },
}, },
], ],
}) },
.then(() => { ).then(() => {
this.$router.push({ this.$router.push({
name: MODULE_PAGE, name: MODULE_PAGE,
params: { params: {
slug, slug
}, }
}); });
}); });
} }

View File

@ -1,18 +1,23 @@
<template> <template>
<div> <div>
<logo class="onboarding__logo" /> <logo class="onboarding__logo" />
<h1 class="onboarding__heading">Herzlich willkommen!</h1> <h1 class="onboarding__heading">
Herzlich willkommen!
</h1>
<p class="onboarding__claim">Schauen Sie sich die Einführung an und lernen Sie {{ $flavor.textAppName }} kennen.</p> <p class="onboarding__claim">
Schauen Sie sich die Einführung an und lernen Sie {{ $flavor.textAppName }} kennen.
</p>
</div> </div>
</template> </template>
<script> <script>
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo'); import {defineAsyncComponent} from 'vue';
const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
export default { export default {
components: { components: {
Logo, Logo
}, },
}; };
</script> </script>

View File

@ -5,20 +5,25 @@
</h1> </h1>
<div id="survey" /> <div id="survey" />
<solution :value="solution" v-if="showSolution" /> <solution
:value="solution"
v-if="showSolution"
/>
<div v-if="surveyComplete"> <div v-if="surveyComplete">
<a class="button button--primary" @click="reopen">Übung bearbeiten</a> <a
class="button button--primary"
@click="reopen"
>Übung bearbeiten</a>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import '@/styles/survey.modern.css';
import '@/styles/survey.reset.css';
import {css} from '@/survey.config'; import {css} from '@/survey.config';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { Model, StylesManager } from 'survey-knockout'; import {Model} from 'survey-core';
// we are switching to the knockout version because the Vue version only works with Vue 2 (as of July 2022) // we are switching to the knockout version because the Vue version only works with Vue 2 (as of July 2022)
import 'survey-knockout-ui';
import SURVEY_QUERY from '@/graphql/gql/queries/surveyQuery.gql'; import SURVEY_QUERY from '@/graphql/gql/queries/surveyQuery.gql';
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql'; import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
@ -28,8 +33,8 @@ import { isTeacher } from '@/helpers/is-teacher';
import {meQuery} from '@/graphql/queries'; import {meQuery} from '@/graphql/queries';
const Solution = () => import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/Solution'); import {defineAsyncComponent} from 'vue';
StylesManager.applyTheme('modern'); const Solution = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Solution'));
const MODULE_QUERY = gql` const MODULE_QUERY = gql`
query ModuleSolutions($slug: String) { query ModuleSolutions($slug: String) {
@ -49,8 +54,6 @@ export default {
data() { data() {
return { return {
survey: this.initSurvey(), survey: this.initSurvey(),
currentPage: null,
surveyData: null,
title: '', title: '',
module: {}, module: {},
completed: false, completed: false,
@ -77,7 +80,7 @@ export default {
} }
if (answer.type === 'matrix' || answer.type === 'checkbox') { if (answer.type === 'matrix' || answer.type === 'checkbox') {
// wrap all the answers inside li tags and convert to a single string // wrap all the answers inside li tags and convert to a single string
const answerText = answer.answer.map((a) => `<li class="solution-text__list-item">${a}</li>`).join(''); const answerText = answer.answer.map(a => `<li class="solution-text__list-item">${a}</li>`).join('');
return ` return `
${previous} ${previous}
<h2 class="solution-text__heading">${answer.title}</h2> <h2 class="solution-text__heading">${answer.title}</h2>
@ -94,8 +97,8 @@ export default {
}; };
}, },
answers() { answers() {
return this.currentPage && this.currentPage.elements return this.survey.currentPage && this.survey.currentPage.elements
? this.currentPage.elements.reduce(extractSurveySolutions, []) ? this.survey.currentPage.elements.reduce(extractSurveySolutions, [])
: []; : [];
}, },
isTeacher() { isTeacher() {
@ -103,14 +106,6 @@ export default {
}, },
}, },
mounted() {
if (this.surveyData) {
this.loadSurveyFromServer(this.surveyData);
}
},
destroyed() {},
methods: { methods: {
initSurvey(data, answers) { initSurvey(data, answers) {
let survey = new Model(data); let survey = new Model(data);
@ -118,19 +113,15 @@ export default {
for (let k in answers) { for (let k in answers) {
flatAnswers[k] = answers[k].answer; flatAnswers[k] = answers[k].answer;
} }
this.$log.debug('flatAnswers', flatAnswers);
this.$log.debug('data', survey.data);
if (Object.keys(flatAnswers).length > 0) { if (Object.keys(flatAnswers).length > 0) {
// answers are not empty // answers are not empty
survey.data = flatAnswers; survey.data = flatAnswers;
} }
this.currentPage = survey.currentPage;
const updatePage = (sender, { oldCurrentPage, newCurrentPage }) => {
console.log(oldCurrentPage, newCurrentPage);
this.currentPage = newCurrentPage;
};
const saveSurvey = (sender, {exit}) => { const saveSurvey = (sender, {exit}) => {
this.$log.debug('saving survey', sender);
if (this.saveDisabled) { if (this.saveDisabled) {
return; return;
} }
@ -158,38 +149,28 @@ export default {
data: JSON.stringify(data), data: JSON.stringify(data),
}; };
this.$apollo this.$apollo.mutate({
.mutate({
mutation: UPDATE_ANSWER, mutation: UPDATE_ANSWER,
variables: { variables: {
input: { input: {
answer, answer,
}, },
}, },
update: ( update: (store, {data: {updateAnswer: {answer}}}) => {
store,
{
data: {
updateAnswer: { answer },
},
}
) => {
const query = SURVEY_QUERY; const query = SURVEY_QUERY;
const variables = {id: this.id}; const variables = {id: this.id};
const {survey} = store.readQuery({query, variables}); const {survey} = store.readQuery({query, variables});
if (survey) { if (survey) {
const newData = { const newData = { // data is already in use in parent scope
// data is already in use in parent scope
survey: { survey: {
...survey, ...survey,
answer, answer
}, }
}; };
store.writeQuery({query, variables, data: newData}); store.writeQuery({query, variables, data: newData});
} }
}, },
}) }).then(() => {
.then(() => {
if (exit) { if (exit) {
this.$router.go(-1); this.$router.go(-1);
} }
@ -199,16 +180,16 @@ export default {
survey.onComplete.add((sender, options) => { survey.onComplete.add((sender, options) => {
saveSurvey(sender, { saveSurvey(sender, {
...options, ...options,
exit: true, exit: true
}); });
}); });
survey.onCurrentPageChanged.add(saveSurvey); survey.onCurrentPageChanged.add(saveSurvey);
survey.onCurrentPageChanged.add(updatePage);
survey.css = css; survey.css = css;
survey.locale = 'de'; survey.locale = 'de';
survey.showProgressBar = 'bottom'; survey.showProgressBar = 'bottom';
survey.pageNextText = 'Speichern & Weiter'; survey.pageNextText = 'Speichern & Weiter';
this.$log.debug(survey.data);
survey.render('survey'); survey.render('survey');
return survey; return survey;
}, },
@ -220,20 +201,6 @@ export default {
this.survey.data = data; // reapply it this.survey.data = data; // reapply it
this.saveDisabled = false; this.saveDisabled = false;
}, },
loadSurveyFromServer(survey) {
let json = JSON.parse(survey.data);
json.showTitle = false;
json.showProgressBar = 'bottom';
let answer = {};
if (survey.answer && survey.answer.data) {
answer = JSON.parse(survey.answer.data);
}
if (!this.completed) {
this.survey = this.initSurvey(json, answer);
}
this.title = json.title;
},
}, },
apollo: { apollo: {
@ -247,14 +214,23 @@ export default {
manual: true, manual: true,
result({data, loading }) { result({data, loading }) {
if (!loading) { if (!loading) {
this.surveyData = data.survey; let json = JSON.parse(data.survey.data);
this.loadSurveyFromServer(data.survey); json.showTitle = false;
let answer = {};
if (data.survey.answer && data.survey.answer.data) {
answer = JSON.parse(data.survey.answer.data);
}
if (!this.completed) {
this.survey = this.initSurvey(json, answer);
}
this.title = json.title;
const module = data.survey.module; const module = data.survey.module;
this.$apollo.addSmartQuery('module', { this.$apollo.addSmartQuery('module', {
query: MODULE_QUERY, query: MODULE_QUERY,
variables: { variables: {
slug: module.slug, slug: module.slug
}, },
}); });
} }
@ -266,7 +242,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import "~styles/helpers";
.survey-page { .survey-page {
max-width: 800px; max-width: 800px;

View File

@ -46,15 +46,15 @@
<script> <script>
import ModuleTeaser from '@/components/modules/ModuleTeaser.vue'; import ModuleTeaser from '@/components/modules/ModuleTeaser.vue';
import {defineAsyncComponent} from 'vue';
import TOPIC_QUERY from '@/graphql/gql/queries/topicQuery.gql'; import TOPIC_QUERY from '@/graphql/gql/queries/topicQuery.gql';
import me from '@/mixins/me'; import me from '@/mixins/me';
import TopicNavigation from '@/components/book-navigation/TopicNavigation'; import TopicNavigation from '@/components/book-navigation/TopicNavigation';
import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql'; import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql'; import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
const PlayIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Play'));
const PlayIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/Play'); const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/BulbIcon'));
const BulbIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/BulbIcon');
export default { export default {