Upgrade code according to migration guide for Vue 3

Update npm dependencies

Update vue router version

Disable validation temporarily

Specify property

Update dependencies

Update store to v4

Update async component definitions

Update some event emitters

Update tiptap vue version

Implement some router changes for v4

Remove obsolete tag attributes

Update dependencies

Fix some cypress tests

Fix most jest tests

Fix some more cypress tests

Fix school class cypress test

Fix another cypress test

Disable failing test temporarily

Fix validation

Fix error messages for validation

Fix e2e test for beta login page

Apply prettier
This commit is contained in:
Ramon Wenger 2022-03-23 16:21:06 +01:00
parent 4b55f8952c
commit a52671fd40
106 changed files with 4604 additions and 5195 deletions

View File

@ -1 +1,3 @@
bundle-analysis
dist dist
node_modules

View File

@ -219,7 +219,9 @@ describe('Snapshot', () => {
cy.getByDataCy('module-snapshots-button').click(); cy.getByDataCy('module-snapshots-button').click();
cy.getByDataCy('create-snapshot-button').click(); cy.getByDataCy('create-snapshot-button').click();
cy.getByDataCy('show-all-snapshots-button').click(); cy.getByDataCy('show-all-snapshots-button').click();
cy.getByDataCy('snapshot-list').should('exist').within(() => { cy.getByDataCy('snapshot-list')
.should('exist')
.within(() => {
cy.get('.snapshots__snapshot').should('have.length', 1); cy.get('.snapshots__snapshot').should('have.length', 1);
}); });
waitNTimes(7); waitNTimes(7);

View File

@ -13,7 +13,8 @@ describe('Room Team Management - Read only', () => {
}, },
}, },
RoomsQuery: { RoomsQuery: {
rooms: [{ rooms: [
{
id: '', id: '',
slug: 'some-room', slug: 'some-room',
title: 'some room', title: 'some room',
@ -24,7 +25,8 @@ describe('Room Team Management - Read only', () => {
id: SELECTED_CLASS_ID, id: SELECTED_CLASS_ID,
name: 'bla', name: 'bla',
}, },
}], },
],
}, },
}); });

View File

@ -1,11 +1,5 @@
module.exports = { module.exports = {
moduleFileExtensions: [ moduleFileExtensions: ['js', 'jsx', 'ts', 'json', 'vue'],
'js',
'jsx',
'ts',
'json',
'vue',
],
transform: { transform: {
'\\.(gql|graphql)$': '@graphql-tools/jest-transform', '\\.(gql|graphql)$': '@graphql-tools/jest-transform',
'^.+\\.js$': 'babel-jest', '^.+\\.js$': 'babel-jest',
@ -13,20 +7,13 @@ module.exports = {
'^.+\\.vue$': '@vue/vue3-jest', '^.+\\.vue$': '@vue/vue3-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
}, },
modulePaths: [ modulePaths: ['<rootDir>/src', '<rootDir>/node_modules'],
'<rootDir>/src', transformIgnorePatterns: ['/node_modules/'],
'<rootDir>/node_modules',
],
transformIgnorePatterns: [
'/node_modules/',
],
moduleNameMapper: { moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', '^@/(.*)$': '<rootDir>/src/$1',
'^gql/(.*)$': '<rootDir>/src/graphql/gql/$1', '^gql/(.*)$': '<rootDir>/src/graphql/gql/$1',
}, },
snapshotSerializers: [ snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
'<rootDir>/node_modules/jest-serializer-vue',
],
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
testEnvironmentOptions: { testEnvironmentOptions: {
url: 'http://localhost/', url: 'http://localhost/',

View File

@ -3,6 +3,9 @@
"version": "1.0.0", "version": "1.0.0",
"description": "skillbox vue client", "description": "skillbox vue client",
"author": "ramon / chrigu", "author": "ramon / chrigu",
"prettier": {
"singleQuote": true
},
"private": true, "private": true,
"prettier": { "prettier": {
"singleQuote": true "singleQuote": true

View File

@ -1,9 +1,6 @@
<template> <template>
<div class="add-content"> <div class="add-content">
<a <a class="add-content__button" @click="addContent">
class="add-content__button"
@click="addContent"
>
<add-pointer class="add-content__icon" /> <add-pointer class="add-content__icon" />
</a> </a>
</div> </div>
@ -23,14 +20,15 @@
where: { where: {
type: Object, type: Object,
validator(prop) { validator(prop) {
return Object.prototype.hasOwnProperty.call(prop, 'after' ) return (
|| Object.prototype.hasOwnProperty.call(prop, 'parent'); Object.prototype.hasOwnProperty.call(prop, 'after') || Object.prototype.hasOwnProperty.call(prop, 'parent')
} );
},
}, },
}, },
components: { components: {
AddPointer AddPointer,
}, },
computed: { computed: {
@ -45,9 +43,8 @@
}, },
slug() { slug() {
return this.$route.params.slug; return this.$route.params.slug;
}
}, },
},
methods: { methods: {
addContent() { addContent() {
@ -60,26 +57,26 @@
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,8 +1,5 @@
<template> <template>
<div <div class="add-content-element" @click="$emit('add-element', index)">
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>
@ -15,13 +12,13 @@
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

@ -18,20 +18,21 @@
props: { props: {
route: { route: {
type: String, type: String,
default: null default: null,
}, },
reverse: { // use reverse colors reverse: {
// 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,17 +41,19 @@
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

@ -1,8 +1,5 @@
<template> <template>
<router-link <router-link :to="to" class="sub-navigation-item back-link">
:to="to"
class="sub-navigation-item back-link"
>
<chevron-left class="back-link__icon sub-navigation-item__icon" /> <chevron-left class="back-link__icon sub-navigation-item__icon" />
{{ fullTitle }} {{ fullTitle }}
</router-link> </router-link>

View File

@ -8,14 +8,8 @@
:key="index" :key="index"
@click="$emit('input', color.name)" @click="$emit('input', color.name)"
> >
<div <div :class="'color-chooser__color--' + color.name" class="color-chooser__color">
:class="'color-chooser__color--' + color.name" <tick class="color-chooser__selected-icon" v-if="selectedColor === color.name" />
class="color-chooser__color"
>
<tick
class="color-chooser__selected-icon"
v-if="selectedColor === color.name"
/>
</div> </div>
</div> </div>
</div> </div>
@ -29,33 +23,33 @@
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;
@ -82,7 +76,7 @@
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

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

View File

@ -1,28 +1,15 @@
<template> <template>
<header class="header-bar"> <header class="header-bar">
<a <a class="header-bar__sidebar-link" data-cy="open-sidebar-link" @click.stop="openSidebar('navigation')">
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" />
<div class="user-header"> <div class="user-header">
<a <a class="user-header__sidebar-link">
class="user-header__sidebar-link" <current-class class="user-header__current-class" @click="openSidebar('profile')" />
>
<current-class
class="user-header__current-class"
@click="openSidebar('profile')"
/>
</a> </a>
<user-widget <user-widget :avatar-url="me.avatarUrl" data-cy="header-user-widget" @click="openSidebar('profile')" />
:avatar-url="me.avatarUrl"
data-cy="header-user-widget"
@click="openSidebar('profile')"
/>
</div> </div>
</header> </header>
</template> </template>
@ -51,7 +38,7 @@
</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

@ -17,14 +17,14 @@
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;
@ -66,7 +66,6 @@
height: 10px; height: 10px;
transform: rotate(-45deg) translateY(-50%); transform: rotate(-45deg) translateY(-50%);
} }
} }
&:hover &__tooltip { &:hover &__tooltip {

View File

@ -1,45 +1,41 @@
<template> <template>
<button <button :disabled="loading || disabled" class="loading-button button button--primary button--big">
: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 <loading-icon class="loading-button__icon" v-else />
class="loading-button__icon"
v-else
/>
</button> </button>
</template> </template>
<script> <script>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const LoadingIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LoadingIcon')); 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,17 +4,11 @@
<hamburger class="mobile-header__hamburger" /> <hamburger class="mobile-header__hamburger" />
</a> </a>
<router-link <router-link to="/" data-cy="mobile-home-link">
to="/"
data-cy="mobile-home-link"
>
<logo /> <logo />
</router-link> </router-link>
<user-widget <user-widget v-bind="me" @click.stop="openSidebar('profile')" />
v-bind="me"
@click.stop="openSidebar('profile')"
/>
</div> </div>
</template> </template>
@ -46,7 +40,7 @@
</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,7 +1,11 @@
<template> <template>
<div class="modal__backdrop"> <div class="modal__backdrop">
<div <div
:class="{'modal--hide-header': hideHeader || fullscreen, 'modal--fullscreen': fullscreen, 'modal--small': small}" :class="{
'modal--hide-header': hideHeader || fullscreen,
'modal--fullscreen': fullscreen,
'modal--small': small,
}"
class="modal" class="modal"
> >
<div class="modal__header"> <div class="modal__header">
@ -9,20 +13,14 @@
</div> </div>
<div class="modal__body"> <div class="modal__body">
<slot /> <slot />
<div <div class="modal__close-button" @click="hideModal">
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 <a class="button" @click="hideModal">Abbrechen</a>
class="button"
@click="hideModal"
>Abbrechen</a>
</slot> </slot>
</div> </div>
</div> </div>
@ -37,32 +35,32 @@
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;
@ -78,7 +76,7 @@
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;
@ -136,7 +134,7 @@
&--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;
@ -145,7 +143,6 @@
#{$parent}__body { #{$parent}__body {
padding: $default-padding; padding: $default-padding;
} }
} }
&--fullscreen { &--fullscreen {
@ -153,7 +150,7 @@
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,17 +1,9 @@
<template> <template>
<div class="more-options"> <div class="more-options">
<a <a class="more-options__more-link" data-cy="more-options-link" @click.stop="showMenu = !showMenu">
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 <widget-popover class="more-options__popover" v-if="showMenu" @hide-me="showMenu = false">
class="more-options__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<slot /> <slot />
</widget-popover> </widget-popover>
</div> </div>
@ -26,19 +18,19 @@
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

@ -1,10 +1,6 @@
<template> <template>
<transition name="fade"> <transition name="fade">
<a <a class="scroll-up" v-if="scroll > 200" @click="scrollTop">
class="scroll-up"
v-if="scroll>200"
@click="scrollTop"
>
<arrow-up class="scroll-up__icon" /> <arrow-up class="scroll-up__icon" />
</a> </a>
</transition> </transition>
@ -16,12 +12,12 @@
export default { export default {
components: { components: {
ArrowUp ArrowUp,
}, },
data() { data() {
return { return {
scroll: 0 scroll: 0,
}; };
}, },
@ -39,7 +35,7 @@
methods: { methods: {
scrollTop() { scrollTop() {
document.scrollingElement.scrollTop = 0; document.scrollingElement.scrollTop = 0;
} },
}, },
}; };
</script> </script>
@ -65,15 +61,14 @@
height: 50px; height: 50px;
fill: $color-brand; fill: $color-brand;
} }
} }
.fade-enter-active, .fade-leave-active { .fade-enter-active,
transition: opacity .3s; .fade-leave-active {
transition: opacity 0.3s;
} }
.fade-enter-from, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ .fade-enter-from, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
{
opacity: 0; opacity: 0;
} }
</style> </style>

