Skip to content

Commit

Permalink
Merge pull request #12996 from AlexVelezLl/tide-up-shopping-cart
Browse files Browse the repository at this point in the history
Integrate shopping cart into lesson resource selection side panel
  • Loading branch information
AlexVelezLl authored Jan 29, 2025
2 parents 072f8ee + 85a925d commit bf19897
Show file tree
Hide file tree
Showing 19 changed files with 465 additions and 440 deletions.
13 changes: 7 additions & 6 deletions kolibri/plugins/coach/assets/src/routes/lessonsRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ import QuestionLearnersPage from '../views/common/reports/QuestionLearnersPage.v
import EditLessonDetails from '../views/lessons/LessonSummaryPage/sidePanels/EditLessonDetails';
import PreviewSelectedResources from '../views/lessons/LessonSummaryPage/sidePanels/PreviewSelectedResources';
import LessonResourceSelection from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection';
import ManageSelectedLessonResources from '../views/lessons/LessonSummaryPage/sidePanels/ManageSelectedLessonResource';
import SelectionIndex from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/SelectionIndex.vue';
import SelectFromBookmarks from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/SelectFromBookmarks.vue';
import SelectFromChannels from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/SelectFromChannels.vue';
import ManageSelectedResources from '../views/lessons/LessonSummaryPage/sidePanels/LessonResourceSelection/subPages/ManageSelectedResources.vue';

import { classIdParamRequiredGuard, RouteSegments } from './utils';

