From 1c67deccb8c722c045162b25f638926e9932914e Mon Sep 17 00:00:00 2001 From: jenniw Date: Mon, 18 Mar 2024 13:13:48 -0400 Subject: [PATCH] Fetch by department when selected (#2120) * set state issue * fix all * Works, but there seems to be a miss on one of the state variables and pulling items while pulling for the filtered list. * fmt * infinite loop of API calls * Can load all pages * Collin's changes Mainly just moving around the setting of the newDepartment variable. * move to changeselecteddepartment * we produce enough, but too many * we produce enough, but too many * test still fails * fix dupes * fix coursescount * program changes * fixed duplicate items and deduped code a little. Need to finish refactor of dupe code + make sure callback goes back in when going back to ALL_XXXXX * Figured out the program issues, stopped duplicate requests and fixed swapping department io on both sides * prevprops wasn't in componentDidUpdate * wrong variable name after merge * wrong variable name after merge * wrong variable name after merge * added countAndRetrieveMoreCourses and countAndRetrieveMorePrograms * behave with far less code except the programs retrieves on every call * programs fixed again * hold * 2/4 fixed * 3/4 fixed * remove print statements * updated for format * fixed, finally. * add test for mergeNewObjects --------- Co-authored-by: Collin Preston --- .../src/containers/pages/CatalogPage.js | 337 +++++++++++++----- .../src/containers/pages/CatalogPage_test.js | 83 +++-- .../public/src/lib/queries/catalogCourses.js | 9 +- frontend/public/src/lib/queries/courses.js | 9 +- 4 files changed, 319 insertions(+), 119 deletions(-) diff --git a/frontend/public/src/containers/pages/CatalogPage.js b/frontend/public/src/containers/pages/CatalogPage.js index ae1d353d0..5aee27335 100644 --- a/frontend/public/src/containers/pages/CatalogPage.js +++ b/frontend/public/src/containers/pages/CatalogPage.js @@ -59,7 +59,9 @@ export class CatalogPage extends React.Component { state = { tabSelected: COURSES_TAB, allCoursesRetrieved: [], + allCoursesCount: 0, allProgramsRetrieved: [], + allProgramsCount: 0, filteredCourses: [], filteredPrograms: [], filterProgramsCalled: false, @@ -71,7 +73,8 @@ export class CatalogPage extends React.Component { items_per_row: 3, courseQueryPage: 1, programQueryPage: 1, - isLoadingMoreItems: false + isLoadingMoreItems: false, + queryIDListString: "" } constructor(props) { @@ -97,54 +100,67 @@ export class CatalogPage extends React.Component { * updated allCoursesRetrieved or allProgramsRetrieved state variable. */ bottomOfLoadedCatalogCallback = async entries => { + const { + coursesIsLoading, + getNextCoursePage, + getNextProgramPage, + programsIsLoading, + programsNextPage + } = this.props const [entry] = entries if (entry.isIntersecting) { if (this.state.tabSelected === COURSES_TAB) { - const { getNextCoursePage, coursesNextPage } = this.props - // Only request the next page if a next page exists (coursesNextPage) // and if we aren't already requesting the next page (isLoadingMoreItems). - if (coursesNextPage && !this.state.isLoadingMoreItems) { + if ( + !coursesIsLoading && + !this.state.isLoadingMoreItems && + this.state.filteredCourses.length < + this.renderNumberOfCatalogCourses() + ) { this.setState({ isLoadingMoreItems: true }) - this.setState({ courseQueryPage: this.state.courseQueryPage + 1 }) - const response = await getNextCoursePage(this.state.courseQueryPage) + const response = await getNextCoursePage( + this.state.courseQueryPage, + this.state.queryIDListString + ) this.setState({ isLoadingMoreItems: false }) + this.setState({ courseQueryPage: this.state.courseQueryPage + 1 }) if (response.body.results) { + const allCourses = this.mergeNewObjects( + this.state.allCoursesRetrieved, + response.body.results + ) const filteredCourses = this.filteredCoursesBasedOnCourseRunCriteria( this.state.selectedDepartment, - [...this.state.allCoursesRetrieved, ...response.body.results] + allCourses ) this.setState({ filteredCourses: filteredCourses }) this.setState({ - allCoursesRetrieved: [ - ...this.state.allCoursesRetrieved, - ...response.body.results - ] + allCoursesRetrieved: allCourses }) } } } else { - const { getNextProgramPage, programsNextPage } = this.props - if (programsNextPage && !this.state.isLoadingMoreItems) { + if ( + !programsIsLoading && + !this.state.isLoadingMoreItems && + programsNextPage + ) { this.setState({ isLoadingMoreItems: true }) - const response = await getNextProgramPage( - this.state.programQueryPage + 1 - ) - this.setState({ isLoadingMoreItems: false }) - this.setState({ programQueryPage: this.state.programQueryPage + 1 }) - if (response.body.results) { + getNextProgramPage(this.state.programQueryPage).then(response => { + this.setState({ isLoadingMoreItems: false }) + this.setState({ programQueryPage: this.state.programQueryPage + 1 }) + const updatedAllPrograms = this.mergeNewObjects( + this.state.allProgramsRetrieved, + response.body.results + ) const filteredPrograms = this.filteredProgramsByDepartmentAndCriteria( this.state.selectedDepartment, - [...this.state.allProgramsRetrieved, ...response.body.results] + updatedAllPrograms ) this.setState({ filteredPrograms: filteredPrograms }) - this.setState({ - allProgramsRetrieved: [ - ...this.state.allProgramsRetrieved, - ...response.body.results - ] - }) - } + this.setState({ allProgramsRetrieved: updatedAllPrograms }) + }) } } } @@ -157,34 +173,18 @@ export class CatalogPage extends React.Component { * Updates the filteredDepartments state variable once departmentsIsLoading * is false. */ - componentDidUpdate = () => { + componentDidUpdate = (prevProps, prevState) => { const { courses, + coursesCount, coursesIsLoading, programsIsLoading, - programs + programs, + programsCount, + departments, + departmentsIsLoading } = this.props - if (!coursesIsLoading && !this.state.filterCoursesCalled) { - this.setState({ filterCoursesCalled: true }) - this.setState({ allCoursesRetrieved: courses }) - const filteredCourses = this.filteredCoursesBasedOnCourseRunCriteria( - this.state.selectedDepartment, - courses - ) - this.setState({ filteredCourses: filteredCourses }) - - // Detect when the bottom of the catalog page has been reached and display more catalog items. - this.io = new window.IntersectionObserver( - this.bottomOfLoadedCatalogCallback, - { threshold: 1.0 } - ) - this.io.observe(this.container.current) - } - - if ( - !this.props.departmentsIsLoading && - !this.state.filterDepartmentsCalled - ) { + if (!departmentsIsLoading && !this.state.filterDepartmentsCalled) { this.setState({ filterDepartmentsCalled: true }) this.setState({ filteredDepartments: this.filterDepartmentsByTabName( @@ -192,17 +192,52 @@ export class CatalogPage extends React.Component { ) }) } - if (!programsIsLoading && !this.state.filterProgramsCalled) { - this.setState({ filterProgramsCalled: true }) + // Initialize allCourses and allPrograms variables in state once they finish loading to store since the value will + // change when changing departments + if (this.state.allCoursesCount === 0 && !coursesIsLoading) { + this.setState({ allCoursesCount: coursesCount }) + } + if (this.state.allProgramsCount === 0 && !programsIsLoading) { + this.setState({ allProgramsCount: programsCount }) + } + if (this.state.allCoursesRetrieved.length === 0 && !coursesIsLoading) { + this.setState({ allCoursesRetrieved: courses }) + this.setState({ filteredCourses: courses }) + } + if (this.state.allProgramsRetrieved.length === 0 && !programsIsLoading) { this.setState({ allProgramsRetrieved: programs }) - const filteredPrograms = this.filteredProgramsByDepartmentAndCriteria( - this.state.selectedDepartment, - programs - ) - this.setState({ - filteredPrograms: filteredPrograms - }) - // Detect when the bottom of the catalog page has been reached and display more catalog items. + this.setState({ filteredPrograms: programs }) + } + if (!departmentsIsLoading && departments.length > 0) { + if (!coursesIsLoading && this.state.filterCoursesCalled) { + if (this.state.selectedDepartment !== prevState.selectedDepartment) { + this.resetQueryVariablesToDefault() + } + } + if (!coursesIsLoading && !this.state.filterCoursesCalled) { + this.setState({ filterCoursesCalled: true }) + const filteredCourses = this.filteredCoursesBasedOnCourseRunCriteria( + this.state.selectedDepartment, + this.state.allCoursesRetrieved + ) + this.setState({ filteredCourses: filteredCourses }) + this.countAndRetrieveMoreCourses( + filteredCourses, + this.state.selectedDepartment + ) + } + if (!programsIsLoading && this.state.filterProgramsCalled) { + if (this.state.selectedDepartment !== prevState.selectedDepartment) { + this.resetQueryVariablesToDefault() + } + } + if (!programsIsLoading && !this.state.filterProgramsCalled) { + this.setState({ filterProgramsCalled: true }) + this.countAndRetrieveMorePrograms( + this.state.allProgramsRetrieved, + this.state.selectedDepartment + ) + } this.io = new window.IntersectionObserver( this.bottomOfLoadedCatalogCallback, { threshold: 1.0 } @@ -241,6 +276,20 @@ export class CatalogPage extends React.Component { } } + /** + * Resets the query-related variables to their default values. + * This is used when the selected department or tab changes to restart the api calls from the beginning. + */ + resetQueryVariablesToDefault() { + if (this.state.tabSelected === COURSES_TAB) { + this.setState({ courseQueryPage: 1 }) + this.setState({ queryIDListString: "" }) + this.setState({ filterCoursesCalled: false }) + } else { + this.setState({ filterProgramsCalled: false }) + } + } + /** * Updates this.state.selectedDepartment to {ALL_DEPARTMENTS}, * updates this.state.tabSelected to the parameter, @@ -252,7 +301,9 @@ export class CatalogPage extends React.Component { */ changeSelectedTab = (selectTabName: string) => { this.setState({ tabSelected: selectTabName }) - + this.setState({ + filteredDepartments: this.filterDepartmentsByTabName(selectTabName) + }) if (selectTabName === PROGRAMS_TAB) { const { programs, programsIsLoading } = this.props if (!programsIsLoading) { @@ -265,19 +316,48 @@ export class CatalogPage extends React.Component { } else { programsToFilter.push(...this.state.allProgramsRetrieved) } - - const filteredPrograms = this.filteredProgramsByDepartmentAndCriteria( + if (this.renderNumberOfCatalogPrograms() === 0) { + this.setState({ selectedDepartment: ALL_DEPARTMENTS }) + } + this.countAndRetrieveMorePrograms( + programsToFilter, + this.state.selectedDepartment + ) + } + } + if (selectTabName === COURSES_TAB) { + const { courses, coursesIsLoading } = this.props + if (!coursesIsLoading) { + const coursesToFilter = [] + if (this.renderNumberOfCatalogCourses() === 0) { + this.setState({ selectedDepartment: ALL_DEPARTMENTS }) + } + // The first time that a user switches to the courses tab, allCoursesRetrieved will be + // empty and should be populated with the results from the first courses API call. + if (this.state.allCoursesRetrieved.length === 0) { + this.setState({ allCoursesRetrieved: courses }) + coursesToFilter.push(...courses) + } else { + coursesToFilter.push(...this.state.allCoursesRetrieved) + } + const filteredCourses = this.filteredCoursesBasedOnCourseRunCriteria( this.state.selectedDepartment, - programsToFilter + coursesToFilter ) this.setState({ - filteredPrograms: filteredPrograms + filteredCourses: filteredCourses }) + this.countAndRetrieveMoreCourses( + filteredCourses, + this.state.selectedDepartment + ) } } - this.setState({ - filteredDepartments: this.filterDepartmentsByTabName(selectTabName) - }) + this.io = new window.IntersectionObserver( + this.bottomOfLoadedCatalogCallback, + { threshold: 1.0 } + ) + this.io.observe(this.container.current) } /** @@ -294,20 +374,107 @@ export class CatalogPage extends React.Component { * @param {string} selectedDepartment The department name to set selectedDepartment to and filter courses by. */ changeSelectedDepartment = (selectedDepartment: string) => { + this.resetQueryVariablesToDefault() this.setState({ selectedDepartment: selectedDepartment }) + const filteredCourses = this.filteredCoursesBasedOnCourseRunCriteria( + selectedDepartment, + this.state.allCoursesRetrieved + ) this.setState({ - filteredCourses: this.filteredCoursesBasedOnCourseRunCriteria( - selectedDepartment, - this.state.allCoursesRetrieved - ) - }) - this.setState({ - filteredPrograms: this.filteredProgramsByDepartmentAndCriteria( - selectedDepartment, - this.state.allProgramsRetrieved - ) + filteredCourses: filteredCourses }) this.toggleMobileFilterWindowExpanded(false) + if (this.state.tabSelected === COURSES_TAB) { + this.countAndRetrieveMoreCourses(filteredCourses, selectedDepartment) + } else if (this.state.tabSelected === PROGRAMS_TAB) { + this.countAndRetrieveMorePrograms( + this.state.allProgramsRetrieved, + selectedDepartment + ) + } + this.io = new window.IntersectionObserver( + this.bottomOfLoadedCatalogCallback, + { threshold: 1.0 } + ) + this.io.observe(this.container.current) + } + + countAndRetrieveMoreCourses(filteredCourses, selectedDepartment) { + const { departments, getNextCoursePage } = this.props + if ( + selectedDepartment !== ALL_DEPARTMENTS && + selectedDepartment !== "" && + departments.length > 0 + ) { + const newDepartment = this.props.departments.find( + department => department.name === selectedDepartment + ) + if ( + filteredCourses.length !== newDepartment.course_ids.length && + !this.state.isLoadingMoreItems + ) { + const remainingIDs = newDepartment.course_ids.filter( + id => + !this.state.allCoursesRetrieved + .map(course => course.id) + .includes(id) + ) + this.setState({ isLoadingMoreItems: true }) + getNextCoursePage(1, remainingIDs.toString()).then(response => { + const allCourses = this.mergeNewObjects( + this.state.allCoursesRetrieved, + response.body.results + ) + this.setState({ allCoursesRetrieved: allCourses }) + this.setState({ courseQueryPage: 2 }) + this.setState({ queryIDListString: remainingIDs.toString() }) + const filteredCourses = this.filteredCoursesBasedOnCourseRunCriteria( + selectedDepartment, + allCourses + ) + this.setState({ filteredCourses: filteredCourses }) + this.setState({ filterCoursesCalled: true }) + this.setState({ isLoadingMoreItems: false }) + }) + } + } + } + countAndRetrieveMorePrograms(allPrograms, selectedDepartment) { + this.setState({ filterProgramsCalled: true }) + const { programsNextPage, getNextProgramPage } = this.props + let filteredPrograms = this.filteredProgramsByDepartmentAndCriteria( + selectedDepartment, + allPrograms + ) + this.setState({ filteredPrograms: filteredPrograms }) + if ( + programsNextPage && + !this.state.isLoadingMoreItems && + this.state.allProgramsRetrieved.length < this.state.allProgramsCount + ) { + this.setState({ isLoadingMoreItems: true }) + getNextProgramPage(this.state.programQueryPage).then(response => { + const updatedAllPrograms = this.mergeNewObjects( + allPrograms, + response.body.results + ) + this.setState({ allProgramsRetrieved: updatedAllPrograms }) + this.setState({ programQueryPage: this.state.programQueryPage + 1 }) + filteredPrograms = this.filteredProgramsByDepartmentAndCriteria( + selectedDepartment, + updatedAllPrograms + ) + this.setState({ isLoadingMoreItems: false }) + this.setState({ filteredPrograms: filteredPrograms }) + }) + } + } + + mergeNewObjects(oldArray, newArray) { + const oldIds = oldArray.map(a => a.id) + const newObjects = newArray.filter(a => !oldIds.includes(a.id)) + console.log(oldArray, newArray, newObjects) + return oldArray.concat(newObjects) } /** @@ -374,9 +541,9 @@ export class CatalogPage extends React.Component { * Returns the number of courseRuns or programs based on the selected catalog tab. */ renderNumberOfCatalogCourses() { - const { coursesCount, departments } = this.props + const { departments } = this.props if (this.state.selectedDepartment === ALL_DEPARTMENTS) { - return coursesCount + return this.state.allCoursesCount } else if (this.state.selectedDepartment !== ALL_DEPARTMENTS) { return departments.find( department => department.name === this.state.selectedDepartment @@ -385,9 +552,9 @@ export class CatalogPage extends React.Component { } renderNumberOfCatalogPrograms() { - const { programsCount, departments } = this.props + const { departments } = this.props if (this.state.selectedDepartment === ALL_DEPARTMENTS) { - return programsCount + return this.state.allProgramsCount } else if (this.state.selectedDepartment !== ALL_DEPARTMENTS) { return departments.find( department => department.name === this.state.selectedDepartment @@ -670,9 +837,9 @@ const courseLoaderGrid = ( ) -const getNextCoursePage = page => +const getNextCoursePage = (page, ids) => requestAsync({ - ...coursesQuery(page), + ...coursesQuery(page, ids), force: true }) @@ -683,7 +850,7 @@ const getNextProgramPage = page => }) const mapPropsToConfig = () => [ - coursesQuery(1), + coursesQuery(1, ""), programsQuery(1), departmentsQuery(1) ] diff --git a/frontend/public/src/containers/pages/CatalogPage_test.js b/frontend/public/src/containers/pages/CatalogPage_test.js index 554c022d4..8e8fd69fb 100644 --- a/frontend/public/src/containers/pages/CatalogPage_test.js +++ b/frontend/public/src/containers/pages/CatalogPage_test.js @@ -181,9 +181,11 @@ describe("CatalogPage", function() { }, {} ) + inner.instance().componentDidUpdate({}, {}) expect(inner.state().selectedDepartment).equals("All Departments") expect(inner.state().tabSelected).equals("courses") + inner.instance().componentDidUpdate({}, {}) expect(JSON.stringify(inner.state().filteredCourses)).equals( JSON.stringify(courses) ) @@ -612,7 +614,10 @@ describe("CatalogPage", function() { }, {} ) + + expect(inner.state().courseQueryPage).equals(1) inner.instance().componentDidUpdate({}, {}) + inner.state().courseQueryPage = 2 expect(inner.state().selectedDepartment).equals("All Departments") expect(inner.state().tabSelected).equals("courses") expect(JSON.stringify(inner.state().filteredCourses)).equals( @@ -623,18 +628,20 @@ describe("CatalogPage", function() { ) // one shows visually, but the total is 2 expect(inner.instance().renderNumberOfCatalogCourses()).equals(2) - expect(inner.state().courseQueryPage).equals(1) + const course2 = JSON.parse(JSON.stringify(displayedCourse)) + course2.id = 3 + course2.departments = [{ name: "Math" }, { name: "History" }] // Mock the second page of course API results. helper.handleRequestStub.returns({ body: { next: null, - results: courses + results: [course2] } }) - // Simulate the user reaching the bottom of the catalog page. const entry = [{ isIntersecting: true }] + await inner.instance().bottomOfLoadedCatalogCallback(entry) sinon.assert.calledWith( @@ -644,12 +651,12 @@ describe("CatalogPage", function() { ) // Should expect 2 courses to be visually displayed in the catalog now. Total count should stay 2. - expect(inner.state().courseQueryPage).equals(2) + expect(inner.state().courseQueryPage).equals(3) expect(JSON.stringify(inner.state().allCoursesRetrieved)).equals( - JSON.stringify([displayedCourse, displayedCourse]) + JSON.stringify([displayedCourse, course2]) ) expect(JSON.stringify(inner.state().filteredCourses)).equals( - JSON.stringify([displayedCourse, displayedCourse]) + JSON.stringify([displayedCourse, course2]) ) expect(inner.instance().renderNumberOfCatalogCourses()).equals(2) }) @@ -753,7 +760,7 @@ describe("CatalogPage", function() { // Set isLoadingMoreItems to true which simualtes that the next page // request is already in progress. - inner.instance().setState({ isLoadingMoreItems: true }) + inner.state().isLoadingMoreItems = true await inner.instance().bottomOfLoadedCatalogCallback(entry) // Should not expect any additional courses. @@ -793,7 +800,7 @@ describe("CatalogPage", function() { next: "http://fake.com/api/courses/?page=2" }, programs: { - count: 2, + count: 4, results: programs, next: "http://fake.com/api/courses/?page=2" }, @@ -820,18 +827,12 @@ describe("CatalogPage", function() { expect(JSON.stringify(inner.state().allProgramsRetrieved)).equals( JSON.stringify(programs) ) - // While there is only one showing, there are still 2 total. The total should be shown. - expect(inner.instance().renderNumberOfCatalogPrograms()).equals(2) - expect(inner.state().programQueryPage).equals(1) + // While there is only one showing, there are still 4 total per the count. The total should be shown. + expect(inner.instance().renderNumberOfCatalogPrograms()).equals(4) - // Mock the second page of program API results. - helper.handleRequestStub.returns({ - body: { - next: null, - results: programs, - count: 2 - } - }) + // simulate the state variables changing correctly since the shallow render doesn't actually change the state + inner.state().programQueryPage = 2 + inner.state().isLoadingMoreItems = false // Simulate the user reaching the bottom of the catalog page. const entry = [{ isIntersecting: true }] @@ -843,16 +844,42 @@ describe("CatalogPage", function() { "GET" ) - // Should expect 2 courses to be displayed in the catalog now. - expect(inner.state().programQueryPage).equals(2) - expect(JSON.stringify(inner.state().allProgramsRetrieved)).equals( - JSON.stringify([displayedProgram, displayedProgram]) - ) - expect(JSON.stringify(inner.state().filteredPrograms)).equals( - JSON.stringify([displayedProgram, displayedProgram]) + // The count should still be 4 regardless of how many are added or not, since the highest provided count was 4. + expect(inner.instance().renderNumberOfCatalogPrograms()).equals(4) + }) + + it("mergeNewObjects removes duplicates if present", async () => { + const oldArray = [displayedProgram] + const newArray = [displayedProgram] + const { inner } = await renderPage() + const mergedArray = inner.instance().mergeNewObjects(oldArray, newArray) + expect(mergedArray.length).equals(1) + expect(JSON.stringify(mergedArray)).equals(JSON.stringify(oldArray)) + }) + + it("mergeNewObjects merges objects if they are not duplicates", async () => { + const oldArray = [displayedProgram] + const newProgram = JSON.parse(JSON.stringify(displayedProgram)) + newProgram.id = 3 + const newArray = [newProgram] + const { inner } = await renderPage() + const mergedArray = inner.instance().mergeNewObjects(oldArray, newArray) + expect(mergedArray.length).equals(2) + expect(JSON.stringify(mergedArray)).equals( + JSON.stringify([displayedProgram, newProgram]) ) + }) - // This should still be 2 because we haven't changed the filter - no matter if one or two have loaded, there are 2 - expect(inner.instance().renderNumberOfCatalogPrograms()).equals(2) + it("mergeNewObjects keeps items with different IDs and removes duplicates", async () => { + const oldArray = [displayedProgram] + const newProgram = JSON.parse(JSON.stringify(displayedProgram)) + newProgram.id = 3 + const newArray = [newProgram, displayedProgram, displayedProgram] + const { inner } = await renderPage() + const mergedArray = inner.instance().mergeNewObjects(oldArray, newArray) + expect(mergedArray.length).equals(2) + expect(JSON.stringify(mergedArray)).equals( + JSON.stringify([displayedProgram, newProgram]) + ) }) }) diff --git a/frontend/public/src/lib/queries/catalogCourses.js b/frontend/public/src/lib/queries/catalogCourses.js index 0c4bfe67a..e08e3eeb2 100644 --- a/frontend/public/src/lib/queries/catalogCourses.js +++ b/frontend/public/src/lib/queries/catalogCourses.js @@ -18,9 +18,12 @@ export const coursesNextPageSelector = pathOr(null, [ export const coursesQueryKey = "courses" -export const coursesQuery = page => ({ - queryKey: coursesQueryKey, - url: `/api/v2/courses/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true`, +export const coursesQuery = (page, ids) => ({ + queryKey: coursesQueryKey, + url: + ids && ids.length > 0 + ? `/api/v2/courses/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true&id=${ids}` + : `/api/v2/courses/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true`, transform: json => ({ courses: json }), diff --git a/frontend/public/src/lib/queries/courses.js b/frontend/public/src/lib/queries/courses.js index 65294d934..4642939b7 100644 --- a/frontend/public/src/lib/queries/courses.js +++ b/frontend/public/src/lib/queries/courses.js @@ -12,9 +12,12 @@ export const coursesNextPageSelector = pathOr(null, [ export const coursesQueryKey = "courses" -export const coursesQuery = page => ({ - queryKey: coursesQueryKey, - url: `/api/courses/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true`, +export const coursesQuery = (page, ids) => ({ + queryKey: coursesQueryKey, + url: + ids.length > 0 + ? `/api/courses/v2/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true&id=${ids}` + : `/api/courses/v2/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true`, transform: json => ({ courses: json }),