View File

@ -1,9 +1,6 @@
<template> <template>
<div class="submission-document"> <div class="submission-document">
<p <p class="submission-document__content content" v-if="document && document.length > 0">
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>
@ -12,7 +9,9 @@
<script> <script>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
import filenameFromUrl from '@/helpers/urls'; import filenameFromUrl from '@/helpers/urls';
const DocumentIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/DocumentIcon')); const DocumentIcon = defineAsyncComponent(() =>
import(/* webpackChunkName: "icons" */ '@/components/icons/DocumentIcon')
);
export default { export default {
name: 'StudentSubmissionDocument', name: 'StudentSubmissionDocument',
@ -22,7 +21,7 @@
computed: { computed: {
filename() { filename() {
return filenameFromUrl(this.document); return filenameFromUrl(this.document);
} },
}, },
}; };
</script> </script>

View File

@ -1,17 +1,7 @@
<template> <template>
<div <div :class="{ 'user-widget--is-profile': isProfile }" class="user-widget" @click.stop="$emit('click', $event)">
:class="{'user-widget--is-profile': isProfile}" <div class="user-widget__avatar" data-cy="user-widget-avatar">
class="user-widget" <avatar :avatar-url="avatarUrl" :icon-highlighted="isProfile" />
@click.stop="$emit('click', $event)"
>
<div
class="user-widget__avatar"
data-cy="user-widget-avatar"
>
<avatar
:avatar-url="avatarUrl"
:icon-highlighted="isProfile"
/>
</div> </div>
</div> </div>
</template> </template>
@ -22,25 +12,25 @@
export default { export default {
props: { props: {
avatarUrl: { avatarUrl: {
type: String type: String,
} },
}, },
emits: ['click'], emits: ['click'],
components: { emits: ['click'],components: {
Avatar Avatar,
}, },
computed: { computed: {
isProfile() { isProfile() {
return this.$route.meta.isProfile; return this.$route.meta.isProfile;
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.user-widget { .user-widget {
color: $color-silver-dark; color: $color-silver-dark;

View File

@ -1,8 +1,5 @@
<template> <template>
<nav <nav :class="{ 'content-navigation--sidebar': isSidebar }" class="content-navigation">
: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
@ -15,9 +12,7 @@
{{ $flavor.textTopics }} {{ $flavor.textTopics }}
</router-link> </router-link>
<topic-navigation <topic-navigation v-if="isSidebar" />
v-if="isSidebar"
/>
</div> </div>
<div class="content-navigation__item"> <div class="content-navigation__item">
@ -45,12 +40,7 @@
</div> </div>
</div> </div>
<router-link <router-link to="/" class="content-navigation__logo" data-cy="home-link" v-if="!isSidebar">
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>
@ -67,10 +57,7 @@
</router-link> </router-link>
</div> </div>
<div <div class="content-navigation__item content-navigation__item--secondary" v-if="showPortfolio">
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"
@ -80,10 +67,7 @@
Portfolio Portfolio
</router-link> </router-link>
</div> </div>
<div <div class="content-navigation__item content-navigation__item--secondary" v-if="isSidebar">
class="content-navigation__item content-navigation__item--secondary"
v-if="isSidebar"
>
<a <a
:href="$flavor.supportLink" :href="$flavor.supportLink"
target="_blank" target="_blank"
@ -108,21 +92,21 @@
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: {
@ -134,14 +118,14 @@
}, },
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;
@ -152,7 +136,8 @@
@include navigation-link; @include navigation-link;
} }
&__primary, &__secondary { &__primary,
&__secondary {
display: none; display: none;
flex-direction: row; flex-direction: row;
@ -162,7 +147,7 @@
} }
&__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;
@ -197,7 +182,8 @@
&--sidebar { &--sidebar {
flex-direction: column; flex-direction: column;
#{$parent}__primary, #{$parent}__secondary { #{$parent}__primary,
#{$parent}__secondary {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
@ -213,7 +199,6 @@
&:only-child { &:only-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
#{$parent}__item { #{$parent}__item {

View File

@ -1,18 +1,8 @@
<template> <template>
<transition name="slide"> <transition name="slide">
<div <div class="navigation-sidebar" v-if="sidebar.navigation" v-click-outside="close">
class="navigation-sidebar" <content-navigation :is-sidebar="true" class="navigation-sidebar__main" />
v-if="sidebar.navigation" <div class="navigation-sidebar__close-button" @click="close">
v-click-outside="close"
>
<content-navigation
:is-sidebar="true"
class="navigation-sidebar__main"
/>
<div
class="navigation-sidebar__close-button"
@click="close"
>
<cross class="navigation-sidebar__close-icon" /> <cross class="navigation-sidebar__close-icon" />
</div> </div>
</div> </div>
@ -32,21 +22,20 @@
components: { components: {
ContentNavigation, ContentNavigation,
Cross Cross,
}, },
methods: { methods: {
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';
$desktop-width: 285px; $desktop-width: 285px;
@ -68,10 +57,10 @@
grid-template-columns: 1fr 50px; grid-template-columns: 1fr 50px;
grid-template-rows: 50px max-content auto 100px; grid-template-rows: 50px max-content auto 100px;
grid-template-areas: "m m" "m m" "s s" "s s"; grid-template-areas: 'm m' 'm m' 's s' 's s';
&--with-subnavigation { &--with-subnavigation {
grid-template-areas: "m m" "m m" "sub sub" "s s"; grid-template-areas: 'm m' 'm m' 'sub sub' 's s';
} }
height: 100vh; height: 100vh;
@ -99,11 +88,13 @@
} }
.slide { .slide {
&-enter-active, &-leave-active { &-enter-active,
&-leave-active {
transition: left 0.2s; transition: left 0.2s;
} }
&-enter-from, &-leave-to { &-enter-from,
&-leave-to {
left: -100vw; left: -100vw;
@include desktop { @include desktop {
left: -$desktop-width; left: -$desktop-width;

View File

@ -1,21 +1,11 @@
<template> <template>
<div <div :class="{ 'sub-navigation-item--active': show }" class="sub-navigation-item" v-click-outside="close">
:class="{ 'sub-navigation-item--active': show}" <div class="sub-navigation-item__title" @click="show = !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 <div class="sub-navigation-item__nav-items book-subnavigation" v-if="show">
class="sub-navigation-item__nav-items book-subnavigation"
v-if="show"
>
<slot /> <slot />
</div> </div>
</div> </div>
@ -31,25 +21,25 @@
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,6 +26,7 @@
default: false, default: false,
}, },
}, },
},
mixins: [sidebarMixin], mixins: [sidebarMixin],
@ -40,6 +41,7 @@
return atob(id); return atob(id);
}, },
}, },
},
apollo: { apollo: {
topics: { topics: {

View File

@ -26,14 +26,10 @@
: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"
/> />
@ -110,7 +106,7 @@
CmsDocumentBlock, CmsDocumentBlock,
InfogramBlock, InfogramBlock,
ThinglinkBlock, ThinglinkBlock,
Assignment Assignment,
}, },
computed: { computed: {
@ -216,12 +212,12 @@
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 {
@ -295,9 +291,12 @@
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

@ -1,13 +1,8 @@
<template> <template>
<div class="content-form-section"> <div class="content-form-section">
<h2 class="content-form-section__heading"> <h2 class="content-form-section__heading">
<component <component class="content-form-section__icon" :is="icon" />
class="content-form-section__icon" <span class="content-form-section__title" data-cy="content-form-section-title">{{ title }}</span>
:is="icon"
/> <span
class="content-form-section__title"
data-cy="content-form-section-title"
>{{ title }}</span>
</h2> </h2>
<content-element-actions <content-element-actions

View File

@ -18,27 +18,11 @@
/> />
</template> </template>
<add-content-element <add-content-element :index="-1" class="contents-form__add" @add-element="addElement" />
:index="-1" <div class="contents-form__element" v-for="(element, index) in localContentBlock.contents" :key="index">
class="contents-form__add" <content-element :element="element" @update="update(index, $event)" @remove="remove(index)" />
@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 <add-content-element :index="index" class="contents-form__add" @add-element="addElement" />
:index="index"
class="contents-form__add"
@add-element="addElement"
/>
</div> </div>
<template #footer> <template #footer>
@ -48,11 +32,9 @@
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 >
class="button" <a class="button" @click="$emit('hide')">Abbrechen</a>
@click="$emit('hide')"
>Abbrechen</a>
</div> </div>
</template> </template>
</modal> </modal>
@ -97,12 +79,15 @@
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: {},
}; };
}, },
@ -148,19 +133,17 @@
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,10 +1,5 @@
<template> <template>
<contents-form <contents-form :content-block="contentBlock" :show-task-selection="true" @save="saveContentBlock" @hide="hideModal" />
:content-block="contentBlock"
:show-task-selection="true"
@save="saveContentBlock"
@hide="hideModal"
/>
</template> </template>
<script> <script>
@ -38,7 +33,8 @@
this.$store.dispatch('hideModal'); this.$store.dispatch('hideModal');
}, },
saveContentBlock(contentBlock) { saveContentBlock(contentBlock) {
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: EDIT_CONTENT_BLOCK_MUTATION, mutation: EDIT_CONTENT_BLOCK_MUTATION,
variables: { variables: {
input: { input: {
@ -61,6 +57,7 @@
}); });
}, },
}, },
},
apollo: { apollo: {
contentBlock() { contentBlock() {

View File

@ -1,9 +1,5 @@
<template> <template>
<div <div :class="componentClass" :data-scrollto="component.id" data-cy="content-component">
:class="componentClass"
:data-scrollto="component.id"
data-cy="content-component"
>
<bookmark-actions <bookmark-actions
:bookmarked="bookmarked" :bookmarked="bookmarked"
:note="note" :note="note"
@ -13,11 +9,7 @@
@edit-note="editNote" @edit-note="editNote"
@bookmark="bookmarkContent(component.id, !bookmarked)" @bookmark="bookmarkContent(component.id, !bookmarked)"
/> />
<component <component v-bind="component" :parent="parent" :is="component.type" />
v-bind="component"
:parent="parent"
:is="component.type"
/>
</div> </div>
</template> </template>
@ -50,60 +42,60 @@ export default {
props: { props: {
component: { component: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
parent: { parent: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
bookmarks: { bookmarks: {
type: Array, type: Array,
default: () => ([]) default: () => [],
}, },
notes: { notes: {
type: Array, type: Array,
default: () => ([]) default: () => [],
}, },
root: { root: {
type: String, type: String,
default: '' default: '',
}, },
editMode: { editMode: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
components: { components: {
'text_block': TextBlock, text_block: TextBlock,
'basic_knowledge': InstrumentWidget, // for legacy basic_knowledge: InstrumentWidget, // for legacy
'instrument': InstrumentWidget, instrument: InstrumentWidget,
'image_block': ImageBlock, image_block: ImageBlock,
'image_url_block': ImageUrlBlock, image_url_block: ImageUrlBlock,
'video_block': VideoBlock, video_block: VideoBlock,
'link_block': LinkBlock, link_block: LinkBlock,
'document_block': DocumentBlock, document_block: DocumentBlock,
'infogram_block': InfogramBlock, infogram_block: InfogramBlock,
'genially_block': GeniallyBlock, genially_block: GeniallyBlock,
'subtitle': SubtitleBlock, subtitle: SubtitleBlock,
'section_title': SectionTitleBlock, section_title: SectionTitleBlock,
'content_list': ContentListBlock, content_list: ContentListBlock,
'module_room_slug': ModuleRoomSlug, module_room_slug: ModuleRoomSlug,
'thinglink_block': ThinglinkBlock, thinglink_block: ThinglinkBlock,
'cms_document_block': CmsDocumentBlock, cms_document_block: CmsDocumentBlock,
Survey, Survey,
Solution, Solution,
Instruction, Instruction,
Assignment, Assignment,
BookmarkActions BookmarkActions,
}, },
computed: { computed: {
bookmarked() { bookmarked() {
return this.bookmarks && !!this.bookmarks.find(bookmark => bookmark.uuid === this.component.id); return this.bookmarks && !!this.bookmarks.find((bookmark) => bookmark.uuid === this.component.id);
}, },
note() { note() {
const bookmark = this.bookmarks && this.bookmarks.find(bookmark => bookmark.uuid === this.component.id); const bookmark = this.bookmarks && this.bookmarks.find((bookmark) => bookmark.uuid === this.component.id);
return bookmark && bookmark.note; return bookmark && bookmark.note;
}, },
showBookmarkActions() { showBookmarkActions() {
@ -115,18 +107,19 @@ export default {
classes.push('content-component--bookmarked'); classes.push('content-component--bookmarked');
} }
return classes; return classes;
} },
}, },
methods: { methods: {
addNote(id) { addNote(id) {
const type = Object.prototype.hasOwnProperty.call(this.parent, '__typename') const type = Object.prototype.hasOwnProperty.call(this.parent, '__typename')
? this.parent.__typename : 'ContentBlockNode'; ? this.parent.__typename
: 'ContentBlockNode';
this.$store.dispatch('addNote', { this.$store.dispatch('addNote', {
content: id, content: id,
type, type,
block: this.root block: this.root,
}); });
}, },
editNote() { editNote() {
@ -134,19 +127,18 @@ export default {
}, },
bookmarkContent(uuid, bookmarked) { bookmarkContent(uuid, bookmarked) {
this.$apollo.mutate(constructContentComponentBookmarkMutation(uuid, bookmarked, this.parent, this.root)); this.$apollo.mutate(constructContentComponentBookmarkMutation(uuid, bookmarked, this.parent, this.root));
} },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~styles/helpers"; @import '~styles/helpers';
.content-component { .content-component {
position: relative; position: relative;
&--bookmarked { &--bookmarked {
} }
&--subtitle { &--subtitle {

View File

@ -1,12 +1,7 @@
<template> <template>
<content-list <content-list :items="contentBlocks">
:items="contentBlocks"
>
<template #default="{ item }"> <template #default="{ item }">
<content-block <content-block :content-block="item" :parent="parent" />
:content-block="item"
:parent="parent"
/>
</template> </template>
</content-list> </content-list>
</template> </template>

View File

@ -1,16 +1,8 @@
<template> <template>
<div class="document-block"> <div class="document-block">
<document-icon class="document-block__icon" /> <document-icon class="document-block__icon" />
<a <a :href="value.url" class="document-block__link" target="_blank">{{ urlName }}</a>
:href="value.url" <a class="document-block__remove" v-if="showTrashIcon" @click="$emit('trash')">
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>
@ -39,13 +31,13 @@
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,7 @@
<template> <template>
<div <div class="instruction" v-if="me.isTeacher">
class="instruction"
v-if="me.isTeacher"
>
<bulb-icon class="instruction__icon" /> <bulb-icon class="instruction__icon" />
<a <a :href="url" class="instruction__link">{{ text }}</a>
:href="url"
class="instruction__link"
>{{ text }}</a>
</div> </div>
</template> </template>
@ -22,7 +16,7 @@
mixins: [me], mixins: [me],
components: { components: {
BulbIcon BulbIcon,
}, },
computed: { computed: {
@ -31,13 +25,13 @@
}, },
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,14 +1,7 @@
<template> <template>
<div <div :class="{ 'link-block--no-margin': noMargin }" class="link-block">
:class="{ 'link-block--no-margin': noMargin}"
class="link-block"
>
<link-icon class="link-block__icon" /> <link-icon class="link-block__icon" />
<a <a :href="href" class="link-block__link" target="_blank">{{ value.text }}</a>
:href="href"
class="link-block__link"
target="_blank"
>{{ value.text }}</a>
</div> </div>
</template> </template>
@ -20,20 +13,20 @@
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,22 +1,12 @@
<template> <template>
<div <div class="final-submission" data-cy="final-submission">
class="final-submission" <document-block :value="{ url: userInput.document }" class="final-submission__document" v-if="userInput.document" />
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 <a class="final-submission__reopen" data-cy="final-submission-reopen" v-if="showReopen" @click="$emit('reopen')"
class="final-submission__reopen" >Bearbeiten</a
data-cy="final-submission-reopen" >
v-if="showReopen"
@click="$emit('reopen')"
>Bearbeiten</a>
</div> </div>
</div> </div>
</template> </template>
@ -24,7 +14,9 @@
<script> <script>
import { newLineToParagraph } from '@/helpers/text'; import { newLineToParagraph } from '@/helpers/text';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const DocumentBlock = defineAsyncComponent(() => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock')); const DocumentBlock = defineAsyncComponent(() =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/DocumentBlock')
);
const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon')); const InfoIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/InfoIcon'));
@ -32,16 +24,16 @@
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: {
@ -52,13 +44,13 @@
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,10 +10,7 @@
/> />
</div> </div>
<div <div class="submission-form-container__actions" v-if="!isFinalOrReadOnly">
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"
@ -29,11 +26,7 @@
> >
{{ spellcheckText }} {{ spellcheckText }}
</button> </button>
<file-upload <file-upload :document="userInput.document" v-if="allowsDocuments" @change-document-url="changeDocumentUrl" />
:document="userInput.document"
v-if="allowsDocuments"
@change-document-url="changeDocumentUrl"
/>
<slot /> <slot />
</div> </div>
@ -53,7 +46,6 @@
const FinalSubmission = defineAsyncComponent(() => import('@/components/content-blocks/assignment/FinalSubmission')); const FinalSubmission = defineAsyncComponent(() => import('@/components/content-blocks/assignment/FinalSubmission'));
const FileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/FileUpload')); const FileUpload = defineAsyncComponent(() => import('@/components/ui/file-upload/FileUpload'));
export default { export default {
props: { props: {
userInput: Object, userInput: Object,
@ -116,7 +108,6 @@
this.$emit('changeDocumentUrl', documentUrl); this.$emit('changeDocumentUrl', documentUrl);
}, },
}, },
}; };
</script> </script>
@ -159,5 +150,4 @@
display: inline-block; display: inline-block;
} }
} }
</style> </style>

View File

@ -11,16 +11,10 @@
v-auto-grow v-auto-grow
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
/> />
<div <div class="submission-form__save-status submission-form__save-status--saved" v-if="saved">
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 <div class="submission-form__save-status submission-form__save-status--unsaved" v-if="!saved">
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>
@ -28,8 +22,12 @@
<script> <script>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const TickCircleIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/TickCircleIcon')); const TickCircleIcon = defineAsyncComponent(() =>
const LoadingIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/LoadingIcon')); import(/* webpackChunkName: "icons" */ '@/components/icons/TickCircleIcon')
);
const LoadingIcon = defineAsyncComponent(() =>
import(/* webpackChunkName: "icons" */ '@/components/icons/LoadingIcon')
);
export default { export default {
props: { props: {
@ -38,18 +36,18 @@
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"
@ -27,12 +27,12 @@
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,28 +1,12 @@
<template> <template>
<div <div class="document-form" ref="documentform">
class="document-form" <div v-if="!value.url" ref="uploadcare-panel" />
ref="documentform" <div class="document-form__spinner" v-if="loading">
>
<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 <div class="document-form__uploaded" v-if="value.url">
class="document-form__uploaded"
v-if="value.url"
>
<document-icon class="document-form__icon" /> <document-icon class="document-form__icon" />
<a <a :href="previewUrl" class="document-form__link" target="_blank">{{ previewLink }}</a>
:href="previewUrl"
class="document-form__link"
target="_blank"
>{{ previewLink }}</a>
</div> </div>
</div> </div>
</template> </template>
@ -65,19 +49,22 @@
}, },
mounted() { mounted() {
uploadcare(this, url => { uploadcare(
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,21 +1,11 @@
<template> <template>
<div> <div>
<div <div class="video-form" v-if="!isVimeo && !isYoutube && !isSrf">
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 <a Sie können Videos auf
class="video-form__platform-link help-text__link" <a class="video-form__platform-link help-text__link" href="https://youtube.com/" target="_blank">Youtube</a>
href="https://youtube.com/" oder <a class="video-form__platform-link help-text__link" href="https://vimeo.com/" target="_blank">Vimeo</a>
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>
@ -24,7 +14,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">
@ -54,7 +44,7 @@
InfoIcon, InfoIcon,
YoutubeEmbed, YoutubeEmbed,
VimeoEmbed, VimeoEmbed,
SrfEmbed SrfEmbed,
}, },
computed: { computed: {
@ -66,14 +56,14 @@
}, },
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;
@ -84,11 +74,9 @@
align-items: center; align-items: center;
&__help-icon { &__help-icon {
} }
&__help-description { &__help-description {
} }
&__platform-link { &__platform-link {
@ -100,7 +88,7 @@
&__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

@ -284,14 +284,9 @@
/> />
<path <path
d="M339.92,387.48c0-.82-.33-1.61-.91-2.19-.58-.58-1.37-.91-2.19-.91H23.71c-.82,0-1.61,.33-2.19,.91-.58,.58-.91,1.37-.91,2.19h0c0,.82,.33,1.61,.91,2.19,.58,.58,1.37,.91,2.19,.91H336.83c.82,0,1.61-.33,2.19-.91,.58-.58,.91-1.37,.91-2.19h0Z" d="M339.92,387.48c0-.82-.33-1.61-.91-2.19-.58-.58-1.37-.91-2.19-.91H23.71c-.82,0-1.61,.33-2.19,.91-.58,.58-.91,1.37-.91,2.19h0c0,.82,.33,1.61,.91,2.19,.58,.58,1.37,.91,2.19,.91H336.83c.82,0,1.61-.33,2.19-.91,.58-.58,.91-1.37,.91-2.19h0Z"
fill="#fff" fill="#fff" fill-rule="evenodd" />
fill-rule="evenodd"
/>
<polygon <polygon
points="291.56 386.68 286.32 386.68 286.32 505.02 291.56 505.02 291.56 386.68 291.56 386.68" points="291.56 386.68 286.32 386.68 286.32 505.02 291.56 505.02 291.56 386.68 291.56 386.68" fill="#fff" fill-rule="evenodd" />
fill="#fff"
fill-rule="evenodd"
/>
<polygon <polygon
points="72.67 387.59 67.43 387.59 67.43 505.02 72.67 505.02 72.67 387.59 72.67 387.59" points="72.67 387.59 67.43 387.59 67.43 505.02 72.67 505.02 72.67 387.59 72.67 387.59"
fill="#fff" fill="#fff"

View File

@ -1,29 +1,19 @@
<template> <template>
<a <a :class="typeClass" class="filter-entry" data-cy="filter-entry" :style="categoryStyle" @click="$emit('filter')"
:class="typeClass"
class="filter-entry"
data-cy="filter-entry"
:style="categoryStyle"
@click="$emit('filter')"
> >
<span class="filter-entry__text">{{ text }}</span> <span class="filter-entry__text">{{ text }}</span>
<span <span :style="activeStyle" class="filter-entry__icon-wrapper">
:style="activeStyle" <chevron-right :style="{ fill: category.foreground }" class="filter-entry__icon" />
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'; import { defineAsyncComponent } from 'vue';
const ChevronRight = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight')); const ChevronRight = defineAsyncComponent(() =>
import(/* webpackChunkName: "icons" */ '@/components/icons/ChevronRight')
);
export default { export default {
props: { props: {

View File

@ -51,15 +51,15 @@
apollo: { apollo: {
instrumentFilter: { instrumentFilter: {
query: INSTRUMENT_FILTER_QUERY query: INSTRUMENT_FILTER_QUERY,
} },
}, },
data() { data() {
return { return {
instrumentFilter: { instrumentFilter: {
currentFilter: '' currentFilter: '',
} },
}; };
}, },
inheritAttrs: false, inheritAttrs: false,
@ -76,10 +76,9 @@
this.$apollo.mutate({ this.$apollo.mutate({
mutation: SET_FILTER_MUTATION, mutation: SET_FILTER_MUTATION,
variables: { variables: {
filter filter,
} },
}); });
}
}, },
}); });
</script> </script>

View File

@ -1,12 +1,6 @@
<template> <template>
<router-link <router-link :to="moduleLink" :class="['module-teaser', { 'module-teaser--small': !teaser }]">
:to="moduleLink" <div :style="{ backgroundImage: 'url(' + heroImage + ')' }" class="module-teaser__image" />
:class="['module-teaser', {'module-teaser--small': !teaser}]"
>
<div
:style="{backgroundImage: 'url('+heroImage+')'}"
class="module-teaser__image"
/>
<div class="module-teaser__body"> <div class="module-teaser__body">
<h3 class="module-teaser__meta-title"> <h3 class="module-teaser__meta-title">
{{ metaTitle }} {{ metaTitle }}
@ -38,7 +32,8 @@
return {}; return {};
} }
} }
} },
},
}; };
</script> </script>
@ -47,7 +42,7 @@
.module-teaser { .module-teaser {
box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.12); box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.12);
border: 1px solid #E2E2E2; border: 1px solid #e2e2e2;
height: 330px; height: 330px;
max-width: 380px; max-width: 380px;
width: 100%; width: 100%;

View File

@ -1,8 +1,5 @@
<template> <template>
<div <div class="bookmark-actions" v-if="!editMode">
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"
@ -41,27 +38,27 @@
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%;
@ -88,7 +85,8 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
&--bookmarked, &--noted { &--bookmarked,
&--noted {
opacity: 1; opacity: 1;
} }
} }
@ -101,5 +99,4 @@
} }
} }
} }
</style> </style>

