diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js
index 89c7f526fb..9c7a8f457d 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.spec.js
@@ -36,6 +36,11 @@ function mountComponent(opts = {}) {
namespaced: true,
getters: {
isNodeInCopyingState: () => jest.fn(),
+ getContentNodesCount: () =>
+ jest.fn().mockReturnValue({
+ resource_count: TOPIC_NODE.resource_count,
+ assessment_item_count: EXERCISE_NODE.assessment_item_count,
+ }),
},
},
},
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue
index f7fc43c8a9..55e0941d27 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeListItem/index.vue
@@ -258,7 +258,11 @@
};
},
computed: {
- ...mapGetters('contentNode', ['isNodeInCopyingState', 'hasNodeCopyingErrored']),
+ ...mapGetters('contentNode', [
+ 'isNodeInCopyingState',
+ 'hasNodeCopyingErrored',
+ 'getContentNodesCount',
+ ]),
isCompact() {
return this.compact || !this.$vuetify.breakpoint.mdAndUp;
},
@@ -273,14 +277,15 @@
return { title, kind, src, encoding };
},
subtitle() {
+ const count = this.getContentNodesCount(this.node.id);
switch (this.node.kind) {
case ContentKindsNames.TOPIC:
return this.$tr('resources', {
- value: this.node.resource_count || 0,
+ value: count?.resource_count || 0,
});
case ContentKindsNames.EXERCISE:
return this.$tr('questions', {
- value: this.node.assessment_item_count || 0,
+ value: count?.assessment_item_count || 0,
});
}
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/EditModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/EditModal.vue
index ad498da058..6c5d676a5b 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/EditModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/EditModal.vue
@@ -447,7 +447,10 @@
this.hideHTMLScroll(false);
this.$router.push({
name: RouteNames.TREE_VIEW,
- params: { nodeId: this.$route.params.nodeId },
+ params: {
+ nodeId: this.$route.params.nodeId,
+ addedCount: this.nodeIds.length,
+ },
});
},
hideHTMLScroll(hidden) {
diff --git a/contentcuration/contentcuration/frontend/channelEdit/pages/StagingTreePage/index.spec.js b/contentcuration/contentcuration/frontend/channelEdit/pages/StagingTreePage/index.spec.js
index 054fba2b6b..013cf754f4 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/pages/StagingTreePage/index.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/pages/StagingTreePage/index.spec.js
@@ -33,6 +33,7 @@ const GETTERS = {
getContentNodeAncestors: () => jest.fn(),
getContentNode: () => jest.fn(),
isNodeInCopyingState: () => jest.fn(),
+ getContentNodesCount: () => jest.fn(),
},
};
diff --git a/contentcuration/contentcuration/frontend/channelEdit/views/NodePanel.vue b/contentcuration/contentcuration/frontend/channelEdit/views/NodePanel.vue
index e8d9dcaa2d..d7dd308d73 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/views/NodePanel.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/views/NodePanel.vue
@@ -46,7 +46,7 @@
@@ -105,16 +105,37 @@
isRoot() {
return this.rootId === this.parentId;
},
+ addedCount() {
+ return this.$route.params.addedCount;
+ },
+ displayShowMoreButton() {
+ // Handle inconsistency with this.more that causes double click on "Show more" to load
+ // more nodes when new nodes(exercises, folders or file uploads) are added to the channel.
+ // If the addedCount is equal to the children length, force hide the "Show more" button.
+ const moreAdditions = this.addedCount !== this.children.length ? this.more : null;
+ return this.addedCount ? moreAdditions : this.more;
+ },
},
created() {
this.loading = true;
- this.loadChildren({ parent: this.parentId }).then(childrenResponse => {
- this.loading = false;
- this.more = childrenResponse.more || null;
+ this.clearContentNodes().then(success => {
+ if (success) {
+ this.loadChildren({ parent: this.parentId }).then(childrenResponse => {
+ this.loading = false;
+ this.more = childrenResponse.more || null;
+ const children = childrenResponse?.results || [];
+ this.setContentNodesCount(children);
+ });
+ }
});
},
methods: {
- ...mapActions('contentNode', ['loadChildren', 'loadContentNodes']),
+ ...mapActions('contentNode', [
+ 'loadChildren',
+ 'loadContentNodes',
+ 'setContentNodesCount',
+ 'clearContentNodes',
+ ]),
goToNodeDetail(nodeId) {
if (
this.$route.params.nodeId === this.parentId &&
@@ -164,6 +185,8 @@
this.loadContentNodes(this.more).then(response => {
this.more = response.more || null;
this.moreLoading = false;
+ const children = response?.results || [];
+ this.setContentNodesCount(children);
});
}
},
@@ -193,8 +216,8 @@
.pagination-container {
display: flex;
- justify-content: flex-start;
- margin: 4px;
+ justify-content: space-evenly;
+ margin: 32px;
}
diff --git a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js
index 70dc763762..8057041832 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/actions.js
@@ -610,3 +610,17 @@ export async function checkSavingProgress(
export function setQuickEditModal(context, open) {
context.commit('SET_QUICK_EDIT_MODAL', open);
}
+
+export function setContentNodesCount(context, nodes) {
+ for (const node of nodes) {
+ const { id, assessment_item_count, resource_count } = node;
+ context.commit('SET_CONTENTNODES_COUNT', { id, resource_count, assessment_item_count });
+ }
+}
+
+export function clearContentNodes(context) {
+ return new Promise(resolve => {
+ context.commit('CLEAR_CONTENTNODES');
+ resolve(true);
+ });
+}
diff --git a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/getters.js b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/getters.js
index 4cbcadace2..177471557a 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/getters.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/getters.js
@@ -365,3 +365,9 @@ export function getQuickEditModalOpen(state) {
return state.quickEditModalOpen;
};
}
+
+export function getContentNodesCount(state) {
+ return function(nodeId) {
+ return state.contentNodesCountMap[nodeId];
+ };
+}
diff --git a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/index.js b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/index.js
index 0d900fabe3..49a4260063 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/index.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/index.js
@@ -75,6 +75,17 @@ export default {
* }
*/
previousStepsMap: {},
+
+ /**
+ * A map of node ids to their respective resource or assessment item nodes counts.
+ *
+ * For example,
+ * {
+ * '': { 'resourceCount': 2 }
+ * '': { 'assessmentItemCount': 1 }
+ * }
+ */
+ contentNodesCountMap: {},
};
},
getters,
diff --git a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/mutations.js b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/mutations.js
index cdbadfc23c..da3e78f478 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/mutations.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/vuex/contentNode/mutations.js
@@ -138,3 +138,23 @@ export function SAVE_NEXT_STEPS(state, { mappings = [] } = {}) {
ADD_PREVIOUS_STEP(state, mapping);
}
}
+
+/**
+ * Saves the content node count to vuex state.
+ * @param state - The vuex state
+ * @param id - The content node id
+ * @param assessment_item_count - The count of assessment items
+ * @param resource_count - The count of resources
+ */
+export function SET_CONTENTNODES_COUNT(state, { id, assessment_item_count, resource_count }) {
+ Vue.set(state.contentNodesCountMap, id, { resource_count, assessment_item_count });
+}
+
+/**
+ * Clears all content node data (nodes and associated counts) from the vuex state.
+ * @param state - The vuex state
+ */
+export function CLEAR_CONTENTNODES(state) {
+ state.contentNodesMap = {};
+ state.contentNodesCountMap = {};
+}