Merge branch 'develop'
This commit is contained in:
commit
ffd87e57bf
|
|
@ -12,7 +12,7 @@
|
|||
@edit-note="editNote"
|
||||
@bookmark="bookmark(!chapter.bookmark)"
|
||||
/>
|
||||
<p class="chapter__description">
|
||||
<p class="chapter__description intro">
|
||||
{{ chapter.description }}
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
grid-template-columns: 1fr 1fr 1fr;
|
||||
|
||||
@include desktop {
|
||||
grid-template-columns: 50px 1fr 200px;
|
||||
grid-template-columns: 50px 1fr auto;
|
||||
grid-template-rows: 50px;
|
||||
grid-auto-rows: auto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<p
|
||||
class="solution__text solution-text fade"
|
||||
data-cy="solution-text"
|
||||
|
||||
v-if="visible"
|
||||
v-html="value.text"/>
|
||||
</transition>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
@edit-note="editNote"
|
||||
@bookmark="bookmark(!module.bookmark)"/>
|
||||
<div
|
||||
class="module__intro"
|
||||
class="module__intro intro"
|
||||
v-html="module.intro"/>
|
||||
</div>
|
||||
|
||||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
<objective-groups :groups="societyObjectiveGroups"/>
|
||||
|
||||
<objective-groups :groups="interdisciplinaryObjectiveGroups"/>
|
||||
|
||||
<chapter
|
||||
:chapter="chapter"
|
||||
:index="index"
|
||||
|
|
@ -86,6 +88,11 @@
|
|||
.filter(group => group.title === 'SOCIETY')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
},
|
||||
interdisciplinaryObjectiveGroups() {
|
||||
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||
.filter(group => group.title === 'INTERDISCIPLINARY')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
},
|
||||
isStudent() {
|
||||
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
<img
|
||||
:src="teaser.imageUrl"
|
||||
class="news-teaser__image">
|
||||
<p class="news-teaser__image-source">
|
||||
<a
|
||||
:href="teaser.imageSource"
|
||||
class="tiny-text">Quelle {{ teaser.imageSource }}</a></p>
|
||||
<a
|
||||
:href="teaser.imageSource"
|
||||
class="news-teaser__image-source">Quelle {{ teaser.imageSource }}</a>
|
||||
|
||||
<h4 class="news-teaser__title">{{ teaser.title }}</h4>
|
||||
<p class="news-teaser__description">{{ teaser.description }}</p>
|
||||
<p class="news-teaser__date">{{ teaser.displayDate }}</p>
|
||||
|
|
@ -16,52 +16,54 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
teaser: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
export default {
|
||||
props: {
|
||||
teaser: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_functions.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_functions.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
|
||||
.news-teaser {
|
||||
position: relative;
|
||||
padding-bottom: $large-spacing;
|
||||
.news-teaser {
|
||||
position: relative;
|
||||
|
||||
&__image {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
&__image {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
@include desktop {
|
||||
max-width: $news_width;
|
||||
}
|
||||
}
|
||||
|
||||
&__image-source {
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-bottom: $large-spacing;
|
||||
}
|
||||
|
||||
&__date {
|
||||
font-family: $sans-serif-font-family;
|
||||
font-weight: $font-weight-regular;
|
||||
color: $color-silver-dark;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@include desktop {
|
||||
max-width: $news-width;
|
||||
}
|
||||
}
|
||||
|
||||
&__image-source {
|
||||
line-height: 25px;
|
||||
@include tiny-text;
|
||||
margin-bottom: $medium-spacing;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-bottom: $small-spacing;
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-bottom: $small-spacing;
|
||||
}
|
||||
|
||||
&__date {
|
||||
@include regular-text;
|
||||
color: $color-silver-dark;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -35,14 +35,15 @@
|
|||
display: grid;
|
||||
}
|
||||
margin-bottom: $large-spacing;
|
||||
grid-gap: 40px;
|
||||
grid-gap: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
grid-column-gap: 40px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, $news_width));
|
||||
grid-column-gap: $large-spacing;
|
||||
grid-row-gap: $section-spacing;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, $news-width));
|
||||
grid-auto-rows: minmax(400px, auto);
|
||||
grid-template-rows: auto auto;
|
||||
-ms-grid-columns: $news_width $news_width;
|
||||
-ms-grid-columns: $news-width $news-width;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
<div>
|
||||
{{ objective.text }}
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@
|
|||
margin-bottom: 25px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
break-inside: avoid;
|
||||
break-inside: avoid-page;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
display: none;
|
||||
@include desktop {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
import CurrentClass from '@/components/school-class/CurrentClass';
|
||||
import AddIcon from '@/components/icons/AddIcon';
|
||||
|
||||
import updateSelectedClassMixin from '@/mixins/updateSelectedClass';
|
||||
import updateSelectedClassMixin from '@/mixins/update-selected-class';
|
||||
import sidebarMixin from '@/mixins/sidebar';
|
||||
import meMixin from '@/mixins/me';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
fragment InstrumentParts on InstrumentNode {
|
||||
id
|
||||
title
|
||||
intro
|
||||
slug
|
||||
bookmarks {
|
||||
uuid
|
||||
|
|
|
|||
|
|
@ -1,5 +1,19 @@
|
|||
const extractAnswerFromQuestion = (previous, question) => {
|
||||
return [...previous, {title: question.title, answer: question.correctAnswer}];
|
||||
let answer = question.correctAnswer;
|
||||
if (question.getType() === 'matrix') {
|
||||
const correctAnswer = question.correctAnswer;
|
||||
const questionRows = question.getRows();
|
||||
const keys = questionRows.map(question => {
|
||||
const text = question.value;
|
||||
if (/[,.!?]/.test(text.slice(-1))) {
|
||||
return text.slice(0, -1);
|
||||
}
|
||||
return text;
|
||||
}); // get the keys as they appear in the question, without punctuation at the end
|
||||
|
||||
answer = keys.map(key => `${key}: ${correctAnswer[key]}`); // return an array, it gets converted to a string further up
|
||||
}
|
||||
return [...previous, {title: question.title, answer, type: question.getType()}];
|
||||
};
|
||||
|
||||
export const extractSurveySolutions = (prev, element) => {
|
||||
|
|
|
|||
|
|
@ -79,16 +79,33 @@
|
|||
max-width: $footer-width;
|
||||
padding: 2*$large-spacing 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
@include desktop {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
&__who-are-we {
|
||||
width: 330px;
|
||||
width: 100%;
|
||||
margin-bottom: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
width: 330px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-hep {
|
||||
width: 147px;
|
||||
width: auto;
|
||||
height: 35px;
|
||||
margin-bottom: $large-spacing;
|
||||
|
||||
@include desktop {
|
||||
width: 147px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-ehb {
|
||||
|
|
@ -100,11 +117,22 @@
|
|||
width: 100%;
|
||||
max-width: $footer-width;
|
||||
padding: $large-spacing 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@include desktop {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
@include aside-with-cheese;
|
||||
margin-right: $large-spacing;
|
||||
margin-bottom: $small-spacing;
|
||||
|
||||
@include desktop {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
<div class="instrument">
|
||||
<h1 class="instrument__title">{{ instrument.title }}</h1>
|
||||
|
||||
<div
|
||||
class="instrument__intro intro"
|
||||
v-html="instrument.intro"/>
|
||||
|
||||
<content-component
|
||||
:key="component.id"
|
||||
:component="component"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<script>
|
||||
import OLD_CLASSES_QUERY from '@/graphql/gql/oldClasses.gql';
|
||||
|
||||
import updateSelectedClassMixin from '@/mixins/updateSelectedClass';
|
||||
import updateSelectedClassMixin from '@/mixins/update-selected-class';
|
||||
|
||||
export default {
|
||||
mixins: [updateSelectedClassMixin],
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@
|
|||
}
|
||||
|
||||
&__modules {
|
||||
margin-bottom: $large-spacing;
|
||||
margin-bottom: $section-spacing;
|
||||
}
|
||||
|
||||
&__modules-list {
|
||||
|
|
@ -201,6 +201,10 @@
|
|||
-ms-grid-column: 3;
|
||||
}
|
||||
}
|
||||
|
||||
&__news {
|
||||
margin-bottom: $section-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
.news {
|
||||
|
|
|
|||
|
|
@ -15,207 +15,206 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import * as SurveyVue from 'survey-vue';
|
||||
import {css} from '@/survey.config';
|
||||
import * as SurveyVue from 'survey-vue';
|
||||
import {css} from '@/survey.config';
|
||||
|
||||
import SURVEY_QUERY from '@/graphql/gql/surveyQuery.gql';
|
||||
import MODULE_QUERY from '@/graphql/gql/moduleByIdQuery.gql';
|
||||
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
|
||||
import Solution from '@/components/content-blocks/Solution';
|
||||
import SURVEY_QUERY from '@/graphql/gql/surveyQuery.gql';
|
||||
import MODULE_QUERY from '@/graphql/gql/moduleByIdQuery.gql';
|
||||
import UPDATE_ANSWER from '@/graphql/gql/mutations/updateAnswer.gql';
|
||||
import Solution from '@/components/content-blocks/Solution';
|
||||
|
||||
import {extractSurveySolutions} from '@/helpers/survey-solutions';
|
||||
import {isTeacher} from '@/helpers/is-teacher';
|
||||
import {extractSurveySolutions} from '@/helpers/survey-solutions';
|
||||
import {isTeacher} from '@/helpers/is-teacher';
|
||||
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
|
||||
const Survey = SurveyVue.Survey;
|
||||
const Survey = SurveyVue.Survey;
|
||||
|
||||
export default {
|
||||
props: ['id'],
|
||||
export default {
|
||||
props: ['id'],
|
||||
|
||||
components: {
|
||||
Solution,
|
||||
Survey
|
||||
components: {
|
||||
Solution,
|
||||
Survey
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
survey: this.initSurvey(),
|
||||
title: '',
|
||||
module: {},
|
||||
completed: false,
|
||||
me: {
|
||||
permissions: []
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
surveyComplete() {
|
||||
return this.survey && this.survey.isCompleted;
|
||||
},
|
||||
|
||||
data() {
|
||||
showSolution() {
|
||||
return (module.solutionsEnabled || isTeacher) && !this.survey.isCompleted;
|
||||
},
|
||||
solution() {
|
||||
// todo: should this be done inside of Solution.vue?
|
||||
return {
|
||||
survey: this.initSurvey(),
|
||||
title: '',
|
||||
module: {},
|
||||
completed: false,
|
||||
me: {
|
||||
permissions: []
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
surveyComplete() {
|
||||
return this.survey && this.survey.isCompleted;
|
||||
},
|
||||
showSolution() {
|
||||
return (module.solutionsEnabled || isTeacher) && !this.survey.isCompleted;
|
||||
},
|
||||
solution() {
|
||||
return {
|
||||
text: this.answers.reduce((previous, answer) => {
|
||||
if (!answer.answer) {
|
||||
return previous;
|
||||
}
|
||||
let answerText;
|
||||
if (typeof answer.answer === 'object') {
|
||||
// this means the answer comes from a matrix, where the keys are the labels and the values are the respective answers
|
||||
let answerObject = answer.answer;
|
||||
let keysAndValues = [];
|
||||
for (let prop of Object.keys(answerObject)) {
|
||||
keysAndValues.push(`${prop}: ${answerObject[prop]}`);
|
||||
}
|
||||
answerText = keysAndValues.join(', ');
|
||||
} else {
|
||||
answerText = answer.answer;
|
||||
}
|
||||
text: this.answers.reduce((previous, answer) => {
|
||||
if (!answer.answer) {
|
||||
return previous;
|
||||
}
|
||||
if (answer.type === 'matrix') {
|
||||
// wrap all the answers inside li tags and convert to a single string
|
||||
const answerText = answer.answer.map(a => `<li class="solution-text__list-item">${a}</li>`).join('');
|
||||
return `
|
||||
${previous}
|
||||
<h2 class="solution-text__heading">${answer.title}</h2>
|
||||
<ul class="solution-text__answer solution-text__list">${answerText}</ul>
|
||||
`;
|
||||
} else {
|
||||
return `
|
||||
${previous}
|
||||
<h2 class="solution-text__heading">${answer.title}</h2>
|
||||
<p class="solution-text__answer">${answerText}</p>
|
||||
<p class="solution-text__answer">${answer.answer}</p>
|
||||
`;
|
||||
}, '')
|
||||
};
|
||||
},
|
||||
answers() {
|
||||
return this.survey.currentPage && this.survey.currentPage.elements
|
||||
? this.survey.currentPage.elements.reduce(extractSurveySolutions, [])
|
||||
: [];
|
||||
},
|
||||
isTeacher() {
|
||||
return isTeacher(this);
|
||||
}
|
||||
}
|
||||
}, '')
|
||||
};
|
||||
},
|
||||
answers() {
|
||||
return this.survey.currentPage && this.survey.currentPage.elements
|
||||
? this.survey.currentPage.elements.reduce(extractSurveySolutions, [])
|
||||
: [];
|
||||
},
|
||||
isTeacher() {
|
||||
return isTeacher(this);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
initSurvey(data, answers) {
|
||||
let survey = new SurveyVue.Model(data);
|
||||
const flatAnswers = {};
|
||||
for (let k in answers) {
|
||||
flatAnswers[k] = answers[k].answer;
|
||||
methods: {
|
||||
initSurvey(data, answers) {
|
||||
let survey = new SurveyVue.Model(data);
|
||||
const flatAnswers = {};
|
||||
for (let k in answers) {
|
||||
flatAnswers[k] = answers[k].answer;
|
||||
}
|
||||
survey.data = flatAnswers;
|
||||
|
||||
const saveSurvey = (sender, options) => {
|
||||
// sender.clear(false);
|
||||
//
|
||||
// sender.mode = 'display';
|
||||
|
||||
this.completed = true;
|
||||
|
||||
const data = {};
|
||||
|
||||
for (let k in survey.data) {
|
||||
if (survey.data.hasOwnProperty(k)) {
|
||||
let question = sender.getQuestionByName(k);
|
||||
data[k] = {
|
||||
answer: survey.data[k],
|
||||
correct: question && question.correctAnswer ? question.correctAnswer : ''
|
||||
};
|
||||
}
|
||||
}
|
||||
survey.data = flatAnswers;
|
||||
|
||||
const saveSurvey = (sender, options) => {
|
||||
// sender.clear(false);
|
||||
//
|
||||
// sender.mode = 'display';
|
||||
|
||||
this.completed = true;
|
||||
|
||||
const data = {};
|
||||
|
||||
for (let k in survey.data) {
|
||||
if (survey.data.hasOwnProperty(k)) {
|
||||
let question = sender.getQuestionByName(k);
|
||||
data[k] = {
|
||||
answer: survey.data[k],
|
||||
correct: question && question.correctAnswer ? question.correctAnswer : ''
|
||||
};
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_ANSWER,
|
||||
variables: {
|
||||
input: {
|
||||
answer: {
|
||||
surveyId: this.id,
|
||||
data: JSON.stringify(data)
|
||||
}
|
||||
}
|
||||
},
|
||||
// fixme: make the update work instead of refetching
|
||||
update: (store, {data: {updateAnswer: {answer}}}) => {
|
||||
const query = SURVEY_QUERY;
|
||||
const variables = {id: this.id};
|
||||
const queryData = store.readQuery({query, variables});
|
||||
if (queryData.survey) {
|
||||
queryData.survey.answer = answer;
|
||||
store.writeQuery({query, variables, data: queryData});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_ANSWER,
|
||||
survey.onComplete.add(saveSurvey);
|
||||
survey.onCurrentPageChanged.add(saveSurvey);
|
||||
|
||||
survey.css = css;
|
||||
survey.locale = 'de';
|
||||
survey.showProgressBar = 'bottom';
|
||||
survey.pageNextText = 'Speichern & Weiter';
|
||||
return survey;
|
||||
},
|
||||
reopen() {
|
||||
this.completed = false;
|
||||
let data = this.survey.data; // save the data
|
||||
this.survey.clear();
|
||||
this.survey.data = data; // reapply it
|
||||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
survey: {
|
||||
query: SURVEY_QUERY,
|
||||
variables() {
|
||||
return {
|
||||
id: this.id
|
||||
};
|
||||
},
|
||||
manual: true,
|
||||
result({data, loading, networkStatus}) {
|
||||
if (!loading) {
|
||||
let json = JSON.parse(data.survey.data);
|
||||
json.showTitle = false;
|
||||
let answer = {};
|
||||
if (data.survey.answer && data.survey.answer.data) {
|
||||
answer = JSON.parse(data.survey.answer.data);
|
||||
}
|
||||
|
||||
if (!this.completed) {
|
||||
this.survey = this.initSurvey(json, answer);
|
||||
}
|
||||
this.title = json.title;
|
||||
const module = data.survey.module;
|
||||
|
||||
this.$apollo.addSmartQuery('module', {
|
||||
query: MODULE_QUERY,
|
||||
variables: {
|
||||
input: {
|
||||
answer: {
|
||||
surveyId: this.id,
|
||||
data: JSON.stringify(data)
|
||||
}
|
||||
}
|
||||
},
|
||||
// fixme: make the update work instead of refetching
|
||||
update: (store, {data: {updateAnswer: {answer}}}) => {
|
||||
const query = SURVEY_QUERY;
|
||||
const variables = {id: this.id};
|
||||
const queryData = store.readQuery({query, variables});
|
||||
if (queryData.survey) {
|
||||
queryData.survey.answer = answer;
|
||||
store.writeQuery({query, variables, data: queryData});
|
||||
}
|
||||
id: module.id
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
survey.onComplete.add(saveSurvey);
|
||||
survey.onCurrentPageChanged.add(saveSurvey);
|
||||
|
||||
survey.css = css;
|
||||
survey.locale = 'de';
|
||||
survey.showProgressBar = 'bottom';
|
||||
survey.pageNextText = 'Speichern & Weiter';
|
||||
return survey;
|
||||
}
|
||||
},
|
||||
reopen() {
|
||||
this.completed = false;
|
||||
let data = this.survey.data; // save the data
|
||||
this.survey.clear();
|
||||
this.survey.data = data; // reapply it
|
||||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
survey: {
|
||||
query: SURVEY_QUERY,
|
||||
variables() {
|
||||
return {
|
||||
id: this.id
|
||||
};
|
||||
},
|
||||
manual: true,
|
||||
result({data, loading, networkStatus}) {
|
||||
if (!loading) {
|
||||
let json = JSON.parse(data.survey.data);
|
||||
json.showTitle = false;
|
||||
let answer = {};
|
||||
if (data.survey.answer && data.survey.answer.data) {
|
||||
answer = JSON.parse(data.survey.answer.data);
|
||||
}
|
||||
|
||||
if (!this.completed) {
|
||||
this.survey = this.initSurvey(json, answer);
|
||||
}
|
||||
this.title = json.title;
|
||||
const module = data.survey.module;
|
||||
|
||||
this.$apollo.addSmartQuery('module', {
|
||||
query: MODULE_QUERY,
|
||||
variables: {
|
||||
id: module.id
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
me: meQuery
|
||||
}
|
||||
};
|
||||
me: meQuery
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
@import "@/styles/_variables.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
|
||||
.survey-page {
|
||||
max-width: 800px;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-auto-rows: auto;
|
||||
grid-row-gap: $large-spacing;
|
||||
justify-self: center;
|
||||
padding: 100px 0;
|
||||
width: 100%;
|
||||
.survey-page {
|
||||
max-width: 800px;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-auto-rows: auto;
|
||||
grid-row-gap: $large-spacing;
|
||||
justify-self: center;
|
||||
padding: 100px 0;
|
||||
width: 100%;
|
||||
|
||||
&__title {
|
||||
@include meta-title;
|
||||
margin: 0;
|
||||
}
|
||||
&__title {
|
||||
@include meta-title;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
@supports (display: grid) {
|
||||
display: grid;
|
||||
}
|
||||
grid-template-rows: auto 1fr auto;
|
||||
grid-template-rows: auto 1fr max-content;
|
||||
grid-template-areas: "h" "c" "f";
|
||||
min-height: 100vh;
|
||||
grid-auto-rows: 1fr;
|
||||
|
|
@ -46,9 +46,27 @@
|
|||
grid-area: h;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 0 $small-spacing;
|
||||
|
||||
@include desktop {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__footer {
|
||||
grid-area: f;
|
||||
// we usually set the margin to the bottom and the right, but here we want the footer to always have
|
||||
// this margin, and we don't want to set it on every content element. And we don't want to set it on
|
||||
// the content element, for if there's no footer.
|
||||
margin-top: 3*$large-spacing;
|
||||
margin-bottom: -3*$large-spacing;
|
||||
|
||||
padding: 0 $small-spacing;
|
||||
|
||||
@include desktop {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
.intro {
|
||||
@include lead-paragraph;
|
||||
|
||||
> p {
|
||||
@include lead-paragraph;
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
}
|
||||
&--blue {
|
||||
background-color: $color-accent-2;
|
||||
|
||||
& .widget-footer {
|
||||
background-color: $color-accent-2-dark;
|
||||
}
|
||||
|
|
@ -44,12 +45,14 @@
|
|||
}
|
||||
&--red {
|
||||
background-color: $color-accent-3;
|
||||
|
||||
& .widget-footer {
|
||||
background-color: $color-accent-3-dark;
|
||||
}
|
||||
}
|
||||
&--green {
|
||||
background-color: $color-accent-4;
|
||||
|
||||
& .widget-footer {
|
||||
background-color: $color-accent-4-dark;
|
||||
}
|
||||
|
|
@ -118,6 +121,13 @@
|
|||
font-size: toRem(14px);
|
||||
}
|
||||
|
||||
@mixin tiny-text {
|
||||
font-size: toRem(11px);
|
||||
font-family: $sans-serif-font-family;
|
||||
font-weight: $font-weight-regular;
|
||||
color: $color-silver-dark;
|
||||
}
|
||||
|
||||
@mixin aside-text {
|
||||
@include regular-text;
|
||||
font-size: toRem(14px);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
.room {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
margin-bottom: -50px;
|
||||
grid-template-rows: max-content 1fr;
|
||||
margin-bottom: -90px;
|
||||
|
||||
&__header {
|
||||
padding: 30px;
|
||||
|
|
|
|||
|
|
@ -8,4 +8,14 @@
|
|||
@include regular-text;
|
||||
margin-bottom: $medium-spacing;
|
||||
}
|
||||
|
||||
&__list {
|
||||
list-style: disc;
|
||||
padding-left: $medium-spacing;
|
||||
}
|
||||
|
||||
&__list-item {
|
||||
@include inline-title;
|
||||
margin-bottom: $medium-spacing;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,3 @@ input, textarea, select, button {
|
|||
color: $color-brand-dark;
|
||||
}
|
||||
|
||||
.tiny-text {
|
||||
font-size: toRem(11px);
|
||||
font-family: $sans-serif-font-family;
|
||||
font-weight: $font-weight-regular;
|
||||
color: $color-silver-dark;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ $default-padding: 30px;
|
|||
$small-spacing: 10px;
|
||||
$medium-spacing: 20px;
|
||||
$large-spacing: 30px;
|
||||
$section-spacing: 60px;
|
||||
|
||||
$font-weight-bold: 700;
|
||||
$font-weight-semibold: 600;
|
||||
|
|
@ -77,4 +78,4 @@ $default-heading-line-height: 1.2;
|
|||
$popover-default-bottom: -110px;
|
||||
|
||||
$footer-width: 800px;
|
||||
$news_width: 550px;
|
||||
$news-width: 550px;
|
||||
|
|
|
|||
|
|
@ -27,3 +27,4 @@
|
|||
@import "simple-list";
|
||||
@import "widget-popover";
|
||||
@import "toast";
|
||||
@import "intro";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.12 on 2020-09-29 07:54
|
||||
|
||||
from django.db import migrations
|
||||
import wagtail.core.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('basicknowledge', '0006_auto_20200520_0954'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='basicknowledge',
|
||||
name='intro',
|
||||
field=wagtail.core.fields.RichTextField(blank=True, default=''),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
from django.db import models
|
||||
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
|
||||
from wagtail.core.fields import StreamField
|
||||
from wagtail.core.fields import StreamField, RichTextField
|
||||
from wagtail.images.blocks import ImageChooserBlock
|
||||
|
||||
from books.blocks import LinkBlock, VideoBlock, DocumentBlock, SectionTitleBlock, InfogramBlock, \
|
||||
GeniallyBlock, InstrumentTextBlock, SubtitleBlock, ThinglinkBlock
|
||||
GeniallyBlock, InstrumentTextBlock, SubtitleBlock, ThinglinkBlock, DEFAULT_RICH_TEXT_FEATURES
|
||||
from core.wagtail_utils import StrictHierarchyPage
|
||||
|
||||
|
||||
class BasicKnowledge(StrictHierarchyPage):
|
||||
parent_page_types = ['books.book']
|
||||
|
||||
intro = RichTextField(features=DEFAULT_RICH_TEXT_FEATURES, default='', blank=True)
|
||||
|
||||
contents = StreamField([
|
||||
('text_block', InstrumentTextBlock()),
|
||||
('image_block', ImageChooserBlock()),
|
||||
|
|
@ -42,6 +44,7 @@ class BasicKnowledge(StrictHierarchyPage):
|
|||
content_panels = [
|
||||
FieldPanel('title', classname="full title"),
|
||||
FieldPanel('type'),
|
||||
FieldPanel('intro'),
|
||||
StreamFieldPanel('contents')
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class InstrumentNode(DjangoObjectType):
|
|||
filter_fields = ['slug', 'type']
|
||||
interfaces = (relay.Node,)
|
||||
only_fields = [
|
||||
'slug', 'title', 'type', 'contents',
|
||||
'slug', 'title', 'intro', 'type', 'contents',
|
||||
]
|
||||
|
||||
def resolve_bookmarks(self, info, **kwargs):
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class ObjectiveGroupAdmin(admin.ModelAdmin):
|
|||
|
||||
@admin.register(Objective)
|
||||
class ObjectiveAdmin(admin.ModelAdmin):
|
||||
list_display = ('text', 'get_topic', 'group', 'owner')
|
||||
list_display = ('text', 'get_topic', 'group', 'order', 'owner')
|
||||
list_filter = ('group', 'owner')
|
||||
|
||||
def get_topic(self, obj):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.14 on 2020-09-28 15:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('objectives', '0008_auto_20190821_1252'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='objectivegroup',
|
||||
name='title',
|
||||
field=models.CharField(blank=True, choices=[('language_communication', 'Sprache & Kommunikation'), ('society', 'Gesellschaft'), ('interdisciplinary', 'Überfachliche Lernziele')], default='language_communication', max_length=255, verbose_name='title'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.2.14 on 2020-09-30 13:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.expressions
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('objectives', '0009_auto_20200928_1547'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='objective',
|
||||
options={'ordering': [django.db.models.expressions.OrderBy(django.db.models.expressions.F('owner'), nulls_first=True), django.db.models.expressions.OrderBy(django.db.models.expressions.F('order'), nulls_last=True)], 'verbose_name': 'Lernziel', 'verbose_name_plural': 'Lernziele'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='objective',
|
||||
name='order',
|
||||
field=models.IntegerField(null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.db.models import F
|
||||
|
||||
from books.models import Module
|
||||
from users.models import SchoolClass
|
||||
|
|
@ -12,10 +13,12 @@ class ObjectiveGroup(models.Model):
|
|||
|
||||
LANGUAGE_COMMUNICATION = 'language_communication'
|
||||
SOCIETY = 'society'
|
||||
INTERDISCIPLINARY = 'interdisciplinary'
|
||||
|
||||
TITLE_CHOICES = (
|
||||
(LANGUAGE_COMMUNICATION, 'Sprache & Kommunikation'),
|
||||
(SOCIETY, 'Gesellschaft'),
|
||||
(INTERDISCIPLINARY, 'Überfachliche Lernziele'),
|
||||
)
|
||||
|
||||
title = models.CharField('title', blank=True, null=False, max_length=255, choices=TITLE_CHOICES, default=LANGUAGE_COMMUNICATION)
|
||||
|
|
@ -34,6 +37,7 @@ class Objective(models.Model):
|
|||
class Meta:
|
||||
verbose_name = 'Lernziel'
|
||||
verbose_name_plural = 'Lernziele'
|
||||
ordering = [F('owner').asc(nulls_first=True), F('order').asc(nulls_last=True)]
|
||||
|
||||
text = models.CharField('text', blank=True, null=False, max_length=255)
|
||||
group = models.ForeignKey(ObjectiveGroup, blank=False, null=False, on_delete=models.CASCADE,
|
||||
|
|
@ -41,6 +45,7 @@ class Objective(models.Model):
|
|||
owner = models.ForeignKey(get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
|
||||
hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_objectives', blank=True)
|
||||
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_objectives', blank=True)
|
||||
order = models.IntegerField(null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return 'Objective {}-{}'.format(self.id, self.text)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
from django.test import TestCase, RequestFactory
|
||||
from graphene.test import Client
|
||||
from graphql_relay import to_global_id
|
||||
|
||||
from api.schema import schema
|
||||
from api.utils import get_object, get_graphql_mutation
|
||||
from books.models import ContentBlock, Chapter
|
||||
from books.factories import ModuleFactory
|
||||
from core.factories import UserFactory
|
||||
from core.management.commands import create_teacher
|
||||
from notes.factories import ChapterBookmarkFactory, ModuleBookmarkFactory
|
||||
from objectives.factories import ObjectiveGroupFactory
|
||||
from objectives.models import Objective
|
||||
from users.models import User
|
||||
from users.services import create_users
|
||||
|
||||
|
||||
class ObjectiveOrderTestCase(TestCase):
|
||||
def setUp(self):
|
||||
create_users()
|
||||
|
||||
self.user = user = User.objects.get(username='teacher')
|
||||
|
||||
self.objective_group = ObjectiveGroupFactory(owner=None)
|
||||
|
||||
|
||||
request = RequestFactory().get('/')
|
||||
request.user = user
|
||||
|
||||
self.client = Client(schema=schema, context_value=request)
|
||||
|
||||
Objective.objects.create(owner=None, text='first', group=self.objective_group, order=0)
|
||||
Objective.objects.create(owner=None, text='second', group=self.objective_group, order=1)
|
||||
Objective.objects.create(owner=None, text='third', group=self.objective_group)
|
||||
Objective.objects.create(owner=user, text='fourth', group=self.objective_group)
|
||||
|
||||
def test_objective_order(self):
|
||||
query = """
|
||||
query ObjectiveGroupQuery($id: ID!) {
|
||||
objectiveGroup(id: $id) {
|
||||
objectives {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
result = self.client.execute(query, variables={
|
||||
'id': to_global_id('ObjectiveGroupNode', self.objective_group.pk)
|
||||
})
|
||||
|
||||
self.assertIsNone(result.get('errors'))
|
||||
objective_nodes = result.get('data').get('objectiveGroup').get('objectives').get('edges')
|
||||
|
||||
objective1, objective2, objective3, objective4 = [node['node'] for node in objective_nodes]
|
||||
|
||||
self.assertEqual(objective1.get('text'), 'first')
|
||||
self.assertEqual(objective2.get('text'), 'second')
|
||||
self.assertEqual(objective3.get('text'), 'third')
|
||||
self.assertEqual(objective4.get('text'), 'fourth')
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.14 on 2020-09-28 15:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0023_user_onboarding_visited'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='license',
|
||||
name='isbn',
|
||||
field=models.CharField(default='978-3-0355-1397-4', max_length=50),
|
||||
),
|
||||
]
|
||||
Loading…
Reference in New Issue