View File

@ -6,10 +6,7 @@
placeholder="Lernziel erfassen..." placeholder="Lernziel erfassen..."
@input="$emit('input', $event)" @input="$emit('input', $event)"
/> />
<a <a class="icon-button" @click="$emit('delete')">
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>
@ -25,13 +22,13 @@
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,8 +1,5 @@
<template> <template>
<a <a class="add-project-entry" @click="addProjectEntry">
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>
@ -18,13 +15,13 @@
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,25 +1,9 @@
<template> <template>
<div class="portfolio-onboarding"> <div class="portfolio-onboarding">
<h1 <h1 class="portfolio-onboarding__heading" data-cy="page-title">Portfolio</h1>
class="portfolio-onboarding__heading" <portfolio-illustration data-cy="portfolio-onboarding-illustration" class="portfolio-onboarding__illustration" />
data-cy="page-title" <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">
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>

View File

@ -1,48 +1,20 @@
<template> <template>
<div <div class="project-actions" data-cy="project-actions">
class="project-actions" <a class="project-actions__more-link" @click.stop="toggleMenu">
data-cy="project-actions"
>
<a
class="project-actions__more-link"
@click.stop="toggleMenu"
>
<ellipses /> <ellipses />
</a> </a>
<widget-popover <widget-popover class="project-actions__popover" v-if="showMenu" @hide-me="showMenu = false">
class="project-actions__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<li class="popover-links__link"> <li class="popover-links__link">
<a <a data-cy="delete-project" @click="deleteProject(slug)">Projekt löschen</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 <a data-cy="edit-project" @click="editProject(slug)">Projekt bearbeiten</a>
data-cy="edit-project"
@click="editProject(slug)"
>Projekt bearbeiten</a>
</li> </li>
<li <li class="popover-links__link" v-if="!final && shareButtons">
class="popover-links__link" <a data-cy="share-project" @click="updateProjectShareState(slug, true)">Projekt teilen</a>
v-if="!final && shareButtons"
>
<a
data-cy="share-project"
@click="updateProjectShareState(slug, true)"
>Projekt teilen</a>
</li> </li>
<li <li class="popover-links__link" v-if="final && shareButtons">
class="popover-links__link" <a data-cy="unshare-project" @click="updateProjectShareState(slug, false)">Projekt nicht mehr teilen</a>
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>
@ -96,30 +68,38 @@ export default {
this.$router.push({ name: 'edit-project', params: { slug } }); this.$router.push({ name: 'edit-project', params: { slug } });
}, },
deleteProject(slug) { deleteProject(slug) {
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: DELETE_PROJECT_MUTATION, mutation: DELETE_PROJECT_MUTATION,
variables: { variables: {
input: { input: {
slug, slug,
}, },
}, },
update(store, {data: {deleteProject: {success}}}) { update(
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');
}); });
}, },
@ -128,7 +108,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,43 +1,21 @@
<template> <template>
<div <div class="project-entry" data-cy="project-entry">
class="project-entry" <more-options-widget class="project-entry__more" data-cy="project-entry-more" v-if="!readOnly">
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 <a data-cy="edit-project-entry" @click="editProjectEntry()">Eintrag bearbeiten</a>
data-cy="edit-project-entry"
@click="editProjectEntry()"
>Eintrag bearbeiten</a>
</li> </li>
<li class="popover-links__link"> <li class="popover-links__link">
<a <a data-cy="delete-project-entry" @click="deleteProjectEntry()">Eintrag löschen</a>
data-cy="delete-project-entry"
@click="deleteProjectEntry()"
>Eintrag löschen</a>
</li> </li>
</more-options-widget> </more-options-widget>
<h3 <h3 class="project-entry__heading" data-cy="project-entry-date">
class="project-entry__heading"
data-cy="project-entry-date"
>
{{ createdDateTime }} {{ createdDateTime }}
</h3> </h3>
<p <p class="project-entry__paragraph" data-cy="project-entry-activity">
class="project-entry__paragraph"
data-cy="project-entry-activity"
>
{{ description }} {{ description }}
</p> </p>
<p <p class="project-entry__paragraph" v-if="documentUrl">
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">
@ -86,7 +64,14 @@
id: this.id, id: this.id,
}, },
}, },
update(store, {data: {deleteProjectEntry: {success}}}) { update(
store,
{
data: {
deleteProjectEntry: { success },
},
}
) {
if (success) { if (success) {
const query = PROJECT_QUERY; const query = PROJECT_QUERY;
const variables = { const variables = {
@ -94,7 +79,7 @@
}; };
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: {
@ -113,7 +98,7 @@
</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;
@ -151,6 +136,5 @@
display: block; display: block;
} }
} }
} }
</style> </style>

