Apply code changes from migration guide for Vue 3
This commit is contained in:
parent
445f09e16a
commit
09d8d36678
|
|
@ -42,7 +42,7 @@ module.exports = {
|
|||
'@': resolve('src'),
|
||||
styles: resolve('src/styles'),
|
||||
gql: resolve('src/graphql/gql'),
|
||||
// vue: '@vue/compat',
|
||||
vue: '@vue/compat',
|
||||
},
|
||||
},
|
||||
module: {
|
||||
|
|
|
|||
|
|
@ -9,38 +9,54 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import ScrollUp from '@/components/ScrollUp';
|
||||
import ReadOnlyBanner from '@/components/ReadOnlyBanner';
|
||||
|
||||
import modals from '@/components/modals';
|
||||
|
||||
const NewContentBlockWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/NewContentBlockWizard');
|
||||
const EditContentBlockWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/EditContentBlockWizard');
|
||||
const EditRoomEntryWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/rooms/room-entries/EditRoomEntryWizard');
|
||||
const NewProjectEntryWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/NewProjectEntryWizard');
|
||||
const EditProjectEntryWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/EditProjectEntryWizard');
|
||||
const NewObjectiveWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/objective-groups/NewObjectiveWizard');
|
||||
const NewNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/NewNoteWizard');
|
||||
const EditNoteWizard = () => import(/* webpackChunkName: "content-forms" */ '@/components/notes/EditNoteWizard');
|
||||
const EditClassNameWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/school-class/EditClassNameWizard');
|
||||
const EditTeamNameWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/profile/EditTeamNameWizard');
|
||||
const EditSnapshotTitleWizard = () =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/snapshots/EditSnapshotTitleWizard');
|
||||
const DefaultLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/DefaultLayout');
|
||||
const SimpleLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/SimpleLayout');
|
||||
const FullScreenLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/FullScreenLayout');
|
||||
const PublicLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/PublicLayout');
|
||||
const BlankLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/BlankLayout');
|
||||
const SplitLayout = () => import(/* webpackChunkName: "layouts" */ '@/layouts/SplitLayout');
|
||||
const NewContentBlockWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/NewContentBlockWizard')
|
||||
);
|
||||
const EditContentBlockWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/content-block-form/EditContentBlockWizard')
|
||||
);
|
||||
const EditRoomEntryWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/rooms/room-entries/EditRoomEntryWizard')
|
||||
);
|
||||
const NewProjectEntryWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/NewProjectEntryWizard')
|
||||
);
|
||||
const EditProjectEntryWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/portfolio/EditProjectEntryWizard')
|
||||
);
|
||||
const NewObjectiveWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/objective-groups/NewObjectiveWizard')
|
||||
);
|
||||
const NewNoteWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/notes/NewNoteWizard')
|
||||
);
|
||||
const EditNoteWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/notes/EditNoteWizard')
|
||||
);
|
||||
const EditClassNameWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/school-class/EditClassNameWizard')
|
||||
);
|
||||
const EditTeamNameWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/profile/EditTeamNameWizard')
|
||||
);
|
||||
const EditSnapshotTitleWizard = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "content-forms" */ '@/components/snapshots/EditSnapshotTitleWizard')
|
||||
);
|
||||
const DefaultLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/DefaultLayout'));
|
||||
const SimpleLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/SimpleLayout'));
|
||||
const FullScreenLayout = defineAsyncComponent(() =>
|
||||
import(/* webpackChunkName: "layouts" */ '@/layouts/FullScreenLayout')
|
||||
);
|
||||
const PublicLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/PublicLayout'));
|
||||
const BlankLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/BlankLayout'));
|
||||
const SplitLayout = defineAsyncComponent(() => import(/* webpackChunkName: "layouts" */ '@/layouts/SplitLayout'));
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
<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" />
|
||||
</a>
|
||||
<content-navigation class="header-bar__content-navigation" />
|
||||
<div class="user-header">
|
||||
<a class="user-header__sidebar-link">
|
||||
<current-class class="user-header__current-class" @click.native.stop="openSidebar('profile')" />
|
||||
<a
|
||||
class="user-header__sidebar-link"
|
||||
>
|
||||
<current-class
|
||||
class="user-header__current-class"
|
||||
@click.stop="openSidebar('profile')"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<user-widget v-bind="me" data-cy="header-user-widget" @click.native.stop="openSidebar('profile')" />
|
||||
<user-widget
|
||||
v-bind="me"
|
||||
data-cy="header-user-widget"
|
||||
@click.stop="openSidebar('profile')"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<logo />
|
||||
</router-link>
|
||||
|
||||
<user-widget v-bind="me" @click.native.stop="openSidebar('profile')" />
|
||||
<user-widget v-bind="me" @click.stop="openSidebar('profile')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
unmounted() {
|
||||
document.body.onscroll = null;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
{{ name }}
|
||||
</div>
|
||||
<div class="student-submission__entry entry">
|
||||
<p>{{ submission.text | trimToLength(50) }}</p>
|
||||
<p>{{ text }}</p>
|
||||
<p class="entry__document" v-if="submission.document && submission.document.length > 0">
|
||||
<student-submission-document :document="submission.document" class="entry-document" />
|
||||
</p>
|
||||
</div>
|
||||
<div class="student-submission__feedback entry" v-if="submission.submissionFeedback">
|
||||
<p :class="{ 'entry__text--final': submission.submissionFeedback.final }" class="entry__text">
|
||||
{{ submission.submissionFeedback.text | trimToLength(50) }}
|
||||
{{ feedback }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -25,7 +25,21 @@ export default {
|
|||
components: {
|
||||
StudentSubmissionDocument,
|
||||
},
|
||||
filters: {
|
||||
|
||||
computed: {
|
||||
text() {
|
||||
return this.trimToLength(this.submission.text, 50);
|
||||
},
|
||||
feedback() {
|
||||
return this.trimToLength(this.submission.submissionFeedback.text, 50);
|
||||
},
|
||||
name() {
|
||||
return this.submission && this.submission.student
|
||||
? `${this.submission.student.firstName} ${this.submission.student.lastName}`
|
||||
: '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
trimToLength: function (text, numberOfChars) {
|
||||
if (!text) {
|
||||
return '';
|
||||
|
|
@ -40,14 +54,6 @@ export default {
|
|||
return `${text.substring(0, index)}…`;
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
name() {
|
||||
return this.submission && this.submission.student
|
||||
? `${this.submission.student.firstName} ${this.submission.student.lastName}`
|
||||
: '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
:to="topicRoute"
|
||||
active-class="content-navigation__link--active"
|
||||
class="content-navigation__link"
|
||||
@click.native="close"
|
||||
@click="close"
|
||||
>
|
||||
{{ $flavor.textTopics }}
|
||||
</router-link>
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
to="/instruments"
|
||||
active-class="content-navigation__link--active"
|
||||
class="content-navigation__link"
|
||||
@click.native="close"
|
||||
@click="close"
|
||||
>
|
||||
{{ $flavor.textInstruments }}
|
||||
</router-link>
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
class="content-navigation__link"
|
||||
data-cy="news-navigation-link"
|
||||
v-if="!me.readOnly"
|
||||
@click.native="close"
|
||||
@click="close"
|
||||
>
|
||||
News
|
||||
</router-link>
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
to="/rooms"
|
||||
active-class="content-navigation__link--active"
|
||||
class="content-navigation__link content-navigation__link--secondary"
|
||||
@click.native="close"
|
||||
@click="close"
|
||||
>
|
||||
Räume
|
||||
</router-link>
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
to="/portfolio"
|
||||
active-class="content-navigation__link--active"
|
||||
class="content-navigation__link content-navigation__link--secondary"
|
||||
@click.native="close"
|
||||
@click="close"
|
||||
>
|
||||
Portfolio
|
||||
</router-link>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
class="topic-navigation__topic book-subnavigation__item"
|
||||
v-for="topic in topics"
|
||||
:key="topic.id"
|
||||
@click.native="closeSidebar('navigation')"
|
||||
@click="closeSidebar('navigation')"
|
||||
>
|
||||
{{ topic.order }}.
|
||||
{{ topic.title }}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue, { PropType } from 'vue';
|
||||
import { PropType, defineComponent } from 'vue';
|
||||
import Toggle from '@/components/ui/Toggle.vue';
|
||||
import ContentFormSection from '@/components/content-block-form/ContentFormSection.vue';
|
||||
import InputWithLabel from '@/components/ui/InputWithLabel.vue';
|
||||
|
|
@ -130,7 +130,7 @@ interface ContentBlockFormData {
|
|||
localContentBlock: any;
|
||||
}
|
||||
|
||||
export default Vue.extend({
|
||||
export default defineComponent({
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import WidgetPopover from '@/components/ui/WidgetPopover.vue';
|
||||
import Ellipses from '@/components/icons/Ellipses.vue';
|
||||
import ButtonWithIconAndText from '@/components/ui/ButtonWithIconAndText.vue';
|
||||
|
|
@ -65,7 +65,7 @@ interface Data {
|
|||
show: boolean;
|
||||
}
|
||||
|
||||
export default Vue.extend({
|
||||
export default defineComponent({
|
||||
props: {
|
||||
actions: {
|
||||
type: Object as () => ActionOptions,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<<<<<<< HEAD
|
||||
import Vue, { PropType } from 'vue';
|
||||
import { Editor, EditorContent } from '@tiptap/vue-2';
|
||||
import Document from '@tiptap/extension-document';
|
||||
|
|
@ -15,6 +16,25 @@ import Text from '@tiptap/extension-text';
|
|||
import BulletList from '@tiptap/extension-bullet-list';
|
||||
import ListItem from '@tiptap/extension-list-item';
|
||||
import Toggle from '@/components/ui/Toggle.vue';
|
||||
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
import Vue, {PropType} from 'vue';
|
||||
import {Editor, EditorContent} from "@tiptap/vue-2";
|
||||
import Document from '@tiptap/extension-document';
|
||||
import Paragraph from '@tiptap/extension-paragraph';
|
||||
import Text from '@tiptap/extension-text';
|
||||
import BulletList from '@tiptap/extension-bullet-list';
|
||||
import ListItem from '@tiptap/extension-list-item';
|
||||
import Toggle from "@/components/ui/Toggle.vue";
|
||||
=======
|
||||
import {PropType, defineComponent} from 'vue';
|
||||
import {Editor, EditorContent} from "@tiptap/vue-2";
|
||||
import Document from '@tiptap/extension-document';
|
||||
import Paragraph from '@tiptap/extension-paragraph';
|
||||
import Text from '@tiptap/extension-text';
|
||||
import BulletList from '@tiptap/extension-bullet-list';
|
||||
import ListItem from '@tiptap/extension-list-item';
|
||||
import Toggle from "@/components/ui/Toggle.vue";
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
|
||||
interface Data {
|
||||
editor: Editor | undefined;
|
||||
|
|
@ -23,6 +43,7 @@ interface Value {
|
|||
text: string;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
|
|
@ -71,6 +92,21 @@ export default Vue.extend({
|
|||
editorProps: {
|
||||
attributes: {
|
||||
class: 'tip-tap__editor',
|
||||
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
type: Object as PropType<Value>,
|
||||
validator(value: Value) {
|
||||
return Object.prototype.hasOwnProperty.call(value, 'text');
|
||||
=======
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: Object as PropType<Value>,
|
||||
validator: (value: Value) => {
|
||||
return Object.prototype.hasOwnProperty.call(value, 'text');
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
},
|
||||
},
|
||||
content: this.text,
|
||||
|
|
@ -80,6 +116,7 @@ export default Vue.extend({
|
|||
this.$emit('input', text);
|
||||
this.$emit('change-text', text);
|
||||
},
|
||||
<<<<<<< HEAD
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -91,6 +128,15 @@ export default Vue.extend({
|
|||
toggleList() {
|
||||
const editor = this.editor as Editor;
|
||||
editor.chain().selectAll().toggleBulletList().run();
|
||||
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
text(): string {
|
||||
return this.value.text;
|
||||
}
|
||||
=======
|
||||
text(): string {
|
||||
return this.value?.text || '';
|
||||
}
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
<filter-entry
|
||||
:text="title"
|
||||
v-bind="$attrs"
|
||||
:type="category"
|
||||
:category="category"
|
||||
:is-category="true"
|
||||
:id="category.id"
|
||||
@click.native="setCategoryFilter(category.id)"
|
||||
@click="setCategoryFilter(category.id)"
|
||||
/>
|
||||
<div class="filter-group__children">
|
||||
<filter-entry
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
v-for="type in types"
|
||||
:id="type.id"
|
||||
:key="type.id"
|
||||
@click.native="setFilter(`type:${type.id}`)"
|
||||
@click="setFilter(`type:${type.id}`)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
<div class="page-form-input">
|
||||
<label :for="id" class="page-form-input__label">{{ label }}</label>
|
||||
<component
|
||||
:value="value"
|
||||
:class="classes"
|
||||
:value.prop="value"
|
||||
:data-cy="cyId"
|
||||
:is="type"
|
||||
:id="id"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
icon="document-with-lines-icon"
|
||||
data-cy="use-template-button"
|
||||
text="Vorlage nutzen"
|
||||
@click.native="useTemplate"
|
||||
@click="useTemplate"
|
||||
/>
|
||||
|
||||
<file-upload
|
||||
|
|
@ -30,12 +30,38 @@
|
|||
</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>
|
||||
<a
|
||||
class="button button--primary"
|
||||
data-cy="modal-save-button"
|
||||
@click="$emit('save', localProjectEntry)"
|
||||
>Speichern</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="$emit('hide')"
|
||||
>Abbrechen</a>
|
||||
</template>
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
|
|
@ -43,8 +69,17 @@
|
|||
import Modal from '@/components/Modal';
|
||||
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';
|
||||
|
||||
const FileUpload = () => import('@/components/ui/file-upload/FileUpload');
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,40 @@
|
|||
<template>
|
||||
<<<<<<< HEAD
|
||||
<page-form :title="title" @save="$emit('save', localProject)">
|
||||
<page-form-input label="Titel" v-model="localProject.title" />
|
||||
<page-form-input label="Beschreibung" type="textarea" v-model="localProject.description" />
|
||||
<template slot="footer">
|
||||
||||||| parent of a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
<page-form
|
||||
:title="title"
|
||||
@save="$emit('save', localProject)"
|
||||
>
|
||||
<page-form-input
|
||||
label="Titel"
|
||||
v-model="localProject.title"
|
||||
/>
|
||||
<page-form-input
|
||||
label="Beschreibung"
|
||||
type="textarea"
|
||||
v-model="localProject.description"
|
||||
/>
|
||||
<template slot="footer">
|
||||
=======
|
||||
<page-form
|
||||
:title="title"
|
||||
@save="$emit('save', localProject)"
|
||||
>
|
||||
<page-form-input
|
||||
label="Titel"
|
||||
v-model="localProject.title"
|
||||
/>
|
||||
<page-form-input
|
||||
label="Beschreibung"
|
||||
type="textarea"
|
||||
v-model="localProject.description"
|
||||
/>
|
||||
<template #footer>
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
<button
|
||||
:class="{ 'button--disabled': !formValid }"
|
||||
:disabled="!formValid"
|
||||
|
|
|
|||
|
|
@ -6,10 +6,34 @@
|
|||
|
||||
<modal-input :value="name" :placeholder="placeholder" data-cy="edit-name-input" @input="$emit('input', $event)" />
|
||||
<template #footer>
|
||||
<<<<<<< HEAD
|
||||
<div slot="footer">
|
||||
<a class="button button--primary" data-cy="modal-save-button" @click="$emit('save')">Speichern</a>
|
||||
<a class="button" @click="$emit('cancel')">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')"
|
||||
>Speichern</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="$emit('cancel')"
|
||||
>Abbrechen</a>
|
||||
</div>
|
||||
=======
|
||||
<a
|
||||
class="button button--primary"
|
||||
data-cy="modal-save-button"
|
||||
@click="$emit('save')"
|
||||
>Speichern</a>
|
||||
<a
|
||||
class="button"
|
||||
@click="$emit('cancel')"
|
||||
>Abbrechen</a>
|
||||
>>>>>>> a423cfde (Apply code changes from migration guide for Vue 3)
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,18 @@
|
|||
<template>
|
||||
<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
|
||||
:is="button"
|
||||
@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)" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
icon="document-icon"
|
||||
text="Dokument hochladen"
|
||||
v-if="!value"
|
||||
@click.native="clickUploadCare"
|
||||
@click="clickUploadCare"
|
||||
/>
|
||||
|
||||
<simple-file-upload-hidden-input @link-change-url="$emit('link-change-url', $event)" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const resizeElement = (el) => {
|
||||
const resizeElement = (el: HTMLElement) => {
|
||||
el.style.height = `auto`;
|
||||
el.style.height = `${el.clientHeight - el.offsetHeight + el.scrollHeight}px`;
|
||||
};
|
||||
|
|
@ -6,13 +6,13 @@ const resizeElement = (el) => {
|
|||
export default {
|
||||
update: resizeElement,
|
||||
inserted: resizeElement,
|
||||
bind(el) {
|
||||
created(el: HTMLElement) {
|
||||
el.classList.add('skillbox-auto-grow');
|
||||
el.addEventListener('input', () => {
|
||||
resizeElement(el);
|
||||
});
|
||||
},
|
||||
unbind(el) {
|
||||
unmounted(el: HTMLElement) {
|
||||
el.classList.remove('skillbox-auto-grow');
|
||||
el.removeEventListener('input', () => {
|
||||
resizeElement(el);
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// taken from https://stackoverflow.com/questions/36170425/detect-click-outside-element
|
||||
export default {
|
||||
bind(el, binding, vnode) {
|
||||
el.clickOutsideEvent = (event) => {
|
||||
if (!(el === event.target || el.contains(event.target))) {
|
||||
vnode.context[binding.expression](event);
|
||||
}
|
||||
};
|
||||
document.body.addEventListener('click', el.clickOutsideEvent);
|
||||
},
|
||||
unbind(el) {
|
||||
document.body.removeEventListener('click', el.clickOutsideEvent);
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// taken from https://stackoverflow.com/questions/36170425/detect-click-outside-element
|
||||
import {DirectiveBinding, VNode} from "vue";
|
||||
|
||||
declare global {
|
||||
interface HTMLElement {
|
||||
clickOutsideEvent: (event: Event) => void
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
unmounted(el: HTMLElement) {
|
||||
document.body.removeEventListener('click', el.clickOutsideEvent);
|
||||
},
|
||||
created: (el: HTMLElement, binding: DirectiveBinding) => {
|
||||
el.clickOutsideEvent = (event: Event) => {
|
||||
if (!(el === event.target || el.contains(event.target as Node))) {
|
||||
const eventHandler = binding.value;
|
||||
eventHandler(event);
|
||||
}
|
||||
};
|
||||
document.body.addEventListener('click', el.clickOutsideEvent);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,131 +1,160 @@
|
|||
<template>
|
||||
<footer class="default-footer" data-cy="page-footer">
|
||||
<footer
|
||||
class="default-footer"
|
||||
data-cy="page-footer"
|
||||
>
|
||||
<div class="default-footer__section">
|
||||
<div class="default-footer__info">
|
||||
<div class="default-footer__who-are-we who-are-we">
|
||||
<h5 class="who-are-we__title">Wer sind wir?</h5>
|
||||
<h5 class="who-are-we__title">
|
||||
Wer sind wir?
|
||||
</h5>
|
||||
<p class="who-are-we__text">
|
||||
mySkillbox ist ein Angebot des hep Verlags in Zusammenarbeit mit der Eidgenössischen Hochschule für
|
||||
Berufsbildung (EHB).
|
||||
mySkillbox ist ein Angebot des hep Verlags in
|
||||
Zusammenarbeit mit der Eidgenössischen Hochschule für Berufsbildung (EHB).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="https://www.hep-verlag.ch/" target="_blank">
|
||||
<a
|
||||
href="https://www.hep-verlag.ch/"
|
||||
target="_blank"
|
||||
>
|
||||
<hep-logo class="default-footer__logo-hep" />
|
||||
</a>
|
||||
<a href="https://www.ehb.swiss/" target="_blank">
|
||||
<a
|
||||
href="https://www.ehb.swiss/"
|
||||
target="_blank"
|
||||
>
|
||||
<ehb-logo class="default-footer__logo-ehb" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="default-footer__section">
|
||||
<div class="default-footer__links">
|
||||
<a href="https://myskillbox.ch/datenschutz" target="_blank" class="default-footer__link">Datenschutz</a>
|
||||
<a href="https://myskillbox.ch/impressum" target="_blank" class="default-footer__link">Impressum</a>
|
||||
<a href="https://myskillbox.ch/agb" target="_blank" class="default-footer__link">AGB</a>
|
||||
<a :href="$flavor.supportLink" target="_blank" class="default-footer__link">Support</a>
|
||||
<a
|
||||
href="https://myskillbox.ch/datenschutz"
|
||||
target="_blank"
|
||||
class="default-footer__link"
|
||||
>Datenschutz</a>
|
||||
<a
|
||||
href="https://myskillbox.ch/impressum"
|
||||
target="_blank"
|
||||
class="default-footer__link"
|
||||
>Impressum</a>
|
||||
<a
|
||||
href="https://myskillbox.ch/agb"
|
||||
target="_blank"
|
||||
class="default-footer__link"
|
||||
>AGB</a>
|
||||
<a
|
||||
:href="$flavor.supportLink"
|
||||
target="_blank"
|
||||
class="default-footer__link"
|
||||
>Support</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const HepLogo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/HepLogo');
|
||||
const EhbLogo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/EhbLogo');
|
||||
import {defineAsyncComponent} from 'vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HepLogo,
|
||||
EhbLogo,
|
||||
},
|
||||
};
|
||||
const HepLogo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/HepLogo'));
|
||||
const EhbLogo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/EhbLogo'));
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HepLogo,
|
||||
EhbLogo
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/_variables.scss';
|
||||
@import '@/styles/_mixins.scss';
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
|
||||
.default-footer {
|
||||
background-color: $color-silver-light;
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
.default-footer {
|
||||
background-color: $color-silver-light;
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
|
||||
&__section {
|
||||
width: 100%;
|
||||
border-bottom: $color-silver 1px solid;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
&__section {
|
||||
width: 100%;
|
||||
border-bottom: $color-silver 1px solid;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__info {
|
||||
width: 100%;
|
||||
max-width: $footer-width;
|
||||
padding: 2 * $large-spacing 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&__info {
|
||||
width: 100%;
|
||||
max-width: $footer-width;
|
||||
padding: 2*$large-spacing 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@include desktop {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
@include desktop {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
&__who-are-we {
|
||||
width: 100%;
|
||||
margin-bottom: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
width: 330px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-hep {
|
||||
width: auto;
|
||||
height: 35px;
|
||||
margin-bottom: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
width: 147px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-ehb {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
&__links {
|
||||
width: 100%;
|
||||
max-width: $footer-width;
|
||||
padding: $large-spacing 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@include desktop {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
@include aside-with-cheese;
|
||||
margin-right: $large-spacing;
|
||||
margin-bottom: $small-spacing;
|
||||
|
||||
@include desktop {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__who-are-we {
|
||||
width: 100%;
|
||||
margin-bottom: $large-spacing;
|
||||
.who-are-we {
|
||||
&__title {
|
||||
@include heading-4;
|
||||
}
|
||||
|
||||
@include desktop {
|
||||
width: 330px;
|
||||
margin-bottom: 0;
|
||||
&__text {
|
||||
@include aside-text;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-hep {
|
||||
width: auto;
|
||||
height: 35px;
|
||||
margin-bottom: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
width: 147px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-ehb {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
&__links {
|
||||
width: 100%;
|
||||
max-width: $footer-width;
|
||||
padding: $large-spacing 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@include desktop {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
@include aside-with-cheese;
|
||||
margin-right: $large-spacing;
|
||||
margin-bottom: $small-spacing;
|
||||
|
||||
@include desktop {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.who-are-we {
|
||||
&__title {
|
||||
@include heading-4;
|
||||
}
|
||||
|
||||
&__text {
|
||||
@include aside-text;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,73 +1,80 @@
|
|||
<template>
|
||||
<div class="layout layout--public public">
|
||||
<div class="public__logo">
|
||||
<router-link :to="{ name: 'hello' }" class="hep-link">
|
||||
<router-link
|
||||
:to="{name: 'hello'}"
|
||||
class="hep-link"
|
||||
>
|
||||
<logo />
|
||||
</router-link>
|
||||
</div>
|
||||
<router-view class="public__content layout__content" />
|
||||
<default-footer class="skillbox__footer public__footer footer" v-if="$flavor.showFooter" />
|
||||
<default-footer
|
||||
class="skillbox__footer public__footer footer"
|
||||
v-if="$flavor.showFooter"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultFooter from '@/layouts/DefaultFooter';
|
||||
import {defineAsyncComponent} from 'vue';
|
||||
import DefaultFooter from '@/layouts/DefaultFooter';
|
||||
const Logo = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/Logo'));
|
||||
|
||||
const Logo = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Logo');
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Logo,
|
||||
DefaultFooter,
|
||||
},
|
||||
};
|
||||
export default {
|
||||
components: {
|
||||
Logo,
|
||||
DefaultFooter
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/_variables.scss';
|
||||
@import '@/styles/_mixins.scss';
|
||||
@import '@/styles/_default-layout.scss';
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
@import "@/styles/_default-layout.scss";
|
||||
|
||||
@mixin content-block {
|
||||
padding-right: $medium-spacing;
|
||||
padding-left: $medium-spacing;
|
||||
max-width: 800px;
|
||||
min-width: 320px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@mixin content-block {
|
||||
padding-right: $medium-spacing;
|
||||
padding-left: $medium-spacing;
|
||||
max-width: 800px;
|
||||
min-width: 320px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: relative;
|
||||
.logo {
|
||||
position: relative;
|
||||
|
||||
width: auto;
|
||||
height: 43px;
|
||||
}
|
||||
|
||||
.public {
|
||||
grid-template-areas: 'h' 'c' 'f';
|
||||
|
||||
&__content {
|
||||
@include content-block();
|
||||
margin-bottom: $large-spacing;
|
||||
width: auto;
|
||||
height: 43px;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
@include content-block();
|
||||
margin-top: $medium-spacing;
|
||||
.public {
|
||||
grid-template-areas: "h" "c" "f";
|
||||
|
||||
&__content {
|
||||
@include content-block();
|
||||
margin-bottom: $large-spacing;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
@include content-block();
|
||||
margin-top: $medium-spacing
|
||||
}
|
||||
|
||||
&__footer {
|
||||
background-color: $color-silver-light;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
background-color: $color-silver-light;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
padding: $large-spacing $medium-spacing 0;
|
||||
|
||||
.footer {
|
||||
padding: $large-spacing $medium-spacing 0;
|
||||
|
||||
&__content {
|
||||
@include content-block();
|
||||
&__content {
|
||||
@include content-block();
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import '@babel/polyfill';
|
||||
import Vue from 'vue';
|
||||
import {createApp, inject} from 'vue';
|
||||
import VueVimeoPlayer from 'vue-vimeo-player';
|
||||
import apolloClientFactory from './graphql/client';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
|
@ -13,42 +13,9 @@ import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
|||
import VueModal from '@/plugins/modal';
|
||||
import VueRemoveEdges from '@/plugins/edges';
|
||||
import VueMatomo from 'vue-matomo';
|
||||
import VueLogger from 'vuejs3-logger';
|
||||
import { joiningClass, loginRequired, unauthorizedAccess } from '@/router/guards';
|
||||
import flavorPlugin from '@/plugins/flavor';
|
||||
import log from 'loglevel';
|
||||
|
||||
window.log = log; // make log available in app when built, to change log level: log.setLevel('debug')
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
const logLevel = isProduction ? 'error' : 'warn';
|
||||
|
||||
log.setDefaultLevel(logLevel);
|
||||
|
||||
Vue.use(VueModal);
|
||||
Vue.use(VueRemoveEdges);
|
||||
Vue.use(VueApollo);
|
||||
Vue.use(VueVimeoPlayer);
|
||||
|
||||
Vue.use(VueScrollTo, {
|
||||
duration: 500,
|
||||
easing: 'ease-out',
|
||||
offset: -50,
|
||||
});
|
||||
|
||||
Vue.use(flavorPlugin);
|
||||
|
||||
if (process.env.MATOMO_HOST) {
|
||||
Vue.use(VueMatomo, {
|
||||
host: process.env.MATOMO_HOST,
|
||||
siteId: process.env.MATOMO_SITE_ID,
|
||||
router: router,
|
||||
});
|
||||
}
|
||||
|
||||
Vue.directive('click-outside', clickOutside);
|
||||
Vue.directive('auto-grow', autoGrow);
|
||||
|
||||
const publicApolloClient = apolloClientFactory('/api/graphql-public/', null);
|
||||
const privateApolloClient = apolloClientFactory('/api/graphql/', networkErrorCallback);
|
||||
|
|
@ -60,56 +27,96 @@ const apolloProvider = new VueApollo({
|
|||
defaultClient: privateApolloClient,
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
store,
|
||||
router,
|
||||
apolloProvider,
|
||||
render: h => h(App),
|
||||
});
|
||||
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
app.use(VueModal);
|
||||
app.use(VueRemoveEdges);
|
||||
app.use(VueApollo);
|
||||
app.use(VueVimeoPlayer);
|
||||
app.use(VueLogger, {
|
||||
isEnabled: true,
|
||||
logLevel: isProduction ? 'error' : 'debug',
|
||||
stringifyArguments: false,
|
||||
showConsoleColors: true,
|
||||
});
|
||||
|
||||
// VueScrollTo.setDefaults({
|
||||
// duration: 500,
|
||||
// easing: 'ease-out',
|
||||
// offset: -50,
|
||||
// });
|
||||
|
||||
app.directive('scroll-to', VueScrollTo);
|
||||
|
||||
|
||||
app.use(flavorPlugin);
|
||||
|
||||
if (process.env.MATOMO_HOST) {
|
||||
app.use(VueMatomo, {
|
||||
host: process.env.MATOMO_HOST,
|
||||
siteId: process.env.MATOMO_SITE_ID,
|
||||
router: router,
|
||||
});
|
||||
}
|
||||
|
||||
app.directive('click-outside', clickOutside);
|
||||
app.directive('auto-grow', autoGrow);
|
||||
|
||||
/* guards */
|
||||
|
||||
|
||||
|
||||
function redirectUsersWithoutValidLicense() {
|
||||
return privateApolloClient
|
||||
.query({
|
||||
query: ME_QUERY,
|
||||
})
|
||||
.then(({ data }) => data.me.expiryDate == null);
|
||||
return privateApolloClient.query({
|
||||
query: ME_QUERY,
|
||||
}).then(({data}) => data.me.expiryDate == null);
|
||||
}
|
||||
|
||||
function redirectStudentsWithoutClass() {
|
||||
return privateApolloClient
|
||||
.query({
|
||||
query: ME_QUERY,
|
||||
})
|
||||
.then(({ data }) => data.me.schoolClasses.length === 0 && !data.me.isTeacher);
|
||||
return privateApolloClient.query({
|
||||
query: ME_QUERY,
|
||||
}).then(({data}) => data.me.schoolClasses.length === 0 && !data.me.isTeacher);
|
||||
}
|
||||
|
||||
function redirectUsersToOnboarding() {
|
||||
return privateApolloClient
|
||||
.query({
|
||||
query: ME_QUERY,
|
||||
})
|
||||
.then(({ data }) => !data.me.onboardingVisited);
|
||||
return privateApolloClient.query({
|
||||
query: ME_QUERY,
|
||||
}).then(({data}) => !data.me.onboardingVisited);
|
||||
}
|
||||
|
||||
function networkErrorCallback(statusCode) {
|
||||
if (statusCode === 402) {
|
||||
log.debug('status code 402, redirecting');
|
||||
router.push({ name: 'licenseActivation' });
|
||||
router.push({name: 'licenseActivation'});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
log.debug('navigation guard called', to, from);
|
||||
// todo: make logger work outside vue app
|
||||
// const logger = inject('vuejs3-logger');
|
||||
// logger.$log.debug('navigation guard called', to, from);
|
||||
if (to.path === '/logout') {
|
||||
log.debug('logout', to);
|
||||
publicApolloClient.resetStore();
|
||||
await publicApolloClient.resetStore();
|
||||
if (process.env.LOGOUT_REDIRECT_URL) {
|
||||
location.replace(`https://sso.hep-verlag.ch/logout?return_to=${process.env.LOGOUT_REDIRECT_URL}`);
|
||||
next(false);
|
||||
return;
|
||||
} else {
|
||||
next({ name: 'hello' });
|
||||
next({name: 'hello'});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (unauthorizedAccess(to)) {
|
||||
log.debug('unauthorized', to);
|
||||
//logger.$log.debug('unauthorized', to);
|
||||
const postLoginRedirectionUrl = to.path;
|
||||
const redirectUrl = `/hello/`;
|
||||
|
||||
|
|
@ -117,46 +124,33 @@ router.beforeEach(async (to, from, next) => {
|
|||
localStorage.setItem(postLoginRedirectUrlKey, postLoginRedirectionUrl);
|
||||
}
|
||||
|
||||
log.debug('redirecting to hello', to);
|
||||
// logger.$log.debug('redirecting to hello', to);
|
||||
next(redirectUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (to.name && to.name !== 'licenseActivation' && loginRequired(to) && (await redirectUsersWithoutValidLicense())) {
|
||||
log.debug('redirecting to licenseActivation', to, null);
|
||||
if (to.name && to.name !== 'licenseActivation' && loginRequired(to) && await redirectUsersWithoutValidLicense()) {
|
||||
// logger.$log.debug('redirecting to licenseActivation', to, null);
|
||||
console.log('redirecting to licenseActivation', to, null);
|
||||
next({ name: 'licenseActivation' });
|
||||
next({name: 'licenseActivation'});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!joiningClass(to) && loginRequired(to) && (await redirectStudentsWithoutClass())) {
|
||||
log.debug('redirecting to join-class', to);
|
||||
log.debug('await redirectStudentsWithoutClass()', await redirectStudentsWithoutClass());
|
||||
next({ name: 'join-class' });
|
||||
if (!joiningClass(to) && loginRequired(to) && await redirectStudentsWithoutClass()) {
|
||||
//logger.$log.debug('redirecting to join-class', to);
|
||||
//logger.$log.debug('await redirectStudentsWithoutClass()', await redirectStudentsWithoutClass());
|
||||
next({name: 'join-class'});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
to.name &&
|
||||
to.name.indexOf('onboarding') === -1 &&
|
||||
!joiningClass(to) &&
|
||||
loginRequired(to) &&
|
||||
(await redirectUsersToOnboarding())
|
||||
) {
|
||||
log.debug('redirecting to onboarding-start', to);
|
||||
next({ name: 'onboarding-start' });
|
||||
if ((to.name && to.name.indexOf('onboarding') === -1) && !joiningClass(to) && loginRequired(to) && await redirectUsersToOnboarding()) {
|
||||
//logger.$log.debug('redirecting to onboarding-start', to);
|
||||
next({name: 'onboarding-start'});
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug('End of Guard reached', to);
|
||||
//logger.$log.debug('End of Guard reached', to);
|
||||
next();
|
||||
});
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
router,
|
||||
apolloProvider,
|
||||
render: (h) => h(App),
|
||||
});
|
||||
app.mount('#app');
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,82 +1,85 @@
|
|||
<template>
|
||||
<content-block-form title="Inhaltsblock erfassen" :content-block="contentBlock" @back="goToModule" @save="save" />
|
||||
<content-block-form
|
||||
title="Inhaltsblock erfassen"
|
||||
:content-block="contentBlock"
|
||||
@back="goToModule"
|
||||
@save="save"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import {defineComponent} from 'vue';
|
||||
|
||||
import ContentBlockForm from '@/components/content-block-form/ContentBlockForm';
|
||||
import { setUserBlockType } from '@/helpers/content-block';
|
||||
import NEW_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/addContentBlock.gql';
|
||||
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
|
||||
import { cleanUpContents } from '@/components/content-block-form/helpers';
|
||||
import ContentBlockForm from '@/components/content-block-form/ContentBlockForm';
|
||||
import {setUserBlockType} from '@/helpers/content-block';
|
||||
import NEW_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/addContentBlock.gql';
|
||||
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
|
||||
import {cleanUpContents} from '@/components/content-block-form/helpers';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
parent: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
after: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
ContentBlockForm,
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
contentBlock: {
|
||||
title: '',
|
||||
isAssignment: false,
|
||||
contents: [],
|
||||
},
|
||||
}),
|
||||
|
||||
methods: {
|
||||
save({ title, contents, isAssignment }) {
|
||||
let cleanedContents = cleanUpContents(contents);
|
||||
const contentBlock = {
|
||||
title: title,
|
||||
contents: cleanedContents,
|
||||
type: setUserBlockType(isAssignment),
|
||||
};
|
||||
let input;
|
||||
const { parent, after, slug } = this.$route.params;
|
||||
if (after) {
|
||||
input = {
|
||||
contentBlock,
|
||||
after,
|
||||
};
|
||||
} else {
|
||||
input = {
|
||||
contentBlock,
|
||||
parent,
|
||||
};
|
||||
export default defineComponent({
|
||||
props: {
|
||||
parent: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
after: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
this.$apollo
|
||||
.mutate({
|
||||
},
|
||||
|
||||
components: {
|
||||
ContentBlockForm,
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
contentBlock: {
|
||||
title: '',
|
||||
isAssignment: false,
|
||||
contents: [
|
||||
]},
|
||||
}),
|
||||
|
||||
methods: {
|
||||
save({title, contents, isAssignment}) {
|
||||
let cleanedContents = cleanUpContents(contents);
|
||||
const contentBlock = {
|
||||
title: title,
|
||||
contents: cleanedContents,
|
||||
type: setUserBlockType(isAssignment),
|
||||
};
|
||||
let input;
|
||||
const { parent, after, slug } = this.$route.params;
|
||||
if(after) {
|
||||
input = {
|
||||
contentBlock,
|
||||
after
|
||||
};
|
||||
} else {
|
||||
input = {
|
||||
contentBlock,
|
||||
parent
|
||||
};
|
||||
}
|
||||
this.$apollo.mutate({
|
||||
mutation: NEW_CONTENT_BLOCK_MUTATION,
|
||||
variables: {
|
||||
input,
|
||||
input
|
||||
},
|
||||
refetchQueries: [
|
||||
{
|
||||
query: MODULE_DETAILS_QUERY,
|
||||
variables: {
|
||||
slug,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.then(this.goToModule);
|
||||
},
|
||||
goToModule() {
|
||||
// use the history, so the scroll position is preserved
|
||||
this.$router.go(-1);
|
||||
},
|
||||
},
|
||||
});
|
||||
refetchQueries: [{
|
||||
query: MODULE_DETAILS_QUERY,
|
||||
variables: {
|
||||
slug
|
||||
}
|
||||
}]
|
||||
}).then(this.goToModule);
|
||||
},
|
||||
goToModule() {
|
||||
// use the history, so the scroll position is preserved
|
||||
this.$router.go(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
:final="project.final"
|
||||
data-cy="project-share-link"
|
||||
class="project__share"
|
||||
@click.native="updateProjectShareState(project.slug, !project.final)"
|
||||
@click="updateProjectShareState(project.slug, !project.final)"
|
||||
/>
|
||||
|
||||
<project-actions :share-buttons="false" class="project__more" :slug="project.slug" v-if="canEdit" />
|
||||
|
|
|
|||
|
|
@ -5,14 +5,21 @@
|
|||
</div>
|
||||
|
||||
<div class="topic__content">
|
||||
<h1 data-cy="topic-title" class="topic__title">
|
||||
<h1
|
||||
data-cy="topic-title"
|
||||
class="topic__title"
|
||||
>
|
||||
{{ topic.title }}
|
||||
</h1>
|
||||
<p class="topic__teaser">
|
||||
{{ topic.teaser }}
|
||||
</p>
|
||||
<div class="topic__links">
|
||||
<div class="topic__video-link topic__link" v-if="topic.vimeoId" @click="openVideo">
|
||||
<div
|
||||
class="topic__video-link topic__link"
|
||||
v-if="topic.vimeoId"
|
||||
@click="openVideo"
|
||||
>
|
||||
<play-icon class="topic__video-link-icon topic__link-icon" />
|
||||
<span class="topic__link-description">Video schauen</span>
|
||||
</div>
|
||||
|
|
@ -27,187 +34,184 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="topic__modules">
|
||||
<module-teaser v-for="module in modules" :key="module.slug" v-bind="module" />
|
||||
<module-teaser
|
||||
v-for="module in modules"
|
||||
v-bind="module"
|
||||
:key="module.slug"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModuleTeaser from '@/components/modules/ModuleTeaser.vue';
|
||||
import TOPIC_QUERY from '@/graphql/gql/queries/topicQuery.gql';
|
||||
import me from '@/mixins/me';
|
||||
import TopicNavigation from '@/components/book-navigation/TopicNavigation';
|
||||
import ModuleTeaser from '@/components/modules/ModuleTeaser.vue';
|
||||
import TOPIC_QUERY from '@/graphql/gql/queries/topicQuery.gql';
|
||||
import me from '@/mixins/me';
|
||||
import TopicNavigation from '@/components/book-navigation/TopicNavigation';
|
||||
|
||||
import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql';
|
||||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
||||
import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql';
|
||||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
||||
|
||||
const PlayIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/Play');
|
||||
const BulbIcon = () => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon');
|
||||
const PlayIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/Play');
|
||||
const BulbIcon = () => import(/* webpackChunkName: "icons" */'@/components/icons/BulbIcon');
|
||||
|
||||
export default {
|
||||
mixins: [me],
|
||||
components: {
|
||||
TopicNavigation,
|
||||
ModuleTeaser,
|
||||
PlayIcon,
|
||||
BulbIcon,
|
||||
},
|
||||
export default {
|
||||
|
||||
apollo: {
|
||||
topic() {
|
||||
return {
|
||||
query: TOPIC_QUERY,
|
||||
variables: {
|
||||
slug: this.$route.params.topicSlug,
|
||||
},
|
||||
update(data) {
|
||||
return this.$getRidOfEdges(data).topic || {};
|
||||
},
|
||||
result() {
|
||||
if (this.saveMe) {
|
||||
this.saveMe = false;
|
||||
this.updateLastVisitedTopic(this.topic.id);
|
||||
}
|
||||
},
|
||||
};
|
||||
mixins: [me],
|
||||
components: {
|
||||
TopicNavigation,
|
||||
ModuleTeaser,
|
||||
PlayIcon,
|
||||
BulbIcon,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
topic: {
|
||||
modules: {
|
||||
edges: [],
|
||||
},
|
||||
apollo: {
|
||||
topic() {
|
||||
return {
|
||||
query: TOPIC_QUERY,
|
||||
variables: {
|
||||
slug: this.$route.params.topicSlug,
|
||||
},
|
||||
update(data) {
|
||||
return this.$getRidOfEdges(data).topic || {};
|
||||
},
|
||||
result() {
|
||||
if (this.saveMe) {
|
||||
this.saveMe = false;
|
||||
this.updateLastVisitedTopic(this.topic.id);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
saveMe: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
modules() {
|
||||
return this.topic.modules;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (!this.topic.id) {
|
||||
// component was loaded before topic, apollo not ready yet
|
||||
this.saveMe = true; // needs saving, apollo will do this
|
||||
} else {
|
||||
this.updateLastVisitedTopic(this.topic.id);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openVideo() {
|
||||
this.$store.dispatch('showFullscreenVideo', this.topic.vimeoId);
|
||||
},
|
||||
updateLastVisitedTopic(topicId) {
|
||||
if (!topicId) {
|
||||
return;
|
||||
}
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_LAST_TOPIC_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
id: topicId,
|
||||
data() {
|
||||
return {
|
||||
topic: {
|
||||
modules: {
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
update(
|
||||
store,
|
||||
{
|
||||
data: {
|
||||
updateLastTopic: { topic },
|
||||
},
|
||||
}
|
||||
) {
|
||||
if (topic) {
|
||||
const query = ME_QUERY;
|
||||
const { me } = store.readQuery({ query });
|
||||
if (me) {
|
||||
const data = {
|
||||
me: {
|
||||
...me,
|
||||
lastTopic: topic,
|
||||
},
|
||||
};
|
||||
store.writeQuery({ query, data });
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
saveMe: false,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
computed: {
|
||||
modules() {
|
||||
return this.topic.modules;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (!this.topic.id) { // component was loaded before topic, apollo not ready yet
|
||||
this.saveMe = true; // needs saving, apollo will do this
|
||||
} else {
|
||||
this.updateLastVisitedTopic(this.topic.id);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openVideo() {
|
||||
this.$store.dispatch('showFullscreenVideo', this.topic.vimeoId);
|
||||
},
|
||||
updateLastVisitedTopic(topicId) {
|
||||
if (!topicId) {
|
||||
return;
|
||||
}
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_LAST_TOPIC_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
id: topicId,
|
||||
},
|
||||
},
|
||||
update(store, {data: {updateLastTopic: {topic}}}) {
|
||||
if (topic) {
|
||||
const query = ME_QUERY;
|
||||
const {me} = store.readQuery({query});
|
||||
if (me) {
|
||||
const data = {
|
||||
me: {
|
||||
...me,
|
||||
lastTopic: topic,
|
||||
},
|
||||
};
|
||||
store.writeQuery({query, data});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/_variables.scss';
|
||||
@import '@/styles/_mixins.scss';
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
|
||||
.topic {
|
||||
display: grid;
|
||||
padding: $large-spacing 0;
|
||||
grid-template-columns: 1fr;
|
||||
@include desktop {
|
||||
grid-template-columns: 300px 1fr;
|
||||
}
|
||||
|
||||
&__navigation {
|
||||
padding: 0 $medium-spacing;
|
||||
display: none;
|
||||
.topic {
|
||||
display: grid;
|
||||
padding: $large-spacing 0;
|
||||
grid-template-columns: 1fr;
|
||||
@include desktop {
|
||||
display: block;
|
||||
grid-template-columns: 300px 1fr;
|
||||
}
|
||||
|
||||
&__navigation {
|
||||
padding: 0 $medium-spacing;
|
||||
display: none;
|
||||
@include desktop {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&__teaser {
|
||||
color: $color-charcoal-dark;
|
||||
width: 90%;
|
||||
@include lead-paragraph;
|
||||
margin-bottom: $large-spacing;
|
||||
}
|
||||
|
||||
&__links {
|
||||
margin-bottom: $large-spacing;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__link {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__video-link {
|
||||
margin-right: $large-spacing;
|
||||
}
|
||||
|
||||
&__link-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: $medium-spacing;
|
||||
}
|
||||
|
||||
&__link-description {
|
||||
@include heading-3;
|
||||
}
|
||||
|
||||
&__modules {
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@supports (display: grid) {
|
||||
display: grid;
|
||||
}
|
||||
grid-column-gap: $large-spacing;
|
||||
grid-row-gap: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
grid-template-columns: repeat(3, minmax(auto, 380px));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__teaser {
|
||||
color: $color-charcoal-dark;
|
||||
width: 90%;
|
||||
@include lead-paragraph;
|
||||
margin-bottom: $large-spacing;
|
||||
}
|
||||
|
||||
&__links {
|
||||
margin-bottom: $large-spacing;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__link {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__video-link {
|
||||
margin-right: $large-spacing;
|
||||
}
|
||||
|
||||
&__link-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: $medium-spacing;
|
||||
}
|
||||
|
||||
&__link-description {
|
||||
@include heading-3;
|
||||
}
|
||||
|
||||
&__modules {
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@supports (display: grid) {
|
||||
display: grid;
|
||||
}
|
||||
grid-column-gap: $large-spacing;
|
||||
grid-row-gap: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
grid-template-columns: repeat(3, minmax(auto, 380px));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,51 +1,51 @@
|
|||
// adapted from
|
||||
// https://stackoverflow.com/questions/41791193/vuejs-reactive-binding-for-a-plugin-how-to/41801107#41801107
|
||||
import Vue, { VueConstructor } from 'vue';
|
||||
import {reactive, App} from 'vue';
|
||||
|
||||
class ModalStore {
|
||||
vm: Vue;
|
||||
data: any;
|
||||
|
||||
constructor() {
|
||||
this.vm = new Vue({
|
||||
data: () => ({
|
||||
component: '',
|
||||
payload: {},
|
||||
}),
|
||||
this.data = reactive({
|
||||
component: '',
|
||||
payload: {}
|
||||
});
|
||||
}
|
||||
|
||||
get state() {
|
||||
return this.vm.$data;
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
interface Modal {
|
||||
state: any;
|
||||
component: string;
|
||||
payload?: any;
|
||||
confirm: (res: any) => void;
|
||||
open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>;
|
||||
cancel: () => void;
|
||||
_resolve: (r?: any) => any;
|
||||
_reject: (r?: any) => any;
|
||||
state: any,
|
||||
component: string,
|
||||
payload?: any,
|
||||
confirm: (res: any) => void,
|
||||
open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>,
|
||||
cancel: () => void,
|
||||
_resolve: (r?: any) => any,
|
||||
_reject: (r?: any) => any
|
||||
}
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$modal: Modal;
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$modal: Modal
|
||||
}
|
||||
}
|
||||
|
||||
const ModalPlugin = {
|
||||
install(Vue: VueConstructor) {
|
||||
install(app: App) {
|
||||
const store = new ModalStore();
|
||||
console.log('installing modal plugin');
|
||||
|
||||
|
||||
const reset = () => {
|
||||
store.state.component = '';
|
||||
store.state.payload = {};
|
||||
};
|
||||
|
||||
const modal: Modal = {
|
||||
app.config.globalProperties.$modal = {
|
||||
state: store.state,
|
||||
component: store.state.component,
|
||||
payload: store.state.payload,
|
||||
|
|
@ -65,12 +65,13 @@ const ModalPlugin = {
|
|||
reset();
|
||||
this._reject();
|
||||
},
|
||||
_resolve: () => {},
|
||||
_reject: () => {},
|
||||
_resolve: () => {
|
||||
},
|
||||
_reject: () => {
|
||||
},
|
||||
};
|
||||
|
||||
Vue.prototype.$modal = modal;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default ModalPlugin;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// from https://stackoverflow.com/questions/64213461/vuejs-typescript-cannot-find-module-components-navigation-or-its-correspon
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue';
|
||||
export default Vue;
|
||||
}
|
||||
// declare module "*.vue" {
|
||||
// import Vue from 'vue';
|
||||
// export default Vue;
|
||||
// }
|
||||
|
||||
// for Vue 3
|
||||
// declare module '*.vue' {
|
||||
// import type { DefineComponent } from 'vue'
|
||||
// const component: DefineComponent<{}, {}, any>
|
||||
// export default component
|
||||
// }
|
||||
//
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue