From 946b42f1e8d365157c8c60fd799510330d683a62 Mon Sep 17 00:00:00 2001 From: Jacob Pierce Date: Fri, 31 May 2024 15:29:37 -0700 Subject: [PATCH] drop-down section and question counter on mobile exampage --- .../assets/src/modules/examViewer/index.js | 27 +++++++++ .../learn/assets/src/views/ExamPage/index.vue | 58 ++++++++++++++++--- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/kolibri/plugins/learn/assets/src/modules/examViewer/index.js b/kolibri/plugins/learn/assets/src/modules/examViewer/index.js index 0de222c8b5e..0b70d9d0a60 100644 --- a/kolibri/plugins/learn/assets/src/modules/examViewer/index.js +++ b/kolibri/plugins/learn/assets/src/modules/examViewer/index.js @@ -18,4 +18,31 @@ export default { Object.assign(state, defaultState()); }, }, + getters: { + sections(state) { + if (!state.exam.question_sources) return []; + return state.exam.question_sources; + }, + currentQuestion(state) { + if (state.questions.length === 0) return null; + return state.questions[state.questionNumber]; + }, + currentSection(state, { currentQuestion, sections }) { + return sections.find(section => + section.questions.map(q => q.item).includes(currentQuestion.item) + ); + }, + sectionSelectOptions(state, { sections }) { + return ( + sections.map((section, i) => ({ + label: section.section_title, + value: i, + })) || [] + ); + }, + currentSectionOption(state, { currentSection, sectionSelectOptions }) { + if (!currentSection) return {}; + return sectionSelectOptions.find(opt => opt.label === currentSection.section_title); + }, + }, }; diff --git a/kolibri/plugins/learn/assets/src/views/ExamPage/index.vue b/kolibri/plugins/learn/assets/src/views/ExamPage/index.vue index 95300087e7d..77cb2338fbc 100644 --- a/kolibri/plugins/learn/assets/src/views/ExamPage/index.vue +++ b/kolibri/plugins/learn/assets/src/views/ExamPage/index.vue @@ -16,7 +16,7 @@ - +
- -

+ + +

{{ currentSection.section_title }}

{{ currentSection.description }}

@@ -109,7 +115,13 @@
-

+ +

{{ $tr('question', { num: questionNumber + 1, total: exam.question_count }) }}

- import { mapState } from 'vuex'; + import { mapGetters, mapState } from 'vuex'; import isEqual from 'lodash/isEqual'; import debounce from 'lodash/debounce'; import BottomAppBar from 'kolibri.coreVue.components.BottomAppBar'; @@ -294,14 +306,37 @@ ...mapState({ loading: state => state.core.loading, }), + ...mapGetters('examViewer', [ + 'currentQuestion', + 'currentSection', + 'sectionSelectOptions', + 'currentSectionOption', + 'currentQuestionOption', + ]), ...mapState('examViewer', ['exam', 'contentNodeMap', 'questions', 'questionNumber']), + questionSelectOptions() { + if (!this.currentSection) return { questions: [] }; + return ( + this.currentSection.questions.map((question, i) => ({ + label: this.$tr('question', { + num: i + 1, + total: this.currentSection.questions.length, + }), + value: question.item, + })) || [] + ); + }, + currentQuestionOption() { + if (!this.currentQuestion) return {}; + return this.questionSelectOptions.find(opt => opt.value === this.currentQuestion.item); + }, currentSection() { return this.sections.find(section => section.questions.map(q => q.item).includes(this.currentQuestion.item) ); }, sections() { - return this.exam.question_sources; + return this.exam.question_sources || []; }, /** * @returns {Array} List of all question "item" in the exam - item being a unique identifier @@ -382,7 +417,10 @@ // We generate a special item value to save to the backend that encodes // both the itemId and the nodeId attemptLogItemValue() { - return this.currentQuestion?.item || null; + if (!this.currentQuestion) { + return null; + } + return this.currentQuestion.item; }, questionsAnswered() { return Object.keys( @@ -461,6 +499,12 @@ }); }, methods: { + handleSectionOptionChange(opt) { + this.goToQuestion(this.sections[opt.value].questions[0].item); + }, + handleQuestionOptionChange(opt) { + this.goToQuestion(opt.value); + }, findFirstEl() { this.$refs.resourcePanel.focusFirstEl(); },