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

Select resource sidepanel #11511

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
591a93c
set not loading when quiz creator comes up
nucleogenesis Oct 31, 2023
c6c2f8e
add DEBUG option to useQuizCreation; bootstrap basic accordion setup
nucleogenesis Oct 12, 2023
29abaca
uuid for test data ID
nucleogenesis Oct 16, 2023
47db90f
remove unneded prop
nucleogenesis Oct 16, 2023
6401cdc
lint
nucleogenesis Oct 16, 2023
f9854a2
drag-and-drop scaffolding function & design
nucleogenesis Oct 16, 2023
be1a910
styles on the accordion headers
nucleogenesis Oct 17, 2023
47fbd4f
section tabs better design parity w/out regression
nucleogenesis Oct 19, 2023
f9a8c76
dragStart event allows dynamic styles when dragging; disallow highlig…
nucleogenesis Oct 19, 2023
8441700
design polish
nucleogenesis Oct 20, 2023
b1980dc
helpers for select all checkbox props
nucleogenesis Oct 24, 2023
87064a3
enhanced quiz messages updates
nucleogenesis Oct 24, 2023
d1b567a
pass click event through tabswithoverflow; a11y improvements
nucleogenesis Oct 24, 2023
621503d
repoint kds
nucleogenesis Oct 24, 2023
b64d29b
edit & delete buttons work for section tabs
nucleogenesis Oct 24, 2023
46614c3
expandcollapse icon;
nucleogenesis Oct 25, 2023
2a85e7d
root page mostly feature complete; polish; a11y updates
nucleogenesis Oct 26, 2023
4449099
handle quiz title field
nucleogenesis Oct 26, 2023
f2b5f0e
update link to kds commit/pr
nucleogenesis Oct 26, 2023
21e6893
Ensure [Tab]-nav flow works properly when leaving Options button
nucleogenesis Nov 16, 2023
e621f66
not debug by default
nucleogenesis Nov 16, 2023
1ceee06
fix eternal loading state when navigation routes
nucleogenesis Nov 16, 2023
4aa72d2
update kds
nucleogenesis Nov 16, 2023
b4f196f
fixed loading failure
AllanOXDi Nov 6, 2023
63239d3
resource sidepanel header added
AllanOXDi Nov 7, 2023
5c97f6d
added search component
AllanOXDi Nov 7, 2023
07b7724
structured the add resource sidepanel
AllanOXDi Nov 8, 2023
827c0b6
adds channel cards to select resource side panel
AllanOXDi Nov 8, 2023
89efc54
added ContentCardList component
AllanOXDi Nov 9, 2023
a429f96
fetch resources for content
AllanOXDi Nov 9, 2023
85ee0d2
Optimize bookmark fetching logic for enhance quiz managment
AllanOXDi Nov 10, 2023
d992550
sets routes for channel resources
AllanOXDi Nov 10, 2023
85d8b0c
added bookmarked code
AllanOXDi Nov 17, 2023
53512bb
added the bookmark card
AllanOXDi Nov 17, 2023
c683194
replaced quizforge data with composable
AllanOXDi Nov 21, 2023
59ab6dd
cleanedup bookmarkicon area
AllanOXDi Nov 21, 2023
8ebe621
add show resourc component
AllanOXDi Nov 22, 2023
b14457f
implement content filtering for select resource side panel
AllanOXDi Nov 22, 2023
fd7bf8e
displays boomarked resources
AllanOXDi Nov 22, 2023
776ba5c
added the component for the channels
AllanOXDi Nov 23, 2023
75ec4ec
added the component for the channels
AllanOXDi Nov 23, 2023
300b355
added dragging feature
AllanOXDi Nov 27, 2023
81300e4
add bookmarked resources
AllanOXDi Nov 28, 2023
3fc3ba5
can navigate to the first level of the topic tree
AllanOXDi Nov 28, 2023
8fc9dde
removed unused data elements and components
AllanOXDi Nov 29, 2023
a389f14
changed structure to only allow contentCardlist component to update
AllanOXDi Nov 30, 2023
5d0e71a
can navigate backwards using the back arrow
AllanOXDi Nov 30, 2023
38687eb
adds topic tree navigation
AllanOXDi Dec 1, 2023
7d62544
added selection breadcrumbs
AllanOXDi Dec 1, 2023
eea8aeb
added selection breadcrumbs
AllanOXDi Dec 1, 2023
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
1 change: 1 addition & 0 deletions kolibri/core/assets/src/views/sortable/DragContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
handleStart() {
// handle cancelation of drags
// document.addEventListener('keyup', this.triggerMouseUpOnESC);
this.$emit('dragStart');
},
handleStop(event) {
const { oldIndex, newIndex } = event.data;
Expand Down
2 changes: 1 addition & 1 deletion kolibri/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"js-cookie": "^3.0.5",
"knuth-shuffle-seeded": "^1.0.6",
"kolibri-constants": "0.2.0",
"kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#v2.0.0-beta1",
"kolibri-design-system": "https://github.com/learningequality/kolibri-design-system#f19ca3fc5677fc7f3b4cdbfee33950f5adde5290",
"lockr": "0.8.5",
"lodash": "^4.17.21",
"loglevel": "^1.8.1",
Expand Down
198 changes: 198 additions & 0 deletions kolibri/plugins/coach/assets/src/composables/useExerciseResources.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { ref, onMounted } from 'kolibri.lib.vueCompositionApi';
import { ChannelResource, ContentNodeResource, ContentNodeSearchResource } from 'kolibri.resources';
import { ContentNodeKinds } from 'kolibri.coreVue.vuex.constants';
import { getContentNodeThumbnail } from 'kolibri.utils.contentNode';
import { set } from '@vueuse/core';
// import pickBy from 'lodash/pickBy';
import uniq from 'lodash/uniq';
// import store from 'kolibri.coreVue.vuex.store';

export function useExerciseResources() {
const resources = ref(null);
const channels = ref([]);
const bookmarks = ref([]);
const channelTopics = ref([]);
const contentList = ref([]);
const ancestors = ref([]);
const currentTopicId = ref(null);
const currentTopic = ref(null);
const currentTopicResource = ref(null);

function fetchChannelResource() {
ChannelResource.fetchCollection({ params: { has_exercises: true, available: true } }).then(
response => {
set(
channels,
response.map(chnl => {
return {
...chnl,
id: chnl.root,
title: chnl.name,
kind: ContentNodeKinds.CHANNEL,
is_leaf: false,
};
})
);
}
);
}

function fetchBookMarkedResource() {
ContentNodeResource.fetchBookmarks({ params: { limit: 25, available: true } }).then(data => {
bookmarks.value = data.results ? data.results : [];
});
}

function _getTopicsWithExerciseDescendants(topicIds = []) {
return new Promise(resolve => {
if (!topicIds.length) {
resolve([]);
return;
}
const topicsNumAssessmentDescendantsPromise = ContentNodeResource.fetchDescendantsAssessments(
topicIds
);

topicsNumAssessmentDescendantsPromise.then(response => {
const topicsWithExerciseDescendants = [];
response.data.forEach(descendantAssessments => {
if (descendantAssessments.num_assessments > 0) {
topicsWithExerciseDescendants.push({
id: descendantAssessments.id,
numAssessments: descendantAssessments.num_assessments,
exercises: [],
});
}
});

ContentNodeResource.fetchDescendants(
topicsWithExerciseDescendants.map(topic => topic.id),
{
descendant_kind: ContentNodeKinds.EXERCISE,
}
).then(response => {
response.data.forEach(exercise => {
channelTopics.value.push(exercise);
const topic = topicsWithExerciseDescendants.find(t => t.id === exercise.ancestor_id);
topic.exercises.push(exercise);
});
channels.value = channelTopics.value;
resolve(topicsWithExerciseDescendants);
});
});
});
}

function fetchTopicResource(topicId) {
const topicNodePromise = ContentNodeResource.fetchModel({ id: topicId });
const childNodesPromise = ContentNodeResource.fetchCollection({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per our conversation - this is where we could use the new fetchTree method instead, to fetch both the topic and its children in one go.

To limit data, can pass the depth: 1 getParam to only get the immediate descendants. The only thing to be aware of here is that the return data here is that the children are paginated by default.

Can see an example of it in use here:

getParams: {
parent: topicId,
kind_in: [ContentNodeKinds.TOPIC, ContentNodeKinds.EXERCISE],
},
});
const loadRequirements = [topicNodePromise, childNodesPromise];

return Promise.all(loadRequirements).then(([topicNode, childNodes]) => {
return filterAndAnnotateContentList(childNodes).then(contentList => {
// set(topicId, topicNode.id);
ancestors.value = [...topicNode.ancestors, topicNode];
return {
...topicNode,
...contentList,
thumbnail: getContentNodeThumbnail(topicNode),
};
});
});
}

function filterAndAnnotateContentList(childNodes) {
return new Promise(resolve => {
if (childNodes) {
const childTopics = childNodes.filter(({ kind }) => kind === ContentNodeKinds.TOPIC);
const topicIds = childTopics.map(({ id }) => id);
const topicsThatHaveExerciseDescendants = _getTopicsWithExerciseDescendants(topicIds);
topicsThatHaveExerciseDescendants.then(topics => {
const childNodesWithExerciseDescendants = childNodes
.map(childNode => {
const index = topics.findIndex(topic => topic.id === childNode.id);
if (index !== -1) {
return { ...childNode, ...topics[index] };
}
return childNode;
})
.filter(childNode => {
if (
childNode.kind === ContentNodeKinds.TOPIC &&
(childNode.numAssessments || 0) < 1
) {
return false;
}
return true;
});
contentList.value = childNodesWithExerciseDescendants.map(node => ({
...node,
thumbnail: getContentNodeThumbnail(node),
}));
channels.value = contentList.value;
resolve(contentList);
});
}
});
}

function showChannelLevel(store, params, query = {}) {
let kinds;
if (query.kind) {
kinds = [query.kind];
} else {
kinds = [ContentNodeKinds.EXERCISE, ContentNodeKinds.TOPIC];
}

ContentNodeSearchResource.fetchCollection({
getParams: {
search: '',
kind_in: kinds,
// ...pickBy({ channel_id: query.channel }),
},
}).then(results => {
return filterAndAnnotateContentList(results.results).then(contentList => {
const searchResults = {
...results,
results: contentList,
content_kinds: results.content_kinds.filter(kind =>
[ContentNodeKinds.TOPIC, ContentNodeKinds.EXERCISE].includes(kind)
),
contentIdsFetched: uniq(results.results.map(({ content_id }) => content_id)),
};

this.channels.value = searchResults.results;
console.log(searchResults.results);
});
});
}

onMounted(() => {
fetchChannelResource();
fetchBookMarkedResource();
filterAndAnnotateContentList();
_getTopicsWithExerciseDescendants([]);
});

return {
resources,
channels,
bookmarks,
contentList,
channelTopics,
currentTopicId,
currentTopic,
currentTopicResource,
ancestors,
fetchChannelResource,
filterAndAnnotateContentList,
_getTopicsWithExerciseDescendants,
showChannelLevel,
fetchTopicResource,
};
}
Loading