diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue new file mode 100644 index 0000000000..debdbeadc3 --- /dev/null +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue @@ -0,0 +1,188 @@ + + + + diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue new file mode 100644 index 0000000000..8a1571b454 --- /dev/null +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue @@ -0,0 +1,816 @@ + + + + diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue index 3fd74720d6..327d6faec3 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue @@ -54,11 +54,7 @@ @focus="trackClick('Description')" /> - + - +

- {{ $tr('assessmentHeader') }} + {{ $tr('assessmentOptionsLabel') }}

- - - - +
+
- - + + +

+ {{ $tr('completionLabel') }} +

+ + + + +
@@ -162,7 +158,7 @@ {{ $tr('thumbnailHeader') }} -
+

- - @@ -274,7 +268,7 @@ autoSelectFirst box :value="provider && provider.toString()" - @input.native="e => provider = e.srcElement.value" + @input.native="(e) => (provider = e.srcElement.value)" @input="provider = $event" @focus="trackClick('Provider')" > @@ -295,7 +289,7 @@ :placeholder="getPlaceholder('aggregator')" box :value="aggregator && aggregator.toString()" - @input.native="e => aggregator = e.srcElement.value" + @input.native="(e) => (aggregator = e.srcElement.value)" @input="aggregator = $event" @focus="trackClick('Aggregator')" > @@ -332,7 +326,7 @@ :readonly="disableAuthEdits" box :value="copyright_holder && copyright_holder.toString()" - @input.native="e => copyright_holder = e.srcElement.value" + @input.native="(e) => (copyright_holder = e.srcElement.value)" @input="copyright_holder = $event" @focus="trackClick('Copyright holder')" /> @@ -375,6 +369,7 @@ import ResourcesNeededOptions from './ResourcesNeededOptions.vue'; import LearningActivityOptions from './LearningActivityOptions.vue'; import CategoryOptions from './CategoryOptions.vue'; + import CompletionOptions from './CompletionOptions.vue'; import { getTitleValidators, @@ -385,11 +380,10 @@ import LanguageDropdown from 'shared/views/LanguageDropdown'; import HelpTooltip from 'shared/views/HelpTooltip'; import LicenseDropdown from 'shared/views/LicenseDropdown'; - import MasteryDropdown from 'shared/views/MasteryDropdown'; import VisibilityDropdown from 'shared/views/VisibilityDropdown'; import Checkbox from 'shared/views/form/Checkbox'; import { ContentKindsNames } from 'shared/leUtils/ContentKinds'; - import { NEW_OBJECT, FeatureFlagKeys, ContentModalities } from 'shared/constants'; + import { NEW_OBJECT, FeatureFlagKeys } from 'shared/constants'; import { validate as validateCompletionCriteria } from 'shared/leUtils/CompletionCriteria'; import { constantsTranslationMixin, metadataTranslationMixin } from 'shared/mixins'; @@ -435,7 +429,9 @@ * - `learner_needs` (resources needed) * - `accessibility_labels` (accessibility options) * - `learning_activities` (learning activities) + * - `categories` (categories) */ + function generateNestedNodesGetterSetter(key) { return { get() { @@ -458,7 +454,6 @@ LanguageDropdown, HelpTooltip, LicenseDropdown, - MasteryDropdown, VisibilityDropdown, FileUpload, SubtitlesList, @@ -469,6 +464,7 @@ ResourcesNeededOptions, LearningActivityOptions, CategoryOptions, + CompletionOptions, }, mixins: [constantsTranslationMixin, metadataTranslationMixin], props: { @@ -552,27 +548,7 @@ resourcesNeeded: generateNestedNodesGetterSetter('learner_needs'), contentLearningActivities: generateNestedNodesGetterSetter('learning_activities'), categories: generateNestedNodesGetterSetter('categories'), - mastery_model() { - return this.getExtraFieldsValueFromNodes('mastery_model'); - }, - m() { - return this.getExtraFieldsValueFromNodes('m'); - }, - n() { - return this.getExtraFieldsValueFromNodes('n'); - }, - masteryModelItem: { - get() { - return { - mastery_model: this.mastery_model, - m: this.m, - n: this.n, - }; - }, - set(value) { - this.updateExtraFields(value); - }, - }, + learnerManaged: generateGetterSetter('learner_managed'), license() { return this.getValueFromNodes('license'); }, @@ -590,9 +566,6 @@ this.update(value); }, }, - extra_fields() { - return this.getValueFromNodes('extra_fields'); - }, thumbnail: { get() { return this.nodeFiles.find(f => f.preset.thumbnail); @@ -602,34 +575,37 @@ }, }, thumbnailEncoding: generateGetterSetter('thumbnail_encoding'), - channelQuiz: { + completionAndDuration: { get() { - const options = this.getExtraFieldsValueFromNodes('options') || {}; - return options.modality === ContentModalities.QUIZ; - }, - set(val) { - const options = { modality: val ? ContentModalities.QUIZ : null }; - this.updateExtraFields({ options }); - }, - }, - // TODO remove eslint disable when `completionCriteria` is utilized - /* eslint-disable-next-line kolibri/vue-no-unused-properties */ - completionCriteria: { - get() { - const options = this.getExtraFieldsValueFromNodes('options') || {}; - return options.completion_criteria || {}; + const { completion_criteria, modality } = + this.getExtraFieldsValueFromNodes('options') || {}; + const suggested_duration_type = this.getExtraFieldsValueFromNodes( + 'suggested_duration_type' + ); + const suggested_duration = this.getValueFromNodes('suggested_duration'); + return { + suggested_duration, + suggested_duration_type, + modality, + ...(completion_criteria || {}), + }; }, - set(completion_criteria) { - // TODO Remove validation if unnecessary after implementing `completionCriteria` - if (validateCompletionCriteria(completion_criteria)) { + set({ completion_criteria, suggested_duration, suggested_duration_type, modality }) { + if (validateCompletionCriteria(completion_criteria, this.firstNode.kind)) { const options = { completion_criteria }; this.updateExtraFields({ options }); } else { console.warn('Invalid completion criteria', [...validateCompletionCriteria.errors]); } + const options = { completion_criteria, modality }; + if (modality) { + options.modality = modality; + } + this.updateExtraFields({ options }); + this.updateExtraFields({ suggested_duration_type }); + this.update({ suggested_duration }); }, }, - /* COMPUTED PROPS */ disableAuthEdits() { return this.nodes.some(node => node.freeze_authoring_data); @@ -665,6 +641,15 @@ nodeFiles() { return (this.firstNode && this.getContentNodeFiles(this.firstNode.id)) || []; }, + fileDuration() { + if (this.firstNode.kind === 'audio' || this.firstNode.kind === 'video') { + return this.nodeFiles.filter( + file => file.file_format === 'mp4' || file.file_format === 'mp3' + )[0].duration; + } else { + return null; + } + }, videoSelected() { return this.oneSelected && this.firstNode.kind === 'video'; }, @@ -674,6 +659,9 @@ allowChannelQuizzes() { return this.$store.getters.hasFeatureEnabled(FeatureFlagKeys.channel_quizzes); }, + isDocument() { + return this.firstNode.kind === 'document'; + }, }, watch: { nodes: { @@ -800,7 +788,6 @@ basicInfoHeader: 'Basic information', audienceHeader: 'Audience', sourceHeader: 'Source', - assessmentHeader: 'Assessment options', thumbnailHeader: 'Thumbnail', titleLabel: 'Title', languageHelpText: 'Leave blank to use the folder language', @@ -819,8 +806,10 @@ descriptionLabel: 'Description', tagsLabel: 'Tags', noTagsFoundText: 'No results found for "{text}". Press \'Enter\' key to create a new tag', + assessmentOptionsLabel: 'Assessment options', randomizeQuestionLabel: 'Randomize question order for learners', - channelQuizzesLabel: 'Allow as a channel quiz', + completionLabel: 'Completion', + learnersCanMarkComplete: 'Allow learners to mark as complete', }, }; diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/MasteryCriteriaGoal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/MasteryCriteriaGoal.vue new file mode 100644 index 0000000000..f56ba17306 --- /dev/null +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/MasteryCriteriaGoal.vue @@ -0,0 +1,139 @@ + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/MasteryDropdown.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/MasteryCriteriaMofNFields.vue similarity index 63% rename from contentcuration/contentcuration/frontend/shared/views/MasteryDropdown.vue rename to contentcuration/contentcuration/frontend/channelEdit/components/edit/MasteryCriteriaMofNFields.vue index 4178418e2d..9ba5a5cf2e 100644 --- a/contentcuration/contentcuration/frontend/shared/views/MasteryDropdown.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/MasteryCriteriaMofNFields.vue @@ -1,36 +1,6 @@