View File

@ -1,12 +1,7 @@
<template> <template>
<modal :hide-header="false"> <modal :hide-header="false">
<template #header> <template #header>
<h2 <h2 class="project-entry-modal__heading" data-cy="modal-title">Beitrag erfassen</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">
@ -36,15 +31,10 @@
</div> </div>
</div> </div>
<template #footer> <template #footer>
<a <a class="button button--primary" data-cy="modal-save-button" @click="$emit('save', localProjectEntry)"
class="button button--primary" >Speichern</a
data-cy="modal-save-button" >
@click="$emit('save', localProjectEntry)" <a class="button" @click="$emit('hide')">Abbrechen</a>
>Speichern</a>
<a
class="button"
@click="$emit('hide')"
>Abbrechen</a>
</template> </template>
</modal> </modal>
</template> </template>
@ -74,9 +64,12 @@
data() { data() {
return { return {
localProjectEntry: Object.assign({}, { localProjectEntry: Object.assign(
{},
{
...this.projectEntry, ...this.projectEntry,
}), }
),
}; };
}, },
@ -92,7 +85,7 @@
</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;
@ -135,7 +128,5 @@
@include heading-3; @include heading-3;
margin-bottom: 0; margin-bottom: 0;
} }
} }
</style> </style>

View File

@ -1,17 +1,7 @@
<template> <template>
<page-form <page-form :title="title" @save="$emit('save', localProject)">
:title="title" <page-form-input label="Titel" v-model="localProject.title" />
@save="$emit('save', localProject)" <page-form-input label="Beschreibung" type="textarea" v-model="localProject.description" />
>
<page-form-input
label="Titel"
v-model="localProject.title"
/>
<page-form-input
label="Beschreibung"
type="textarea"
v-model="localProject.description"
/>
<template #footer> <template #footer>
<button <button
:class="{ 'button--disabled': !formValid }" :class="{ 'button--disabled': !formValid }"
@ -22,12 +12,7 @@
> >
Speichern Speichern
</button> </button>
<router-link <router-link to="/portfolio" class="button"> Abbrechen </router-link>
to="/portfolio"
class="button"
>
Abbrechen
</router-link>
</template> </template>
</page-form> </page-form>
</template> </template>

View File