const {
Expand Down Expand Up @@ -155,13 +156,13 @@ export default [
path: 'channels',
component: SelectFromChannels,
},
{
name: PageNames.LESSON_PREVIEW_SELECTED_RESOURCES,
path: 'preview-resources',
component: ManageSelectedResources,
},
],
},
{
name: PageNames.LESSON_PREVIEW_SELECTED_RESOURCES,
path: 'preview-resources/',
component: ManageSelectedLessonResources,
},
{
name: PageNames.LESSON_PREVIEW_RESOURCE,
path: 'preview-resources/:nodeId',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,16 @@ const coachStrings = createTranslator('CommonCoachStrings', {
message: 'Deleted',
context: 'Generic notification.',
},
closeConfirmationTitle: {
message: 'Are you sure you want to leave this page?',
context:
'The title of a confirmation modal informing the user that they will lose their work if they leave the page',
},
closeConfirmationMessage: {
message: 'You will lose any unsaved edits to your work',
context:
'Warning message for the user that they will lose their work if they leave the page without saving.',
},

// errors
saveLessonError: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
.content-list {
display: block;
padding: 0;
margin: 0;
list-style: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
<BottomAppBar>
<KRouterLink
v-if="workingResources.length > 0"
:text="numberOfSelectedResource$({ count: workingResources.length })"
:text="numberOfSelectedResources$({ count: workingResources.length })"
:primary="true"
:to="goToPreviewSelection()"
:style="{ marginRight: '1em', marginTop: '0.5em' }"
Expand Down Expand Up @@ -163,13 +163,13 @@
const { windowIsSmall } = useKResponsiveWindow();
const { getUserPermissions } = useUser();
const { createSnackbar, clearSnackbar } = useSnackbar();
const { numberOfSelectedResource$ } = searchAndFilterStrings;
const { numberOfSelectedResources$ } = searchAndFilterStrings;
return {
windowIsSmall,
getUserPermissions,
createSnackbar,
clearSnackbar,
numberOfSelectedResource$,
numberOfSelectedResources$,
};
},
data() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@
type: Array,
required: true,
},
unselectableResourceIds: {
type: Array,
required: false,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
},
computed: {
channelsLink() {
Expand Down Expand Up @@ -151,9 +160,15 @@
}
},
contentCheckboxDisabled(resource) {
if (this.disabled || this.unselectableResourceIds?.includes(resource.id)) {
return true;
}
return !this.selectionRules.every(rule => rule(resource) === true);
},
contentIsChecked(resource) {
if (this.unselectableResourceIds?.includes(resource.id)) {
return true;
}
return this.selectedResources.some(res => res.id === resource.id);
},
toggleSelected({ content, checked }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
</KPageContainer>
</KGridItem>
</KGrid>
<router-view />
<router-view @workingResourcesUpdated="workingResourcesBackup = [...workingResources]" />
</CoachAppBarPage>

</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
:setTitle="setTitle"
:setGoBack="setGoBack"
:topic="topic"
:disabled="isSaving"
:channelsFetch="channelsFetch"
:bookmarksFetch="bookmarksFetch"
:treeFetch="treeFetch"
:selectionRules="selectionRules"
:selectedResources="selectedResources"
:unselectableResourceIds="unselectableResourceIds"
:selectedResourcesSize="selectedResourcesSize"
@selectResources="selectResources"
@deselectResources="deselectResources"
@setSelectedResources="setSelectedResources"
Expand All @@ -40,29 +43,50 @@
<div class="bottom-nav-container">
<KButtonGroup>
<KRouterLink
v-if="selectedResources.length > 0"
v-if="
selectedResources.length > 0 &&
$route.name !== PageNames.LESSON_PREVIEW_SELECTED_RESOURCES
"
:to="{ name: PageNames.LESSON_PREVIEW_SELECTED_RESOURCES }"
>
{{ selectedResourcesMessage }}
</KRouterLink>
<KButton
primary
:disabled="isSaving"
:text="saveAndFinishAction$()"
@click="closeSidePanel"
@click="save"
/>
</KButtonGroup>
</div>
</template>

<KModal
v-if="isCloseConfirmationModalOpen"
appendToOverlay
:submitText="continueAction$()"
:cancelText="cancelAction$()"
:title="closeConfirmationTitle$()"
@cancel="isCloseConfirmationModalOpen = false"
@submit="closeSidePanel(false)"
>
{{ closeConfirmationMessage$() }}
</KModal>
</SidePanelModal>

</template>


<script>
import uniqBy from 'lodash/uniqBy';
import { mapState, mapActions, mapMutations } from 'vuex';
import SidePanelModal from 'kolibri-common/components/SidePanelModal';
import notificationStrings from 'kolibri/uiText/notificationStrings';
import { coreStrings } from 'kolibri/uiText/commonCoreStrings';
import bytesForHumans from 'kolibri/uiText/bytesForHumans';
import useSnackbar from 'kolibri/composables/useSnackbar';
import { PageNames } from '../../../../../constants';
import { coachStrings } from '../../../../common/commonCoachStrings';
import useResourceSelection from '../../../../../composables/useResourceSelection';
Expand All @@ -86,7 +110,18 @@
setSelectedResources,
} = useResourceSelection();
const { saveAndFinishAction$ } = coreStrings;
const { createSnackbar } = useSnackbar();
const { resourcesAddedWithCount$ } = notificationStrings;
function notifyResourcesAdded(count) {
createSnackbar(resourcesAddedWithCount$({ count }));
}
const { saveLessonError$, closeConfirmationTitle$, closeConfirmationMessage$ } = coachStrings;
function notifySaveLessonError() {
createSnackbar(saveLessonError$());
}
const { saveAndFinishAction$, continueAction$, cancelAction$ } = coreStrings;
return {
loading,
Expand All @@ -99,18 +134,27 @@
selectResources,
deselectResources,
setSelectedResources,
notifyResourcesAdded,
notifySaveLessonError,
cancelAction$,
continueAction$,
saveAndFinishAction$,
closeConfirmationTitle$,
closeConfirmationMessage$,
};
},
data() {
return {
title: '',
goBack: null,
isSaving: false,
isCloseConfirmationModalOpen: false,
PageNames,
};
},
computed: {
totalSize() {
...mapState('lessonSummary', ['currentLesson', 'workingResources']),
selectedResourcesSize() {
let size = 0;
this.selectedResources.forEach(resource => {
const { files = [] } = resource;
Expand All @@ -124,15 +168,74 @@
const { someResourcesSelected$ } = coachStrings;
return someResourcesSelected$({
count: this.selectedResources.length,
bytesText: bytesForHumans(this.totalSize),
bytesText: bytesForHumans(this.selectedResourcesSize),
});
},
unselectableResourceIds() {
return this.workingResources.map(resource => resource.contentnode_id);
},
},
methods: {
closeSidePanel() {
this.$router.push({
name: PageNames.LESSON_SUMMARY_BETTER,
});
...mapActions('lessonSummary', ['saveLessonResources', 'addToResourceCache']),
...mapMutations('lessonSummary', {
setWorkingResources: 'SET_WORKING_RESOURCES',
}),
getNewResources() {
return uniqBy(
[
...this.workingResources,
...this.selectedResources.map(resource => ({
contentnode_id: resource.id,
content_id: resource.content_id,
channel_id: resource.channel_id,
})),
],
'contentnode_id',
);
},
async save() {
if (!this.selectedResources.length) {
this.closeSidePanel(false);
return;
}
this.isSaving = true;
const newResources = this.getNewResources();
// As we are just adding resources, we can rely on the difference in length
// to determine if there are new resources to save.
const countNewResources = newResources.length - this.workingResources.length;
if (countNewResources > 0) {
try {
await this.saveLessonResources({
lessonId: this.currentLesson.id,
resources: newResources,
});
} catch (error) {
this.notifySaveLessonError();
this.isSaving = false;
throw error;
}
for (const resource of this.selectedResources) {
this.addToResourceCache({ node: resource });
}
this.setWorkingResources(newResources);
// Notify the lesson summary page that the working resources have been updated
// so that it can update the backup resources.
this.$emit('workingResourcesUpdated');
this.notifyResourcesAdded(countNewResources);
}
this.closeSidePanel(false);
},
closeSidePanel(verifyHasNewResources = true) {
const newResources = this.getNewResources();
const hasNewResources = newResources.length > this.workingResources.length;
if (hasNewResources && verifyHasNewResources) {
this.isCloseConfirmationModalOpen = true;
} else {
this.$router.push({
name: PageNames.LESSON_SUMMARY_BETTER,
});
}
},
setTitle(title) {
this.title = title;
Expand Down
Loading

0 comments on commit bf19897

Please sign in to comment.