diff --git a/kolibri/plugins/coach/assets/src/views/common/TruncatedItemList.vue b/kolibri/plugins/coach/assets/src/views/common/TruncatedItemList.vue index ed4430ca9e4..f27604aa0ae 100644 --- a/kolibri/plugins/coach/assets/src/views/common/TruncatedItemList.vue +++ b/kolibri/plugins/coach/assets/src/views/common/TruncatedItemList.vue @@ -5,17 +5,8 @@ v-else class="items-label" > - - {{ items[0] }} - - - {{ $tr('twoItems', { item1: items[0], item2: items[1] }) }} - - - {{ $tr('threeItems', { item1: items[0], item2: items[1], item3: items[2] }) }} - - - {{ $tr('manyItems', { item1: items[0], item2: items[1], count: items.length - 2 }) }} + + {{ getTruncatedItemsString(items) }} @@ -24,6 +15,8 @@ + + + diff --git a/kolibri/plugins/coach/assets/src/views/common/assignments/SidePanelRecipientsSelector/LearnersSelectorSidePanel.vue b/kolibri/plugins/coach/assets/src/views/common/assignments/SidePanelRecipientsSelector/LearnersSelectorSidePanel.vue new file mode 100644 index 00000000000..9e8670fb20a --- /dev/null +++ b/kolibri/plugins/coach/assets/src/views/common/assignments/SidePanelRecipientsSelector/LearnersSelectorSidePanel.vue @@ -0,0 +1,218 @@ + + + + + + + diff --git a/kolibri/plugins/coach/assets/src/views/common/assignments/SidePanelRecipientsSelector/index.vue b/kolibri/plugins/coach/assets/src/views/common/assignments/SidePanelRecipientsSelector/index.vue new file mode 100644 index 00000000000..5494bb73766 --- /dev/null +++ b/kolibri/plugins/coach/assets/src/views/common/assignments/SidePanelRecipientsSelector/index.vue @@ -0,0 +1,250 @@ + + + + + + + diff --git a/kolibri/plugins/coach/assets/src/views/common/commonCoachStrings.js b/kolibri/plugins/coach/assets/src/views/common/commonCoachStrings.js index 2c602fff59e..06fa823fab9 100644 --- a/kolibri/plugins/coach/assets/src/views/common/commonCoachStrings.js +++ b/kolibri/plugins/coach/assets/src/views/common/commonCoachStrings.js @@ -200,6 +200,16 @@ const coachStrings = createTranslator('CommonCoachStrings', { context: 'A group is a collection of learners created by a coach inside a class to help with differentiated learning. Quizzes and lessons can be assigned to individual groups as well as to the whole class.', }, + individualLearnersLabel: { + message: 'Individual learners', + context: + 'A label for a checkbox that allows the Coach to assign the quiz to individual learners who may not be in a selected group.', + }, + onlyShowingEnrolledLabel: { + message: 'Only showing learners that are enrolled in this class', + context: + "Shows beneath 'Select individual learners' explaining that the table only includes enrolled learners.", + }, helpNeededLabel: { message: 'Help needed', context: @@ -635,6 +645,11 @@ const coachStrings = createTranslator('CommonCoachStrings', { context: "In the 'Manage lesson resources' coaches can add new/remove resource material to a lesson.", }, + groupsAndLearnersLabel: { + message: 'Groups and individual learners', + context: + 'Label for the radio button that allows the coach to select groups or individual learners to assign a quiz to.', + }, }); // Strings for the Missing Content modals, tooltips, alerts, etc. @@ -656,6 +671,24 @@ const MissingContentStrings = createTranslator('MissingContentStrings', { }, }); +// Strings for showing lists of items that can be truncated +const truncatedItemsStrings = createTranslator('TruncatedItemsStrings', { + twoItems: { + message: '{item1}, {item2}', + context: + "DO NOT TRANSLATE\nCopy the source string.\n\nFor reference: 'item' will be replaced by the name of the coach(es) in the list of classes.", + }, + threeItems: { + message: '{item1}, {item2}, {item3}', + context: + "DO NOT TRANSLATE\nCopy the source string.\n\nFor reference: 'item' will be replaced by the name of the coach(es) in the list of classes.", + }, + manyItems: { + message: '{item1}, {item2}, and {count, number, integer} others', + context: "'item' will be replaced by the name of the coach(es) in the list of classes.", + }, +}); + function coachString(key, args) { return coachStrings.$tr(key, args); } @@ -669,4 +702,28 @@ const coachStringsMixin = { }, }; -export { coachString, coachStrings, coachStringsMixin }; +function getTruncatedItemsString(items) { + if (items.length <= 1) { + return items[0] || ''; + } + if (items.length === 2) { + return truncatedItemsStrings.$tr('twoItems', { + item1: items[0], + item2: items[1], + }); + } + if (items.length === 3) { + return truncatedItemsStrings.$tr('threeItems', { + item1: items[0], + item2: items[1], + item3: items[2], + }); + } + return truncatedItemsStrings.$tr('manyItems', { + item1: items[0], + item2: items[1], + count: items.length - 2, + }); +} + +export { coachString, coachStrings, coachStringsMixin, getTruncatedItemsString }; diff --git a/kolibri/plugins/coach/assets/src/views/quizzes/CreateExamPage/index.vue b/kolibri/plugins/coach/assets/src/views/quizzes/CreateExamPage/index.vue index c60b7065895..b160e7eac08 100644 --- a/kolibri/plugins/coach/assets/src/views/quizzes/CreateExamPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/quizzes/CreateExamPage/index.vue @@ -19,6 +19,7 @@ v-if="quizInitialized" ref="detailsModal" assignmentType="quiz" + :selectRecipientsWithSidePanel="true" :assignment="quiz" :classId="classId" :groups="groups" @@ -334,6 +335,10 @@ } }, saveQuizAndRedirect(close = true) { + const errorText = this.$refs.detailsModal.validate(); + if (errorText) { + return; + } this.saveQuiz() .then(exam => { this.$refs.detailsModal.handleSubmitSuccess(); diff --git a/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/QuizOptionsDropdownMenu.vue b/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/QuizOptionsDropdownMenu.vue index fb40a75fc74..68ef7bd45d0 100644 --- a/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/QuizOptionsDropdownMenu.vue +++ b/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/QuizOptionsDropdownMenu.vue @@ -26,26 +26,30 @@ name: 'QuizOptionsDropdownMenu', mixins: [coachStringsMixin, commonCoreStrings], props: { - draft: { - type: Boolean, - default: false, + exam: { + type: Object, + required: false, + default: null, }, }, computed: { options() { - return [ - { - label: this.draft - ? this.coreString('editAction') - : this.coreString('editDetailsAction'), - value: 'EDIT_DETAILS', - }, + const options = [ { label: this.$tr('copyQuizAction'), value: 'COPY', }, { label: this.coreString('deleteAction'), value: 'DELETE' }, ]; + if (!this.exam?.archive) { + options.unshift({ + label: this.exam?.draft + ? this.coreString('editAction') + : this.coreString('editDetailsAction'), + value: 'EDIT_DETAILS', + }); + } + return options; }, }, $trs: { diff --git a/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/index.vue b/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/index.vue index c2f9348159c..7857db317cb 100644 --- a/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/quizzes/QuizSummaryPage/index.vue @@ -23,7 +23,7 @@ style="margin-right: 8px" /> diff --git a/packages/kolibri-common/components/PaginatedListContainer.vue b/packages/kolibri-common/components/PaginatedListContainer.vue index 5facd29b654..58ab38d5a5f 100644 --- a/packages/kolibri-common/components/PaginatedListContainer.vue +++ b/packages/kolibri-common/components/PaginatedListContainer.vue @@ -2,16 +2,17 @@
- + @@ -75,6 +76,10 @@ required: false, default: 30, }, + searchFieldBlock: { + type: Boolean, + required: false, + }, }, data() { return { diff --git a/packages/kolibri-common/components/SidePanelModal/index.vue b/packages/kolibri-common/components/SidePanelModal/index.vue index 375714e843f..26b1029c8fe 100644 --- a/packages/kolibri-common/components/SidePanelModal/index.vue +++ b/packages/kolibri-common/components/SidePanelModal/index.vue @@ -88,8 +88,6 @@ /* Will be calculated in mounted() as it will get the height of the fixedHeader then */ // @type {RefImpl} windowBreakpoint, - fixedHeaderHeight: 0, - fixedBottombarHeight: 0, lastFocus: null, }; }, @@ -181,11 +179,6 @@ mounted() { const htmlTag = window.document.getElementsByTagName('html')[0]; htmlTag.style['overflow-y'] = 'hidden'; - // Gets the height of the fixed header - adds 40 to account for padding + 24 for closeButton - this.fixedHeaderHeight = this.$refs.fixedHeader.clientHeight; - if (this.$refs.fixedBottombar) { - this.fixedBottombarHeight = this.$refs.fixedBottombar.clientHeight; - } this.$nextTick(() => { this.$emit('shouldFocusFirstEl'); });