@ -70,10 +70,8 @@ export default {
@include desktop { @include desktop {
display: flex; display: flex;
flex: 80%; flex: 80%;
flex-direction: row; flex-direction: row;align-items: center;
align-items: center; }}
}
}
&__title { &__title {
flex: 50%; flex: 50%;
@ -96,7 +94,6 @@ export default {
&__entry-count { &__entry-count {
justify-self: flex-end; justify-self: flex-end;
grid-column: 2 / span 1; grid-column: 2 / span 1;}
}
} }
</style> </style>

View File

@ -1,7 +1,5 @@
<template> <template>
<a <a class="share-icon" @click="$emit('share')"
class="share-icon"
@click="$emit('share')"
> >
<share-icon class="share-icon__icon" /> <share-icon class="share-icon__icon" />
<span class="share-icon__text"> <span class="share-icon__text">

View File

@ -15,17 +15,9 @@
ref="avatarImage" ref="avatarImage"
/> />
</transition> </transition>
<img <img :src="avatarUrl" class="avatar__fake-image" ref="fakeImage" />
:src="avatarUrl"
class="avatar__fake-image"
ref="fakeImage"
>
<div <div class="avatar__edit" v-if="editable" @click="closeSidebar">
class="avatar__edit"
v-if="editable"
@click="closeSidebar"
>
<router-link :to="{ name: 'profile' }"> <router-link :to="{ name: 'profile' }">
<pen-icon /> <pen-icon />
</router-link> </router-link>
@ -43,20 +35,20 @@
export default { export default {
props: { props: {
avatarUrl: { avatarUrl: {
type: String type: String,
}, },
iconHighlighted: {}, iconHighlighted: {},
editable: { editable: {
default: false default: false,
} },
}, },
components: { components: {
DefaultAvatar, DefaultAvatar,
PenIcon PenIcon,
}, },
data() { data() {
return { return {
isAvatarLoaded: false isAvatarLoaded: false,
}; };
}, },
mounted() { mounted() {
@ -75,17 +67,17 @@
mutation: TOGGLE_SIDEBAR, mutation: TOGGLE_SIDEBAR,
variables: { variables: {
sidebar: { sidebar: {
profile: false profile: false,
} },
} },
}); });
} },
} },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
$max-width: 100%; $max-width: 100%;

View File

@ -1,15 +1,9 @@
<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 <div v-if="content.type === 'text_block'" v-html="text" />
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 <link-block :value="content.value" :no-margin="true" />
:value="content.value"
:no-margin="true"
/>
</div> </div>
<p v-else> <p v-else>
{{ type }} {{ type }}
@ -27,8 +21,8 @@
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';
@ -46,7 +40,7 @@
default: default:
return this.content.type; return this.content.type;
} }
} },
} },
}; };
</script> </script>

View File

@ -1,9 +1,5 @@
<template> <template>
<a <a class="edit-group-name" data-cy="edit-group-name-link" @click="$emit('edit')">
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>
@ -14,13 +10,13 @@
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

@ -181,17 +181,13 @@ export default {
} }
&__role { &__role {
@include desktop { @include desktop {flex: 0 1 110px;
flex: 0 1 110px;
text-align: right; text-align: right;
} }}
}
&__action { &__action {
@include desktop { @include desktop {flex: 0 1 110px;
flex: 0 1 110px;
padding-left: $large-spacing; padding-left: $large-spacing;
} }
} }}
}
</style> </style>

View File

