Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability to preview non-practice resources from the sidepanel #13012

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ref } from 'vue';
import ContentNodeResource from 'kolibri-common/apiResources/ContentNodeResource';

export default function useFetchContentNode(contentId) {
const contentNode = ref({});
const ancestors = ref([]);
const questions = ref([]);

const fetchContentNode = () => {
ContentNodeResource.fetchModel({
id: contentId,
getParams: { no_available_filtering: true },
}).then(node => {
contentNode.value = node;

if (node.ancestors.length) {
ancestors.value = node.ancestors;
}

if (node.assessmentmetadata) {
questions.value = node.assessmentmetadata.assessment_item_ids;
}
});
};

fetchContentNode();

return {
ancestors,
contentNode,
questions,
};
}
16 changes: 11 additions & 5 deletions kolibri/plugins/coach/assets/src/routes/lessonsRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,24 @@ export default [
path: 'channels',
component: SelectFromChannels,
},
{
name: PageNames.LESSON_PREVIEW_RESOURCE,
path: 'preview-selected-resources',
component: PreviewSelectedResources,
props: toRoute => {
const contentId = toRoute.query.contentId;
return {
contentId,
};
},
},
],
},
{
name: PageNames.LESSON_PREVIEW_SELECTED_RESOURCES,
path: 'preview-resources/',
component: ManageSelectedLessonResources,
},
{
name: PageNames.LESSON_PREVIEW_RESOURCE,
path: 'preview-resources/:nodeId',
component: PreviewSelectedResources,
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,20 @@
},
methods: {
contentLink(content) {
const { name, params, query } = this.$route;
const { params, query } = this.$route;
if (!content.is_leaf) {
return this.topicsLink(content.id);
}
// Just return the current route; router-link will handle the no-op from here
return { name, params, query };
return {
name: PageNames.LESSON_PREVIEW_RESOURCE,
params: {
...params,
},
query: {
contentId: content.id,
...query,
},
};
},
topicsLink(topicId) {
const { name, params, query } = this.$route;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<template>

<div>
<KGrid>
<KGridItem
:layout12="{ span: 9 }"
:layout8="{ span: 4 }"
:layout4="{ span: 2 }"
>
{{ coreString('selectFromChannels') }}
</KGridItem>
<KGridItem
:layout12="{ span: 3 }"
:layout8="{ span: 4 }"
:layout4="{ span: 2 }"
>
<template>
<div class="add-remove-button-style">
<template v-if="isSelected">
<KIcon icon="onDevice" />
{{ addedIndicator$() }}
</template>

<KButton
v-if="isSelected"
:text="coreString('removeAction')"
:primary="true"
:disabled="disableSelectButton"
@click="removeResource"
/>
<KButton
v-else
:text="addText$()"
:primary="false"
:disabled="disableSelectButton"
@click="addResource"
/>
</div>
</template>
</KGridItem>
</KGrid>

<KBreadcrumbs
:items="breadcrumbs"
:showSingleItem="true"
/>

<h5>
<KLabeledIcon :icon="content.kind">
<template>
{{ content.title }}
</template>
</KLabeledIcon>
</h5>

<ContentArea
:header="questionLabel(selectedQuestionIndex)"
:selectedQuestion="selectedQuestion"
:content="content"
:isExercise="isExercise"
/>

<SlotTruncator
v-if="description"
:maxHeight="75"
:showViewMore="true"
>
<!-- eslint-disable vue/no-v-html -->
<p
dir="auto"
v-html="description"
></p>
<!-- eslint-enable -->
</SlotTruncator>

<template>
<HeaderTable class="license-detail-style">
<HeaderTableRow :keyText="coreString('suggestedTime')">
<template #value>
{{ content.duration ? getTime(content.duration) : 'Not available' }}
</template>
</HeaderTableRow>

<HeaderTableRow :keyText="licenseDataHeader$()">
<template #value>
{{ licenseName }}
</template>
</HeaderTableRow>

<HeaderTableRow :keyText="copyrightHolderDataHeader$()">
<template #value>
{{ content.license_owner }}
</template>
</HeaderTableRow>
</HeaderTable>
</template>
</div>

</template>


<script>

import commonCoreStrings from 'kolibri/uiText/commonCoreStrings';
import { searchAndFilterStrings } from 'kolibri-common/strings/searchAndFilterStrings';
import { licenseLongName } from 'kolibri/uiText/licenses';
import markdownIt from 'markdown-it';
import SlotTruncator from 'kolibri-common/components/SlotTruncator';
import ContentArea from '../../LessonSelectionContentPreviewPage/LessonContentPreview/ContentArea.vue';
import commonCoach from '../../../common';
import { PageNames } from '../../../../constants/index';

export default {
name: 'PreviewContent',
components: {
ContentArea,
SlotTruncator,
},
mixins: [commonCoreStrings, commonCoach],
setup() {
const { addText$, copyrightHolderDataHeader$, licenseDataHeader$, addedIndicator$ } =
searchAndFilterStrings;

return {
addText$,
licenseDataHeader$,
copyrightHolderDataHeader$,
addedIndicator$,
};
},
props: {
currentContentNode: {
type: Object,
required: true,
},
ancestors: {
type: Array,
required: false,
default: () => [],
},
isSelected: {
type: Boolean,
required: true,
},
},
data() {
return {
selectedQuestionIndex: 0,
disableSelectButton: false,
};
},
computed: {
breadcrumbs() {
return [
{ text: this.coreString('channelsLabel'), link: this.channelsLink },
...this.ancestors.map(a => ({
text: a.title,
link: this.topicsLink(a.id),
})),
];
},
isExercise() {
return this.content.kind === 'exercise';
},
selectedQuestion() {
if (this.isExercise) {
return this.questions[this.selectedQuestionIndex];
}
return '';
},
licenseName() {
return licenseLongName(this.content.license_name);
},
content() {
return this.currentContentNode;
},
channelsLink() {
return this.selectionRootLink();
},
description() {
if (this.content && this.content.description) {
const md = new markdownIt('zero', { breaks: true });
return md.render(this.content.description);
}

return undefined;
},
},
methods: {
topicsLink(topicId) {
return this.topicListingLink({ ...this.$route.params, topicId });
},
topicListingLink({ topicId }) {
return this.$router.getRoute(
PageNames.LESSON_RESOURCE_SELECTION,
{ topicId },
this.$route.query,
);
},
selectionRootLink() {
return this.$router.getRoute(
PageNames.LESSON_RESOURCE_SELECTION_ROOT,
{},
this.$route.query,
);
},
questionLabel(questionIndex) {
if (!this.isExercise) {
return '';
}
const questionNumber = questionIndex + 1;
return this.coreString('questionNumberLabel', { questionNumber });
},
addResource() {
this.disableSelectButton = true;
this.$emit('addResource', this.content);
},
removeResource() {
this.disableSelectButton = true;
this.$emit('removeResource', this.content);
},
getTime(seconds) {
return this.$tr('minutes', { value: Math.floor(seconds / 60) });
},
},
$trs: {
minutes: {
message: '{value, number, integer} {value, plural, one {minute} other {minutes}}',
context:
'Indicates time spent by learner on a specific activity. Only translate minute/minutes.',
},
},
};

</script>


<style lang="scss" scoped>

.add-remove-button-style {
float: right;
}

.license-detail-style {
margin: 30px 0 32px;
}

/deep/ .content-renderer {
max-height: 500px;
}

</style>
Loading
Loading