Skip to content

Commit

Permalink
Merge pull request learningequality#13012 from AllanOXDi/preview-reso…
Browse files Browse the repository at this point in the history
…urce

Adds ability to preview  non-practice resources from the sidepanel
  • Loading branch information
marcellamaki authored Feb 6, 2025
2 parents 3e2a484 + 550973a commit 4260da7
Show file tree
Hide file tree
Showing 8 changed files with 449 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ref, getCurrentInstance } from 'vue';
import ContentNodeResource from 'kolibri-common/apiResources/ContentNodeResource';

export default function useFetchContentNode(contentId) {
const contentNode = ref({});
const ancestors = ref([]);
const questions = ref([]);
const loading = ref(true);
const store = getCurrentInstance().proxy.$store;

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

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

if (node.assessmentmetadata) {
questions.value = node.assessmentmetadata.assessment_item_ids;
}
})
.catch(error => {
store.dispatch('handleApiError', { error });
});
};

fetchContentNode();

return {
loading,
ancestors,
contentNode,
questions,
};
}
13 changes: 12 additions & 1 deletion kolibri/plugins/coach/assets/src/routes/lessonsRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
import LessonLearnerExercisePage from '../views/lessons/reports/LessonLearnerExercisePage.vue';
import QuestionLearnersPage from '../views/common/reports/QuestionLearnersPage.vue';
import EditLessonDetails from '../views/lessons/LessonSummaryPage/sidePanels/EditLessonDetails';
import PreviewSelectedResources from '../views/lessons/LessonSummaryPage/sidePanels/PreviewSelectedResources';
import PreviewSelectedResources from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/PreviewSelectedResources';
import LessonResourceSelection from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/index.vue';
import SearchFilters from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/SearchFilters.vue';
import SelectionIndex from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/SelectionIndex.vue';
Expand Down Expand Up @@ -172,6 +172,17 @@ export default [
path: 'preview-resources',
component: ManageSelectedResources,
},
{
name: PageNames.LESSON_PREVIEW_RESOURCE,
path: 'preview',
component: PreviewSelectedResources,
props: toRoute => {
const contentId = toRoute.query.contentId;
return {
contentId,
};
},
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import { ContentNodeKinds } from 'kolibri/constants';
import ContentCardList from '../../lessons/LessonResourceSelectionPage/ContentCardList.vue';
import ResourceSelectionBreadcrumbs from '../../lessons/LessonResourceSelectionPage/SearchTools/ResourceSelectionBreadcrumbs.vue';
import { ViewMoreButtonStates } from '../../../constants';
import { ViewMoreButtonStates, PageNames } from '../../../constants';
export default {
name: 'UpdatedResourceSelection',
Expand Down Expand Up @@ -148,12 +148,18 @@
},
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: {
...query,
contentId: content.id,
},
};
},
topicsLink(topicId) {
const route = this.getTopicLink?.(topicId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<div v-if="subpageLoading">
<KCircularLoader />
</div>

<router-view
v-else
:setTitle="setTitle"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<template>

<div>
<div class="channel-header">
<p>
{{ coreString('selectFromChannels') }}
</p>

<div>
<template v-if="isSelected">
<KIcon icon="onDevice" />
{{ addedIndicator$() }}
</template>

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

<ResourceSelectionBreadcrumbs
v-if="ancestors.length"
:ancestors="[...ancestors, currentContentNode]"
:channelsLink="channelsLink"
:topicsLink="topicsLink"
/>

<h2>
<KLabeledIcon :label="currentContentNode.kind">
<template #icon>
<LearningActivityIcon :kind="learningActivities" />
</template>
<template>
{{ currentContentNode.title }}
</template>
</KLabeledIcon>
</h2>

<ContentArea
:header="questionLabel(selectedQuestionIndex)"
:selectedQuestion="selectedQuestion"
:content="currentContentNode"
: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>

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

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

<HeaderTableRow :keyText="copyrightHolderDataHeader$()">
<template #value>
{{ currentContentNode.license_owner }}
</template>
</HeaderTableRow>
</HeaderTable>
</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 { ContentNodeKinds } from 'kolibri/constants';
import LearningActivityIcon from 'kolibri-common/components/ResourceDisplayAndSearch/LearningActivityIcon.vue';
import SlotTruncator from 'kolibri-common/components/SlotTruncator';
import ContentArea from '../../../../../LessonSelectionContentPreviewPage/LessonContentPreview/ContentArea.vue';
import commonCoach from '../../../../../../common';
import { PageNames } from '../../../../../../../constants/index';
import ResourceSelectionBreadcrumbs from '../../../../../LessonResourceSelectionPage/SearchTools/ResourceSelectionBreadcrumbs.vue';
export default {
name: 'PreviewContent',
components: {
ContentArea,
SlotTruncator,
LearningActivityIcon,
ResourceSelectionBreadcrumbs,
},
mixins: [commonCoreStrings, commonCoach],
setup() {
const {
addText$,
copyrightHolderDataHeader$,
licenseDataHeader$,
addedIndicator$,
notAvailableLabel$,
minutes$,
} = searchAndFilterStrings;
return {
addText$,
licenseDataHeader$,
copyrightHolderDataHeader$,
addedIndicator$,
notAvailableLabel$,
minutes$,
};
},
props: {
currentContentNode: {
type: Object,
required: true,
},
ancestors: {
type: Array,
required: false,
default: () => [],
},
isSelected: {
type: Boolean,
required: true,
},
questions: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
selectedQuestionIndex: 0,
};
},
computed: {
channelsLink() {
return {
name: PageNames.LESSON_SELECT_RESOURCES_INDEX,
};
},
isExercise() {
return this.currentContentNode.kind === ContentNodeKinds.EXERCISE;
},
selectedQuestion() {
if (this.isExercise) {
return this.questions[this.selectedQuestionIndex];
}
return '';
},
licenseName() {
return licenseLongName(this.currentContentNode.license_name);
},
description() {
if (this.currentContentNode && this.currentContentNode.description) {
const md = new markdownIt('zero', { breaks: true });
return md.render(this.currentContentNode.description);
}
return undefined;
},
learningActivities() {
if (this.currentContentNode.learning_activities) {
return this.currentContentNode.learning_activities;
}
return [];
},
},
methods: {
topicsLink(topicId) {
const { params, query } = this.$route;
return {
name: PageNames.LESSON_SELECT_RESOURCES_TOPIC_TREE,
params: params,
query: {
...query,
topicId,
},
};
},
questionLabel(questionIndex) {
if (!this.isExercise) {
return '';
}
const questionNumber = questionIndex + 1;
return this.coreString('questionNumberLabel', { questionNumber });
},
addResource() {
this.$emit('addResource', this.currentContentNode);
},
removeResource() {
this.$emit('removeResource', this.currentContentNode);
},
getTime(seconds) {
return this.minutes$({ value: Math.floor(seconds / 60) });
},
},
};
</script>


<style lang="scss" scoped>
.license-detail-style {
margin-top: 10px;
}
/deep/ .content-renderer {
position: relative;
max-height: 500px;
}
.channel-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.channel-header p {
font-weight: 600;
}
</style>
Loading

0 comments on commit 4260da7

Please sign in to comment.