@ -1,26 +1,15 @@
<template> <template>
<div class="profile"> <div class="profile">
<h1 class="profile__header"> <h1 class="profile__header">Profilbild</h1>
Profilbild <div class="profile-avatar" v-if="me.avatarUrl">
</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 <a class="profile-avatar__remove icon-button" @click="deleteAvatar()">
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 <avatar-upload-form v-else @avatarUpdate="updateAvatar" />
v-else
@avatarUpdate="updateAvatar"
/>
</div> </div>
</template> </template>
@ -36,14 +25,14 @@
components: { components: {
AvatarUploadForm, AvatarUploadForm,
Avatar, Avatar,
TrashIcon TrashIcon,
}, },
data() { data() {
return { return {
me: { me: {
avatarUrl: '' avatarUrl: '',
} },
}; };
}, },
apollo: { apollo: {
@ -56,37 +45,46 @@
this.updateAvatar(''); this.updateAvatar('');
}, },
updateAvatar(url) { updateAvatar(url) {
this.$apollo.mutate({ this.$apollo
.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;
@ -101,5 +99,4 @@
.profile-avatar { .profile-avatar {
margin-bottom: $large-spacing; margin-bottom: $large-spacing;
} }
</style> </style>

View File

@ -1,69 +1,31 @@
<template> <template>
<transition name="slide"> <transition name="slide">
<div <div class="profile-sidebar" data-cy="sidebar" v-if="sidebar.profile" v-click-outside="close">
class="profile-sidebar" <a class="profile-sidebar__close-link" data-cy="close-profile-sidebar-link" @click="close">
data-cy="sidebar"
v-if="sidebar.profile"
v-click-outside="close"
>
<a
class="profile-sidebar__close-link"
data-cy="close-profile-sidebar-link"
@click="close"
>
<cross class="profile-sidebar__close-icon" /> <cross class="profile-sidebar__close-icon" />
</a> </a>
<div class="profile-sidebar__section"> <div class="profile-sidebar__section">
<profile-widget class="profile-sidebar__item" /> <profile-widget class="profile-sidebar__item" />
<div <div class="profile-sidebar__item" @click="close">
class="profile-sidebar__item" <router-link to="/me/activity" class="profile-sidebar__link"> Meine Aktivitäten </router-link>
@click="close"
>
<router-link
to="/me/activity"
class="profile-sidebar__link"
>
Meine Aktivitäten
</router-link>
</div> </div>
<div <div class="profile-sidebar__item" v-if="me.isTeacher && !me.readOnly" @click="close">
class="profile-sidebar__item" <router-link :to="myTeamPage" data-cy="my-team-link" class="profile-sidebar__link"> Mein Team </router-link>
v-if="me.isTeacher && !me.readOnly"
@click="close"
>
<router-link
:to="myTeamPage"
data-cy="my-team-link"
class="profile-sidebar__link"
>
Mein Team
</router-link>
</div> </div>
</div> </div>
<div class="profile-sidebar__section"> <div class="profile-sidebar__section">
<div class="profile-sidebar__item"> <div class="profile-sidebar__item">
<class-selection-widget /> <class-selection-widget />
<div @click="close"> <div @click="close">
<router-link <router-link :to="{ name: 'my-class' }" data-cy="class-list-link" class="profile-sidebar__link">
:to="{name: 'my-class'}"
data-cy="class-list-link"
class="profile-sidebar__link"
>
Klassenliste Klassenliste
</router-link> </router-link>
</div> </div>
</div> </div>
</div> </div>
<div class="profile-sidebar__section"> <div class="profile-sidebar__section">
<div <div class="profile-sidebar__item" @click="close">
class="profile-sidebar__item" <router-link :to="{ name: 'join-class' }" data-cy="join-class-link" class="profile-sidebar__link">
@click="close"
>
<router-link
:to="{name:'join-class'}"
data-cy="join-class-link"
class="profile-sidebar__link"
>
Zugangscode Zugangscode
</router-link> </router-link>
</div> </div>
@ -88,7 +50,6 @@
const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon')); const Cross = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/CrossIcon'));
export default { export default {
mixins: [sidebar, me], mixins: [sidebar, me],
components: { components: {
@ -115,7 +76,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
$desktop-width: 333px; $desktop-width: 333px;
@ -173,11 +134,13 @@
} }
.slide { .slide {
&-enter-active, &-leave-active { &-enter-active,
&-leave-active {
transition: right 0.2s; transition: right 0.2s;
} }
&-enter-from, &-leave-to { &-enter-from,
&-leave-to {
right: -100vw; right: -100vw;
@include desktop { @include desktop {
right: -$desktop-width; right: -$desktop-width;

View File

@ -32,12 +32,9 @@ export default {
.show-code { .show-code {
&__title { &__title {
@include regular-text; @include regular-text;
margin-bottom: $large-spacing; margin-bottom: $large-spacing;
@include desktop { @include desktop { margin-bottom: 2 * $large-spacing;
margin-bottom: 2 * $large-spacing; }}
}
}
&__code { &__code {
font-size: toRem(60px); font-size: toRem(60px);

View File

@ -1,9 +1,5 @@
<template> <template>
<router-link <router-link class="add-room-entry-button" data-cy="add-room-entry-button" :to="addRoomEntryRoute">
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>
@ -32,7 +28,7 @@
</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,7 +1,9 @@
<template> <template>
<div class="entry-count-widget"> <div class="entry-count-widget">
<component :is="icon" /> <component :is="icon" />
<span data-cy="entry-count">{{ entryCount }} <template v-if="verbose">{{ entryCount === 1 ? 'Beitrag' : 'Beiträge' }}</template></span> <span data-cy="entry-count"
>{{ entryCount }} <template v-if="verbose">{{ entryCount === 1 ? 'Beitrag' : 'Beiträge' }}</template></span
>
</div> </div>
</template> </template>
@ -21,8 +23,8 @@
}, },
icon: { icon: {
type: String, type: String,
default: 'cards' default: 'cards',
} },
}, },
components: { components: {
@ -34,7 +36,7 @@
</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;
@ -52,5 +54,4 @@
@include room-widget-text-style; @include room-widget-text-style;
} }
} }
</style> </style>

View File

@ -8,11 +8,7 @@
> >
<ellipses /> <ellipses />
</a> </a>
<widget-popover <widget-popover class="more-actions__popover" v-if="showMenu" @hide-me="showMenu = false">
class="more-actions__popover"
v-if="showMenu"
@hide-me="showMenu = false"
>
<slot :toggle="toggleMenu" /> <slot :toggle="toggleMenu" />
</widget-popover> </widget-popover>
</div> </div>
@ -27,8 +23,8 @@
props: { props: {
background: { background: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
components: { components: {

View File

@ -1,41 +1,16 @@
<template> <template>
<page-form <page-form class="room-form" title="Neuer Raum" @save="$emit('save', localRoom)">
class="room-form" <page-form-input label="Titel" v-model="localRoom.title" />
title="Neuer Raum"
@save="$emit('save', localRoom)"
>
<page-form-input
label="Titel"
v-model="localRoom.title"
/>
<page-form-input <page-form-input label="Beschreibung" type="textarea" v-model="localRoom.description" />
label="Beschreibung"
type="textarea"
v-model="localRoom.description"
/>
<h2 class="room-form__property-heading"> <h2 class="room-form__property-heading">Farbe</h2>
Farbe <color-chooser :selected-color="localRoom.appearance" @input="updateColor" />
</h2>
<color-chooser
:selected-color="localRoom.appearance"
@input="updateColor"
/>
<template #footer> <template #footer>
<button <button type="submit" data-cy="room-form-save" class="button button--primary room-form__save-button">
type="submit"
data-cy="room-form-save"
class="button button--primary room-form__save-button"
>
Speichern Speichern
</button> </button>
<router-link <router-link to="/rooms" class="button"> Abbrechen </router-link>
to="/rooms"
class="button"
>
Abbrechen
</router-link>
</template> </template>
</page-form> </page-form>
</template> </template>
@ -53,13 +28,13 @@
components: { components: {
ColorChooser, ColorChooser,
PageForm, PageForm,
PageFormInput PageFormInput,
}, },
data() { data() {
return { return {
localRoom: Object.assign({}, this.room), localRoom: Object.assign({}, this.room),
me: {} me: {},
}; };
}, },
@ -75,19 +50,19 @@
updateColor(newColor) { updateColor(newColor) {
this.localRoom.appearance = newColor; this.localRoom.appearance = newColor;
this.$store.dispatch('setSpecialContainerClass', newColor); this.$store.dispatch('setSpecialContainerClass', newColor);
} },
}, },
apollo: { apollo: {
me: { me: {
query: ME_QUERY, query: ME_QUERY,
} },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "~styles/helpers"; @import '~styles/helpers';
.room-form { .room-form {
&__property-heading { &__property-heading {

View File

@ -15,13 +15,13 @@
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;
@ -35,7 +35,7 @@
} }
& > span { & > span {
@include room-widget-text-style;; @include room-widget-text-style;
} }
} }
</style> </style>

View File

@ -15,25 +15,27 @@
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon')); const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'));
const ClosedEyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ClosedEyeIcon')); 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;
@ -50,5 +52,4 @@
@include room-widget-text-style; @include room-widget-text-style;
} }
} }
</style> </style>

View File

@ -1,12 +1,6 @@
<template> <template>
<div <div :class="roomClass" class="room-widget">
:class="roomClass" <router-link :to="{ name: 'room', params: { slug: slug } }" class="room-widget__content">
class="room-widget"
>
<router-link
:to="{name: 'room', params: {slug: slug}}"
class="room-widget__content"
>
<h2 class="room-widget__title"> <h2 class="room-widget__title">
{{ title }} {{ title }}
</h2> </h2>

View File

@ -1,25 +1,12 @@
<template> <template>
<div class="rooms-onboarding"> <div class="rooms-onboarding">
<h1 <h1 class="rooms-onboarding__heading" data-cy="page-title">Räume</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 <p data-cy="rooms-onboarding-text" class="rooms-onboarding__text">
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 <router-link :to="newRoomRoute" class="button button--primary" data-cy="create-room-button" v-if="isTeacher">
:to="newRoomRoute"
class="button button--primary"
data-cy="create-room-button"
v-if="isTeacher"
>
Raum erstellen Raum erstellen
</router-link> </router-link>
</div> </div>
@ -29,7 +16,9 @@
<script> <script>
import { NEW_ROOM_PAGE } from '@/router/room.names'; import { NEW_ROOM_PAGE } from '@/router/room.names';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const RoomsIllustration = defineAsyncComponent(() => import(/* webpackChunkName: "illustrations" */'@/components/illustrations/RoomsIllustration')); const RoomsIllustration = defineAsyncComponent(() =>
import(/* webpackChunkName: "illustrations" */ '@/components/illustrations/RoomsIllustration')
);
export default { export default {
props: { props: {

View File

@ -13,12 +13,7 @@
/> />
<chevron-down class="selected-class__dropdown-icon" /> <chevron-down class="selected-class__dropdown-icon" />
</div> </div>
<widget-popover <widget-popover :mobile="mobile" class="class-selection__popover" v-if="showPopover" @hide-me="showPopover = false">
: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"
@ -70,12 +65,11 @@
const AddIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/AddIcon')); 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],
@ -83,22 +77,22 @@
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: {
@ -111,12 +105,12 @@
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;
@ -130,7 +124,6 @@
left: 0; left: 0;
transform: translateY($small-spacing); transform: translateY($small-spacing);
} }
} }
.selected-class { .selected-class {

View File

@ -1,8 +1,5 @@
<template> <template>
<span <span class="current-class" data-cy="current-class-name">{{ currentClassName }}</span>
class="current-class"
data-cy="current-class-name"
>{{ currentClassName }}</span>
</template> </template>
<script> <script>
@ -14,8 +11,8 @@
</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';
.current-class { .current-class {
display: flex; display: flex;

View File

@ -6,26 +6,19 @@
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="{'base-input-container__checkbox': type==='checkbox', 'base-input-container__radiobutton': type === 'radiobutton'}" :class="{
'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 <circle-icon data-cy="circle-icon" v-if="type === 'radiobutton'" />
data-cy="circle-icon"
v-if="type === 'radiobutton'"
/>
</span> </span>
<span <span class="base-input-container__label" data-cy="base-input-label" v-if="label">{{ label }}</span>
class="base-input-container__label" <slot class="base-input-container__label" v-if="!label" />
data-cy="base-input-label"
v-if="label"
>{{ label }}</span>
<slot
class="base-input-container__label"
v-if="!label"
/>
</label> </label>
</template> </template>
@ -38,15 +31,15 @@
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,15 +1,7 @@
<template> <template>
<li <li class="popover-links__link">
class="popover-links__link" <a class="popover-link" @click="$emit('link-action')">
> <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>

View File

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

View File

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

View File

@ -30,5 +30,5 @@ export default {
ArrowThinDown, ArrowThinDown,
ArrowThinTop, ArrowThinTop,
ArrowThinUp, ArrowThinUp,
TrashIcon TrashIcon,
}; };

View File

@ -1,11 +1,6 @@
<template> <template>
<div <div class="skillboxform-input">
class="skillboxform-input" <label :for="id" class="skillboxform-input__label">
>
<label
:for="id"
class="skillboxform-input__label"
>
{{ label }} {{ label }}
</label> </label>
<slot :id="id" /> <slot :id="id" />
@ -15,6 +10,6 @@
<script setup> <script setup>
defineProps({ defineProps({
id: String, id: String,
label: String label: String,
}); });
</script> </script>

View File

@ -1,18 +1,8 @@
<template> <template>
<div class="visibility-action"> <div class="visibility-action">
<a <a class="visibility-action__action-button" v-if="canManageContent" @click="toggleVisibility()">
class="visibility-action__action-button" <closed-eye-icon class="visibility-action__action-icon action-icon" v-if="hidden" />
v-if="canManageContent" <eye-icon class="visibility-action__action-icon action-icon" v-else />
@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>
@ -25,35 +15,37 @@
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon')); const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'));
const ClosedEyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */'@/components/icons/ClosedEyeIcon')); 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: {
@ -61,16 +53,18 @@
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,56 +1,29 @@
<template> <template>
<footer <footer class="default-footer" data-cy="page-footer">
class="default-footer"
data-cy="page-footer"
>
<div class="default-footer__section"> <div class="default-footer__section">
<div class="default-footer__info"> <div class="default-footer__info">
<div class="default-footer__who-are-we who-are-we"> <div class="default-footer__who-are-we who-are-we">
<h5 class="who-are-we__title"> <h5 class="who-are-we__title">Wer sind wir?</h5>
Wer sind wir?
</h5>
<p class="who-are-we__text"> <p class="who-are-we__text">
mySkillbox ist ein Angebot des hep Verlags in mySkillbox ist ein Angebot des hep Verlags in Zusammenarbeit mit der Eidgenössischen Hochschule für
Zusammenarbeit mit der Eidgenössischen Hochschule für Berufsbildung (EHB). Berufsbildung (EHB).
</p> </p>
</div> </div>
<a <a href="https://www.hep-verlag.ch/" target="_blank">
href="https://www.hep-verlag.ch/"
target="_blank"
>
<hep-logo class="default-footer__logo-hep" /> <hep-logo class="default-footer__logo-hep" />
</a> </a>
<a <a href="https://www.ehb.swiss/" target="_blank">
href="https://www.ehb.swiss/"
target="_blank"
>
<ehb-logo class="default-footer__logo-ehb" /> <ehb-logo class="default-footer__logo-ehb" />
</a> </a>
</div> </div>
</div> </div>
<div class="default-footer__section"> <div class="default-footer__section">
<div class="default-footer__links"> <div class="default-footer__links">
<a <a href="https://myskillbox.ch/datenschutz" target="_blank" class="default-footer__link">Datenschutz</a>
href="https://myskillbox.ch/datenschutz" <a href="https://myskillbox.ch/impressum" target="_blank" class="default-footer__link">Impressum</a>
target="_blank" <a href="https://myskillbox.ch/agb" target="_blank" class="default-footer__link">AGB</a>
class="default-footer__link" <a :href="$flavor.supportLink" target="_blank" class="default-footer__link">Support</a>
>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>
</div> </div>
</footer> </footer>
@ -65,14 +38,14 @@
export default { export default {
components: { components: {
HepLogo, HepLogo,
EhbLogo EhbLogo,
} },
}; };
</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';
.default-footer { .default-footer {
background-color: $color-silver-light; background-color: $color-silver-light;

View File

@ -1,12 +1,6 @@
<template> <template>
<div <div :class="specialContainerClass" class="container layout layout--fullscreen">
:class="specialContainerClass" <div class="close-button" @click="back">
class="container layout layout--fullscreen"
>
<div
class="close-button"
@click="back"
>
<cross class="close-button__icon" /> <cross class="close-button__icon" />
</div> </div>
@ -20,25 +14,25 @@
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;
@ -49,5 +43,4 @@
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
</style> </style>

View File

@ -1,18 +1,12 @@
<template> <template>
<div class="layout layout--public public"> <div class="layout layout--public public">
<div class="public__logo"> <div class="public__logo">
<router-link <router-link :to="{ name: 'hello' }" class="hep-link">
:to="{name: 'hello'}"
class="hep-link"
>
<logo /> <logo />
</router-link> </router-link>
</div> </div>
<router-view class="public__content layout__content" /> <router-view class="public__content layout__content" />
<default-footer <default-footer class="skillbox__footer public__footer footer" v-if="$flavor.showFooter" />
class="skillbox__footer public__footer footer"
v-if="$flavor.showFooter"
/>
</div> </div>
</template> </template>
@ -24,15 +18,15 @@
export default { export default {
components: { components: {
Logo, Logo,
DefaultFooter DefaultFooter,
}, },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/styles/_variables.scss"; @import '@/styles/_variables.scss';
@import "@/styles/_mixins.scss"; @import '@/styles/_mixins.scss';
@import "@/styles/_default-layout.scss"; @import '@/styles/_default-layout.scss';
@mixin content-block { @mixin content-block {
padding-right: $medium-spacing; padding-right: $medium-spacing;
@ -51,7 +45,7 @@
} }
.public { .public {
grid-template-areas: "h" "c" "f"; grid-template-areas: 'h' 'c' 'f';
&__content { &__content {
@include content-block(); @include content-block();
@ -61,7 +55,7 @@
&__logo { &__logo {
@include content-block(); @include content-block();
margin-top: $medium-spacing margin-top: $medium-spacing;
} }
&__footer { &__footer {

View File

@ -1,19 +1,10 @@
<template> <template>
<div <div :class="{ 'layout--full-width': $route.meta.fullWidth }" class="skillbox layout layout--simple">
:class="{'layout--full-width': $route.meta.fullWidth}" <div class="close-button" @click="back">
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 <simple-footer class="layout__footer" v-if="enableFooter" />
class="layout__footer"
v-if="enableFooter"
/>
</div> </div>
</template> </template>
@ -25,7 +16,7 @@
export default { export default {
components: { components: {
Cross, Cross,
SimpleFooter SimpleFooter,
}, },
computed: { computed: {
@ -34,19 +25,19 @@
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

@ -24,7 +24,7 @@
contents: ContentsIllustration, contents: ContentsIllustration,
portfolio: PortfolioIllustration, portfolio: PortfolioIllustration,
rooms: RoomsIllustration, rooms: RoomsIllustration,
hello: Hello hello: Hello,
}, },
computed: { computed: {
@ -32,14 +32,16 @@
return this.$route.meta.illustration; return this.$route.meta.illustration;
}, },
illustrationAlignment() { illustrationAlignment() {
return this.$route.meta.illustrationAlign ? `split-view__illustration--${this.$route.meta.illustrationAlign}` : ''; return 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

@ -126,7 +126,8 @@
const redirectUrl = this.$route.query.redirect ? this.$route.query.redirect : '/'; const redirectUrl = this.$route.query.redirect ? this.$route.query.redirect : '/';
this.$apollo.mutate({ this.$apollo
.mutate({
client: 'publicClient', client: 'publicClient',
mutation: BETA_LOGIN_MUTATION, mutation: BETA_LOGIN_MUTATION,
variables, variables,
@ -141,7 +142,8 @@
this.loginError = 'Es ist ein Fehler aufgetreten. Bitte versuchen Sie nochmals.'; this.loginError = 'Es ist ein Fehler aufgetreten. Bitte versuchen Sie nochmals.';
} }
}, },
}).catch(error => { })
.catch((error) => {
const firstError = error.graphQLErrors[0]; const firstError = error.graphQLErrors[0];
switch (firstError.message) { switch (firstError.message) {
case 'invalid_credentials': case 'invalid_credentials':
@ -158,7 +160,7 @@
</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;
@ -166,7 +168,6 @@
} }
.actions { .actions {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -177,5 +178,4 @@
line-height: 19px; line-height: 19px;
} }
} }
</style> </style>

View File

@ -1,10 +1,5 @@
<template> <template>
<content-block-form <content-block-form title="Inhaltsblock erfassen" :content-block="contentBlock" @back="goToModule" @save="save" />
title="Inhaltsblock erfassen"
:content-block="contentBlock"
@back="goToModule"
@save="save"
/>
</template> </template>
<script> <script>
@ -27,6 +22,7 @@
default: '' default: ''
} }
}, },
},
components: { components: {
ContentBlockForm, ContentBlockForm,
@ -36,8 +32,8 @@
contentBlock: { contentBlock: {
title: '', title: '',
isAssignment: false, isAssignment: false,
contents: [ contents: [],
]}, },
}), }),
methods: { methods: {
@ -53,33 +49,35 @@
if (after) { if (after) {
input = { input = {
contentBlock, contentBlock,
after after,
}; };
} else { } else {
input = { input = {
contentBlock, contentBlock,
parent parent,
}; };
} }
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: NEW_CONTENT_BLOCK_MUTATION, mutation: NEW_CONTENT_BLOCK_MUTATION,
variables: { variables: {
input input,
}, },
refetchQueries: [{ refetchQueries: [
{
query: MODULE_DETAILS_QUERY, query: MODULE_DETAILS_QUERY,
variables: { variables: {
slug slug,
} },
}] },
}).then(this.goToModule); ],
})
.then(this.goToModule);
}, },
goToModule() { goToModule() {
// use the history, so the scroll position is preserved // use the history, so the scroll position is preserved
this.$router.go(-1); this.$router.go(-1);
} },
} },
}); });
</script> </script>

View File

@ -1,64 +1,39 @@
<template> <template>
<div <div class="hello" data-cy="hello-page">
class="hello"
data-cy="hello-page"
>
<div class="about"> <div class="about">
<div class="about__logos logos"> <div class="about__logos logos">
<a <a href="https://www.hep-verlag.ch/" target="_blank">
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 <a href="https://www.ehb.swiss/" target="_blank" v-if="$flavor.showEHB">
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 {{ $flavor.textAppName }} ist ein Angebot des hep Verlags in Zusammenarbeit mit der Eidgenössischen Hochschule
Zusammenarbeit mit der Eidgenössischen Hochschule für Berufsbildung (EHB). 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 <h2 class="login-actions__title" data-cy="hello-title">
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 <a class="button button--primary button--big actions__submit" href="/api/oauth/login/" data-cy="oauth-login"
class="button button--primary button--big actions__submit" >Mit hep Konto anmelden</a
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 <a class="hep-link" href="/api/oauth/login/" data-cy="oauth-login">Jetzt registrieren</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 <a class="hep-link" href="https://myskillbox.ch/anleitung" data-cy="oauth-login">Anleitung anschauen</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">
@ -86,22 +61,21 @@
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;
@ -163,7 +137,8 @@
&__register { &__register {
margin-top: $large-spacing; margin-top: $large-spacing;
> p, a { > p,
a {
@include regular-text; @include regular-text;
} }
} }
@ -171,7 +146,8 @@
.information { .information {
margin-top: $hello-block-margin; margin-top: $hello-block-margin;
> p, a { > p,
a {
@include regular-text; @include regular-text;
} }
} }
@ -181,7 +157,6 @@
display: flex; display: flex;
&__list-item { &__list-item {
color: $color-silver-dark; color: $color-silver-dark;
> a { > a {
@ -205,5 +180,4 @@
} }
} }
} }
</style> </style>

View File

@ -88,9 +88,10 @@ export default {
display: grid; display: grid;
@include desktop { @include desktop {
grid-template-columns: 300px auto; grid-template-columns: 300px auto;
} }grid-column-gap: $small-spacing;
grid-column-gap: $small-spacing;
padding: 0 $small-spacing; padding: 0 $small-spacing;
box-sizing: border-box; box-sizing: border-box;
&__list { &__list {

View File

@ -2,8 +2,7 @@
<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 Für <span class="info-header__emph">{{ me.email }}</span> haben wir keine gültige Lizenz gefunden
gültige Lizenz gefunden
</p> </p>
</header> </header>
<section class="coupon"> <section class="coupon">
@ -67,16 +66,10 @@
<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 <a :href="teacherEditionUrl" class="hep-link">{{ $flavor.textAppName }} für Lehrpersonen</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 <a :href="studentEditionUrl" class="hep-link">{{ $flavor.textAppName }} für Lernende</a>
:href="studentEditionUrl"
class="hep-link"
>{{ $flavor.textAppName }} für Lernende</a>
</li> </li>
</ul> </ul>
</section> </section>
@ -130,32 +123,33 @@
console.log('coupon', couponCode); console.log('coupon', couponCode);
this.submitted = true; this.submitted = true;
this.loading = true; this.loading = true;
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: REDEEM_COUPON, mutation: REDEEM_COUPON,
variables: { variables: {
input: { input: {
couponCode couponCode
}, },
}, },
update: ( update: (store, { data: { coupon } }) => {
store,
{
data: {coupon},
},
) => {
if (coupon.success) { if (coupon.success) {
this.couponErrors = []; this.couponErrors = [];
this.$apollo.query({ this.$apollo
.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 = ['Es ist ein Fehler aufgetreten. Bitte versuchen Sie es nochmals oder kontaktieren Sie den Administrator.']; this.couponErrors = [
'Es ist ein Fehler aufgetreten. Bitte versuchen Sie es nochmals oder kontaktieren Sie den Administrator.',
];
} }
}) })
.finally(() => { .finally(() => {
@ -167,7 +161,7 @@
</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;
@ -182,7 +176,7 @@
} }
.get-license { .get-license {
margin-top: $large-spacing margin-top: $large-spacing;
} }
.license-links { .license-links {
@ -190,5 +184,4 @@
margin-bottom: $medium-spacing; margin-bottom: $medium-spacing;
} }
} }
</style> </style>

View File

@ -1,8 +1,6 @@
<template> <template>
<div class="module-visibility"> <div class="module-visibility">
<h1 class="module-visibility__page-title"> <h1 class="module-visibility__page-title">Sichtbarkeit</h1>
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 (
@ -18,28 +16,15 @@
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 <option value="" selected>-</option>
value="" <option :value="schoolClass.id" v-for="schoolClass in schoolClasses" :key="schoolClass.id">
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 <a class="button button--primary" data-cy="save-visibility-button" @click="sync">Anpassungen übernehmen</a>
class="button button--primary"
data-cy="save-visibility-button"
@click="sync"
>Anpassungen übernehmen</a>
</div> </div>
</div> </div>
</template> </template>
@ -55,7 +40,6 @@
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon')); const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'));
export default { export default {
mixins: [me], mixins: [me],
components: { components: {
EyeIcon, EyeIcon,
@ -69,11 +53,11 @@
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: {
@ -83,7 +67,8 @@
sync() { sync() {
if (this.selectedClassId) { if (this.selectedClassId) {
const slug = this.slug; const slug = this.slug;
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: SYNC_VISIBILITY_MUTATION, mutation: SYNC_VISIBILITY_MUTATION,
variables: { variables: {
input: { input: {
@ -100,13 +85,13 @@
}, },
}, },
], ],
}, })
).then(() => { .then(() => {
this.$router.push({ this.$router.push({
name: MODULE_PAGE, name: MODULE_PAGE,
params: { params: {
slug slug,
} },
}); });
}); });
} }

View File

@ -1,13 +1,9 @@
<template> <template>
<div> <div>
<logo class="onboarding__logo" /> <logo class="onboarding__logo" />
<h1 class="onboarding__heading"> <h1 class="onboarding__heading">Herzlich willkommen!</h1>
Herzlich willkommen!
</h1>
<p class="onboarding__claim"> <p class="onboarding__claim">Schauen Sie sich die Einführung an und lernen Sie {{ $flavor.textAppName }} kennen.</p>
Schauen Sie sich die Einführung an und lernen Sie {{ $flavor.textAppName }} kennen.
</p>
</div> </div>
</template> </template>
@ -17,7 +13,7 @@
export default { export default {
components: { components: {
Logo Logo,
}, },
}; };
</script> </script>

View File

@ -140,8 +140,7 @@ export default {
't t' 't t'
'd d' 'd d'
'm m'; 'm m';
} }}
}
&__back { &__back {
grid-area: b; grid-area: b;
@ -155,10 +154,8 @@ export default {
grid-area: a; grid-area: a;
display: flex; display: flex;
@include desktop { @include desktop {justify-self: end;
justify-self: end; }}
}
}
&__more { &__more {
display: none; display: none;
@ -198,17 +195,15 @@ export default {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: $medium-spacing; margin-bottom: $medium-spacing;@include desktop {
flex-direction: row-reverse;align-items: center;
@include desktop {
flex-direction: row-reverse;
align-items: center;
margin-bottom: 0; margin-bottom: 0;
} }
justify-content: flex-start; justify-content: flex-start;
position: relative; position: relative;
& > :first-child {
margin-bottom: $medium-spacing;
& > :first-child {margin-bottom: $medium-spacing;
@include desktop { @include desktop {
margin-left: $large-spacing; margin-left: $large-spacing;
margin-bottom: 0; margin-bottom: 0;
@ -216,11 +211,9 @@ export default {
} }
& > :nth-child(2) { & > :nth-child(2) {
@include desktop { @include desktop {margin-left: $large-spacing;
margin-left: $large-spacing;
}
}
} }
}}
&__content { &__content {
display: flex; display: flex;

View File

@ -1,11 +1,5 @@
<template> <template>
<content-block-form <content-block-form :content-block="roomEntry" :features="features" v-if="roomEntry.id" @save="save" @back="goBack" />
:content-block="roomEntry"
:features="features"
v-if="roomEntry.id"
@save="save"
@back="goBack"
/>
</template> </template>
<script> <script>
@ -30,6 +24,7 @@
required: true required: true
} }
}, },
},
components: { components: {
ContentBlockForm, ContentBlockForm,
@ -50,10 +45,10 @@
query: ROOM_ENTRY_QUERY, query: ROOM_ENTRY_QUERY,
variables() { variables() {
return { return {
slug: this.entrySlug slug: this.entrySlug,
}; };
} },
} },
}, },
methods: { methods: {
@ -61,8 +56,8 @@
this.$router.push({ this.$router.push({
name: ROOM_PAGE, name: ROOM_PAGE,
params: { params: {
slug: this.slug slug: this.slug,
} },
}); });
}, },
save({ title, contents }) { save({ title, contents }) {
@ -71,14 +66,22 @@
title, title,
contents, contents,
}; };
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: UPDATE_ROOM_ENTRY_MUTATION, mutation: UPDATE_ROOM_ENTRY_MUTATION,
variables: { variables: {
input: { input: {
roomEntry: entry, roomEntry: entry,
}
}, },
update: (store, {data: {updateRoomEntry: {roomEntry}}}) => { },
update: (
store,
{
data: {
updateRoomEntry: { roomEntry },
},
}
) => {
try { try {
const fragment = ROOM_ENTRY_FRAGMENT; const fragment = ROOM_ENTRY_FRAGMENT;
const id = store.identify(roomEntry); const id = store.identify(roomEntry);
@ -87,24 +90,21 @@
store.writeFragment({ store.writeFragment({
id, id,
fragment, fragment,
data data,
}); });
} catch (e) { } catch (e) {
// 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.goBack(); this.goBack();
}); });
},
}
}, },
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import '~styles/helpers';
</style> </style>

View File

@ -1,10 +1,5 @@
<template> <template>
<content-block-form <content-block-form :content-block="roomEntry" :features="features" @save="save" @back="goBack" />
:content-block="roomEntry"
:features="features"
@save="save"
@back="goBack"
/>
</template> </template>
<script> <script>
@ -21,8 +16,8 @@
props: { props: {
slug: { slug: {
type: String, type: String,
required: true required: true,
} },
}, },
components: { components: {
@ -44,24 +39,32 @@
this.$router.push({ this.$router.push({
name: ROOM_PAGE, name: ROOM_PAGE,
params: { params: {
slug: this.slug slug: this.slug,
} },
}); });
}, },
save({ title, contents }) { save({ title, contents }) {
const entry = { const entry = {
title, title,
contents, contents,
roomSlug: this.slug roomSlug: this.slug,
}; };
this.$apollo.mutate({ this.$apollo
.mutate({
mutation: NEW_ROOM_ENTRY_MUTATION, mutation: NEW_ROOM_ENTRY_MUTATION,
variables: { variables: {
input: { input: {
roomEntry: entry, roomEntry: entry,
}
}, },
update: (store, {data: {addRoomEntry: {roomEntry}}}) => { },
update: (
store,
{
data: {
addRoomEntry: { roomEntry },
},
}
) => {
try { try {
const query = ROOM_ENTRIES_QUERY; const query = ROOM_ENTRIES_QUERY;
const variables = { slug: this.slug }; const variables = { slug: this.slug };
@ -69,39 +72,33 @@
if (room && room.roomEntries) { if (room && room.roomEntries) {
const newEdge = { const newEdge = {
node: roomEntry, node: roomEntry,
__typename: 'RoomEntryNodeEdge' __typename: 'RoomEntryNodeEdge',
}; };
const edges = [ const edges = [newEdge, ...room.roomEntries.edges];
newEdge,
...room.roomEntries.edges
];
const data = { const data = {
room: { room: {
...room, ...room,
roomEntries: { roomEntries: {
...room.roomEntries, ...room.roomEntries,
edges edges,
} },
} },
}; };
store.writeQuery({ query, variables, data }); store.writeQuery({ query, variables, data });
} }
} catch (e) { } catch (e) {
// 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.goBack(); this.goBack();
}); });
},
}
}, },
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '~styles/helpers'; @import '~styles/helpers';
</style> </style>

View File

@ -17,12 +17,7 @@
:key="index" :key="index"
/> />
</div> </div>
<router-link <router-link :to="topicRoute" class="button"> Alle {{ $flavor.textModules }} anzeigen </router-link>
:to="topicRoute"
class="button"
>
Alle {{ $flavor.textModules }} anzeigen
</router-link>
</div> </div>
<div class="start-page__news news" data-cy="news-teasers" v-if="!me.readOnly"> <div class="start-page__news news" data-cy="news-teasers" v-if="!me.readOnly">
<h2 class="start-page__heading">News</h2> <h2 class="start-page__heading">News</h2>

View File

@ -14,8 +14,7 @@
<script> <script>
import '@/styles/survey.modern.css'; import '@/styles/survey.modern.css';
import '@/styles/survey.reset.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 , StylesManager } from 'survey-knockout';
// 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)
@ -35,7 +34,8 @@ const Solution = defineAsyncComponent(() =>
*/ '@/components/content-blocks/Solution' */ '@/components/content-blocks/Solution'
) )
); );
StylesManager.applyTheme('modern'); StylesManager.applyTheme('modern')
);
const MODULE_QUERY = gql` const MODULE_QUERY = gql`
query ModuleSolutions($slug: String) { query ModuleSolutions($slug: String) {
@ -56,8 +56,7 @@ export default {
return { return {
survey: this.initSurvey(), survey: this.initSurvey(),
currentPage: null, currentPage: null,
surveyData: null, surveyData: null,title: '',
title: '',
module: {}, module: {},
completed: false, completed: false,
me: { me: {
@ -115,15 +114,14 @@ export default {
} }
}, },
destroyed() {}, destroyed() {},methods: {
methods: {
initSurvey(data, answers) { initSurvey(data, answers) {
let survey = new Model(data); let survey = new Model(data);
const flatAnswers = {}; const flatAnswers = {};
for (let k in answers) { for (let k in answers) {
flatAnswers[k] = answers[k].answer; flatAnswers[k] = answers[k].answer;
} }
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;
@ -215,6 +213,7 @@ export default {
survey.locale = 'de'; survey.locale = 'de';
survey.showProgressBar = 'bottom'; survey.showProgressBar = 'bottom';
survey.pageNextText = 'Speichern & Weiter'; survey.pageNextText = 'Speichern & Weiter';
survey.render('survey'); survey.render('survey');
return survey; return survey;
}, },

View File

@ -5,21 +5,14 @@
</div> </div>
<div class="topic__content"> <div class="topic__content">
<h1 <h1 data-cy="topic-title" class="topic__title">
data-cy="topic-title"
class="topic__title"
>
{{ topic.title }} {{ topic.title }}
</h1> </h1>
<p class="topic__teaser"> <p class="topic__teaser">
{{ topic.teaser }} {{ topic.teaser }}
</p> </p>
<div class="topic__links"> <div class="topic__links">
<div <div class="topic__video-link topic__link" v-if="topic.vimeoId" @click="openVideo">
class="topic__video-link topic__link"
v-if="topic.vimeoId"
@click="openVideo"
>
<play-icon class="topic__video-link-icon topic__link-icon" /> <play-icon class="topic__video-link-icon topic__link-icon" />
<span class="topic__link-description">Video schauen</span> <span class="topic__link-description">Video schauen</span>
</div> </div>
@ -34,10 +27,7 @@
</a> </a>
</div> </div>
<div class="topic__modules"> <div class="topic__modules">
<module-teaser <module-teaser v-for="module in modules" v-bind="module" :key="module.slug"
v-for="module in modules"
v-bind="module"
:key="module.slug"
/> />
</div> </div>
</div> </div>
@ -57,7 +47,6 @@
const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon')); const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon'));
export default { export default {
mixins: [me], mixins: [me],
components: { components: {
TopicNavigation, TopicNavigation,
@ -104,7 +93,8 @@
}, },
mounted() { mounted() {
if (!this.topic.id) { // component was loaded before topic, apollo not ready yet if (!this.topic.id) {
// component was loaded before topic, apollo not ready yet
this.saveMe = true; // needs saving, apollo will do this this.saveMe = true; // needs saving, apollo will do this
} else { } else {
this.updateLastVisitedTopic(this.topic.id); this.updateLastVisitedTopic(this.topic.id);
@ -126,7 +116,14 @@
id: topicId, id: topicId,
}, },
}, },
update(store, {data: {updateLastTopic: {topic}}}) { update(
store,
{
data: {
updateLastTopic: { topic },
},
}
) {
if (topic) { if (topic) {
const query = ME_QUERY; const query = ME_QUERY;
const { me } = store.readQuery({ query }); const { me } = store.readQuery({ query });
@ -148,8 +145,8 @@
</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';
.topic { .topic {
display: grid; display: grid;

View File

@ -18,19 +18,19 @@ class ModalStore {
} }
interface Modal { interface Modal {
state: any, state: any;
component: string, component: string;
payload?: any, payload?: any;
confirm: (res: any) => void, confirm: (res: any) => void;
open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>, open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>;
cancel: () => void, cancel: () => void;
_resolve: (r?: any) => any, _resolve: (r?: any) => any;
_reject: (r?: any) => any _reject: (r?: any) => any;
} }
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface ComponentCustomProperties { interface ComponentCustomProperties {
$modal: Modal $modal: Modal;
} }
} }

View File

@ -1,4 +1,4 @@
import {RouteLocationNormalized} from "vue-router"; import { RouteLocationNormalized } from 'vue-router';
function getCookieValue(cookieName: string) { function getCookieValue(cookieName: string) {
// https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript // https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript

View File

@ -74,7 +74,7 @@ const routes = [
}, },
{ {
path: '/oauth-redirect', path: '/oauth-redirect',
redirect: to => { redirect: (to) => {
switch (to.query.state) { switch (to.query.state) {
case EMAIL_NOT_VERIFIED_STATE: case EMAIL_NOT_VERIFIED_STATE:
return '/verify-email'; return '/verify-email';
@ -96,11 +96,11 @@ const routes = [
{ {
path: '/not-found', path: '/not-found',
name: 'not-found', name: 'not-found',
...notFoundRoute ...notFoundRoute,
}, },
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
...notFoundRoute ...notFoundRoute,
}, },
]; ];

View File

@ -10,4 +10,3 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any>; const component: DefineComponent<{}, {}, any>;
export default component; export default component;
} }

View File

@ -92,7 +92,13 @@
height: 100%; height: 100%;
> span { > span {
position: absolute;
width: 100%;
text-align: right;
bottom: -2em;
@include regular-text;
} }
} }
&__error { &__error {

Some files were not shown because too many files have changed in this diff Show More