From 291fd3d075f288f579b3905c54b07cfba0496510 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Fri, 12 Sep 2025 14:21:09 -0500 Subject: [PATCH 1/9] feat: add course details retrieval and course enrollments API --- src/courses/api.ts | 11 +++++++++++ src/courses/hooks.ts | 23 ++++++++++++++++++++++- src/enrollments/api.ts | 29 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/enrollments/api.ts diff --git a/src/courses/api.ts b/src/courses/api.ts index bcbfca0..c77eb8a 100644 --- a/src/courses/api.ts +++ b/src/courses/api.ts @@ -27,3 +27,14 @@ export const deleteCourse = async (partnerId: string, catalogId: string, courseI throw error; } }; + +export const getCourseDetails = async (partnerId: string, catalogId: string, courseId: number) +: Promise => { + try { + const { data } = await getAuthenticatedHttpClient().get(`${getConfig().LMS_BASE_URL}/corporate_access/api/v1/partners/${partnerId}/catalogs/${catalogId}/courses/${courseId}/`); + return camelCaseObject(data); + } catch (error) { + logError(error); + return {} as CorporateCourse; + } +}; diff --git a/src/courses/hooks.ts b/src/courses/hooks.ts index bdd7ce0..7e0eba9 100644 --- a/src/courses/hooks.ts +++ b/src/courses/hooks.ts @@ -1,5 +1,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { getCourses, deleteCourse } from './api'; +import { CorporateCourse, PaginatedResponse } from '@src/app/types'; +import { getCourses, deleteCourse, getCourseDetails } from './api'; export const useCatalogCourses = (partnerId: string, catalogId: string, pageIndex, pageSize) => { const { data, isLoading } = useQuery({ @@ -26,3 +27,23 @@ export const useDeleteCatalogCourse = () => { }); return mutateAsync; }; + +export const useCourseDetails = ({ partnerId, catalogId, courseId }) => { + const queryClient = useQueryClient(); + + const allCourses = queryClient + .getQueriesData>({ queryKey: ['catalogCourses'] }) + .flatMap(([, data]) => data?.results ?? []); + const courseCached: CorporateCourse | undefined = allCourses.find((course) => course.id === Number(courseId)); + + const { data, isLoading } = useQuery({ + queryKey: ['courseDetails', partnerId, catalogId, courseId], + queryFn: () => getCourseDetails(partnerId, catalogId, courseId), + enabled: !courseCached, + }); + + return { + courseDetails: courseCached || data, + isLoadingCourseDetails: isLoading, + }; +}; diff --git a/src/enrollments/api.ts b/src/enrollments/api.ts new file mode 100644 index 0000000..1d04071 --- /dev/null +++ b/src/enrollments/api.ts @@ -0,0 +1,29 @@ +import { camelCaseObject, getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { logError } from '@edx/frontend-platform/logging'; +import { Learner, PaginatedResponse } from '../app/types'; + +export const getCourseEnrollments = async ( + partnerId: string, + catalogId: string, + courseId: string, + page: number, + pageSize: number, +): Promise> => { + try { + const url = `${getConfig().LMS_BASE_URL}/corporate_access/api/v1/partners/${partnerId}/catalogs/${catalogId}/courses/${courseId}/enrollments/?page=${page}&page_size=${pageSize}`; + const response = await getAuthenticatedHttpClient().get(url); + return camelCaseObject(response.data); + } catch (error) { + logError(error); + return { + next: null, + previous: null, + count: 0, + numPages: 0, + currentPage: 0, + start: 0, + results: [], + }; + } +}; From 83be0532b4b1d0e82e0b9d25588961036d07a574 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Fri, 12 Sep 2025 14:22:40 -0500 Subject: [PATCH 2/9] feat: add CourseEnrollmentsPage and CourseEnrollmentsList components --- src/Router.tsx | 5 +- src/enrollments/CourseEnrollmentsPage.tsx | 38 +++++++++++ .../components/CourseEnrollmentsList.tsx | 66 +++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/enrollments/CourseEnrollmentsPage.tsx create mode 100644 src/enrollments/components/CourseEnrollmentsList.tsx diff --git a/src/Router.tsx b/src/Router.tsx index fd874c4..89ff747 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -7,6 +7,7 @@ import { CatalogEditionModalProvider } from './catalogs/useCatalogEditionModal'; const CorporatePartnerPage = lazy(() => import('@src/partner/CorporatePartnerPage')); const PartnerCatalogsPage = lazy(() => import('@src/catalogs/PartnerCatalogsPage')); const CoursesPage = lazy(() => import('@src/courses/CoursesPage')); +const CourseEnrollmentsPage = lazy(() => import('@src/enrollments/CourseEnrollmentsPage')); const Router = () => ( Loading...}> @@ -17,9 +18,7 @@ const Router = () => ( - -

Course Details

-
+

404 Not Found

diff --git a/src/enrollments/CourseEnrollmentsPage.tsx b/src/enrollments/CourseEnrollmentsPage.tsx new file mode 100644 index 0000000..04636dc --- /dev/null +++ b/src/enrollments/CourseEnrollmentsPage.tsx @@ -0,0 +1,38 @@ +import { useParams } from 'wouter'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import { usePartnerDetails } from '@src/partner/hooks'; +import HeaderDescription from '@src/app/HeaderDescription'; +import { paths } from '@src/constants'; +import { useCourseDetails } from '@src/courses/hooks'; +import AppLayout from '../app/AppLayout'; +import CourseEnrollmentsList from './components/CourseEnrollmentsList'; +import messages from './messages'; + +const CourseEnrollmentsPage = () => { + const intl = useIntl(); + const { partnerId, catalogId, courseId } = useParams<{ partnerId: string, catalogId: string, courseId: string }>(); + const { partnerDetails } = usePartnerDetails({ partnerId }); + const { courseDetails } = useCourseDetails({ partnerId, catalogId, courseId }); + + return ( + + {courseDetails && ( + + )} + + + ); +}; + +export default CourseEnrollmentsPage; diff --git a/src/enrollments/components/CourseEnrollmentsList.tsx b/src/enrollments/components/CourseEnrollmentsList.tsx new file mode 100644 index 0000000..d7b350d --- /dev/null +++ b/src/enrollments/components/CourseEnrollmentsList.tsx @@ -0,0 +1,66 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import { + DataTable, TextFilter, +} from '@openedx/paragon'; + +import { useParams } from 'wouter'; +import { getCourseEnrollments } from '../api'; +import TableFooter from '../../app/TableFooter'; + +import messages from '../messages'; + +const CourseEnrollmentsList = () => { + const intl = useIntl(); + + const { partnerId, catalogId, courseId } = useParams(); + + const { data, isLoading } = useSuspenseQuery({ + queryKey: ['courseLearners'], + queryFn: () => getCourseEnrollments(partnerId!, catalogId!, courseId!, 1, 10), + }); + + return ( + `${value}%`, + }, + ]} + > + + + + + ); +}; + +export default CourseEnrollmentsList; From 92e28c8fd4c1f32ef4b67a31f56aa5bdc702e1f5 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Fri, 12 Sep 2025 14:22:51 -0500 Subject: [PATCH 3/9] feat: add internationalization messages for course enrollments --- src/enrollments/messages.js | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/enrollments/messages.js diff --git a/src/enrollments/messages.js b/src/enrollments/messages.js new file mode 100644 index 0000000..a528ec5 --- /dev/null +++ b/src/enrollments/messages.js @@ -0,0 +1,51 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + titleCorporatePage: { + id: 'course.enrollments.page.title', + defaultMessage: 'Course Enrollments', + description: 'Page title for the course enrollments page', + }, + headerStudentName: { + id: 'course.enrollments.table.header.student_name', + defaultMessage: 'Student Name', + description: 'Header for the student name column', + }, + headerEmail: { + id: 'course.enrollments.table.header.email', + defaultMessage: 'Email', + description: 'Header for the email column', + }, + headerCompletedAssessments: { + id: 'course.enrollments.table.header.completed_assessments', + defaultMessage: 'Completed Assessments', + description: 'Header for the completed assessments column', + }, + headerDueAssessments: { + id: 'course.enrollments.table.header.due_assessments', + defaultMessage: 'Assessments to be done', + description: 'Header for the due assessments column', + }, + headerProgress: { + id: 'course.enrollments.table.header.progress', + defaultMessage: 'Progress', + description: 'Header for the progress column', + }, + infoEnrollments: { + id: 'course.enrollments.info.enrollments', + defaultMessage: 'Enrollments', + description: 'Info for the number of enrollments', + }, + infoCertified: { + id: 'course.enrollments.info.certified', + defaultMessage: 'Certified students', + description: 'Info for the number of certified students', + }, + infoCompletion: { + id: 'course.enrollments.info.completion', + defaultMessage: 'Completion rate', + description: 'Info for the completion rate', + }, +}); + +export default messages; From cf54865d7257df44eb5435f50ca47ab995c8f1ea Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Fri, 12 Sep 2025 14:22:58 -0500 Subject: [PATCH 4/9] fix: change objectFit style from 'cover' to 'contain' in ImageWithSkeleton component --- src/app/ImageWithSkeleton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/ImageWithSkeleton.tsx b/src/app/ImageWithSkeleton.tsx index e80f7b9..9b60818 100644 --- a/src/app/ImageWithSkeleton.tsx +++ b/src/app/ImageWithSkeleton.tsx @@ -22,7 +22,7 @@ const ImageWithSkeleton: FC = ({ onLoad={() => setImageLoaded(true)} className={className ?? ''} style={{ - maxHeight: height, width, display: isImageLoaded ? 'block' : 'none', objectFit: 'cover', + maxHeight: height, width, display: isImageLoaded ? 'block' : 'none', objectFit: 'contain', }} /> {!isImageLoaded && } From 2c6fcab4aedd7539c0dd7c67048012ff15d79ed5 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Mon, 15 Sep 2025 15:14:10 -0500 Subject: [PATCH 5/9] refactor: fix unknown error with partial wrapping & send a falsy value when error --- src/Router.tsx | 22 +++++++++++----------- src/courses/api.ts | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Router.tsx b/src/Router.tsx index 89ff747..136930a 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -11,19 +11,19 @@ const CourseEnrollmentsPage = lazy(() => import('@src/enrollments/CourseEnrollme const Router = () => ( Loading...}> - - - - + + + + - - - -

404 Not Found

-
-
-
+ + +

404 Not Found

+
+ + +
); diff --git a/src/courses/api.ts b/src/courses/api.ts index c77eb8a..5ffcf76 100644 --- a/src/courses/api.ts +++ b/src/courses/api.ts @@ -29,12 +29,12 @@ export const deleteCourse = async (partnerId: string, catalogId: string, courseI }; export const getCourseDetails = async (partnerId: string, catalogId: string, courseId: number) -: Promise => { +: Promise => { try { const { data } = await getAuthenticatedHttpClient().get(`${getConfig().LMS_BASE_URL}/corporate_access/api/v1/partners/${partnerId}/catalogs/${catalogId}/courses/${courseId}/`); return camelCaseObject(data); } catch (error) { logError(error); - return {} as CorporateCourse; + return null; } }; From 9a68405a7f70ed055826e64e9f88abd5b5881bb0 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Thu, 18 Sep 2025 17:33:21 -0500 Subject: [PATCH 6/9] feat: enhance getCourses and useCatalogCourses to support courseOverview parameter --- src/courses/api.ts | 15 +++++++++++---- src/courses/hooks.ts | 26 +++++++++++++++++++++----- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/courses/api.ts b/src/courses/api.ts index 5ffcf76..cf56aef 100644 --- a/src/courses/api.ts +++ b/src/courses/api.ts @@ -3,12 +3,19 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { logError } from '@edx/frontend-platform/logging'; import { CorporateCourse, PaginatedResponse } from '@src/app/types'; -export const getCourses = async (partnerId: string, catalogId: string, pageIndex, pageSize) -: Promise> => { +export const getCourses = async ( + partnerId: string, + catalogId: string, + pageIndex?: number, + pageSize?: number, + courseOverview?: string, +): Promise> => { try { const url = new URL(`${getConfig().LMS_BASE_URL}/corporate_access/api/v1/partners/${partnerId}/catalogs/${catalogId}/courses/`); - url.searchParams.append('page', pageIndex); - url.searchParams.append('page_size', pageSize); + if (courseOverview) { url.searchParams.append('course_overview', courseOverview); } + if (pageIndex) { url.searchParams.append('page', String(pageIndex)); } + if (pageSize) { url.searchParams.append('page_size', String(pageSize)); } + const { data } = await getAuthenticatedHttpClient().get(url); return camelCaseObject(data); } catch (error) { diff --git a/src/courses/hooks.ts b/src/courses/hooks.ts index 7e0eba9..27c1a44 100644 --- a/src/courses/hooks.ts +++ b/src/courses/hooks.ts @@ -2,14 +2,30 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { CorporateCourse, PaginatedResponse } from '@src/app/types'; import { getCourses, deleteCourse, getCourseDetails } from './api'; -export const useCatalogCourses = (partnerId: string, catalogId: string, pageIndex, pageSize) => { +export const useCatalogCourses = ({ + partnerId, catalogId, pageIndex, pageSize, courseOverview, +} : { + partnerId: string, + catalogId: string, + pageIndex?: number, + pageSize?: number, + courseOverview?: string, +}) => { + const queryClient = useQueryClient(); + + const courseCached = queryClient + .getQueriesData>({ queryKey: ['catalogCourses'] }) + .flatMap(([, data]) => data?.results ?? []) + .filter((course) => course.courseRun.id === courseOverview); + const { data, isLoading } = useQuery({ - queryKey: ['catalogCourses', partnerId, catalogId, pageIndex, pageSize], - queryFn: () => getCourses(partnerId, catalogId, pageIndex, pageSize), + queryKey: ['catalogCourses', partnerId, catalogId, pageIndex, pageSize, courseOverview], + queryFn: () => getCourses(partnerId, catalogId, pageIndex, pageSize, courseOverview), + enabled: !courseCached.length, }); return { - courses: data?.results || [], count: data?.count || 0, pageCount: data?.numPages, isLoading, + courses: data?.results || courseCached, count: data?.count || 0, pageCount: data?.numPages, isLoading, }; }; @@ -39,7 +55,7 @@ export const useCourseDetails = ({ partnerId, catalogId, courseId }) => { const { data, isLoading } = useQuery({ queryKey: ['courseDetails', partnerId, catalogId, courseId], queryFn: () => getCourseDetails(partnerId, catalogId, courseId), - enabled: !courseCached, + enabled: !courseCached && !!courseId, }); return { From 2266ebd506953c16204c7c538d266985eaaa53fb Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Thu, 18 Sep 2025 17:53:06 -0500 Subject: [PATCH 7/9] refactor: update useCatalogCourses and useCourseDetails to use object destructuring for parameters --- src/courses/components/CoursesList.tsx | 4 +++- src/courses/hooks.test.tsx | 20 +++++++++++++++----- src/courses/hooks.ts | 6 ++++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/courses/components/CoursesList.tsx b/src/courses/components/CoursesList.tsx index 5502d3a..f21e2d3 100644 --- a/src/courses/components/CoursesList.tsx +++ b/src/courses/components/CoursesList.tsx @@ -30,7 +30,9 @@ const CoursesList = ({ partnerId, catalogId }: CoursesListProps) => { count, pageCount, isLoading, - } = useCatalogCourses(partnerId, catalogId, pageIndex + 1, pageSize); + } = useCatalogCourses({ + partnerId, catalogId, pageIndex: pageIndex + 1, pageSize, + }); const deleteCatalogCourse = useDeleteCatalogCourse(); const positions = Array.from({ length: count + 1 || 0 }, (_, i) => i); diff --git a/src/courses/hooks.test.tsx b/src/courses/hooks.test.tsx index 34c06e7..18c35f4 100644 --- a/src/courses/hooks.test.tsx +++ b/src/courses/hooks.test.tsx @@ -33,7 +33,9 @@ describe('useCatalogCourses', () => { }); const { result } = renderHook( - () => useCatalogCourses('p1', 'c1', 1, 10), + () => useCatalogCourses({ + partnerId: 'p1', catalogId: 'c1', pageIndex: 1, pageSize: 10, + }), { wrapper: createWrapper() }, ); @@ -46,7 +48,9 @@ describe('useCatalogCourses', () => { it('returns loading state', async () => { (api.getCourses as jest.Mock).mockImplementation(() => new Promise(() => {})); - const { result } = renderHook(() => useCatalogCourses('p1', 'c1', 1, 10), { + const { result } = renderHook(() => useCatalogCourses({ + partnerId: 'p1', catalogId: 'c1', pageIndex: 1, pageSize: 10, + }), { wrapper: createWrapper(), }); @@ -61,7 +65,9 @@ describe('useCatalogCourses', () => { }); const { result } = renderHook( - () => useCatalogCourses('p1', 'c1', 1, 10), + () => useCatalogCourses({ + partnerId: 'p1', catalogId: 'c1', pageIndex: 1, pageSize: 10, + }), { wrapper: createWrapper() }, ); @@ -79,7 +85,9 @@ describe('useCatalogCourses', () => { }); const { result } = renderHook( - () => useCatalogCourses('p1', 'c1', 1, 10), + () => useCatalogCourses({ + partnerId: 'p1', catalogId: 'c1', pageIndex: 1, pageSize: 10, + }), { wrapper: createWrapper() }, ); @@ -91,7 +99,9 @@ describe('useCatalogCourses', () => { (api.getCourses as jest.Mock).mockRejectedValue(new Error('API error')); const { result } = renderHook( - () => useCatalogCourses('p1', 'c1', 1, 10), + () => useCatalogCourses({ + partnerId: 'p1', catalogId: 'c1', pageIndex: 1, pageSize: 10, + }), { wrapper: createWrapper() }, ); diff --git a/src/courses/hooks.ts b/src/courses/hooks.ts index 27c1a44..4549a21 100644 --- a/src/courses/hooks.ts +++ b/src/courses/hooks.ts @@ -44,7 +44,9 @@ export const useDeleteCatalogCourse = () => { return mutateAsync; }; -export const useCourseDetails = ({ partnerId, catalogId, courseId }) => { +export const useCourseDetails = ( + { partnerId, catalogId, courseId } : { partnerId: string; catalogId: string; courseId?: number }, +) => { const queryClient = useQueryClient(); const allCourses = queryClient @@ -54,7 +56,7 @@ export const useCourseDetails = ({ partnerId, catalogId, courseId }) => { const { data, isLoading } = useQuery({ queryKey: ['courseDetails', partnerId, catalogId, courseId], - queryFn: () => getCourseDetails(partnerId, catalogId, courseId), + queryFn: () => getCourseDetails(partnerId, catalogId, courseId!), enabled: !courseCached && !!courseId, }); From 43430c77684156fd00060cdcf2428d73b3e3e99c Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Thu, 18 Sep 2025 17:56:30 -0500 Subject: [PATCH 8/9] feat: implement useCatalogCourseEnrollments hook to fetch course enrollment data --- src/enrollments/api.ts | 17 +++++++++++------ src/enrollments/hooks.ts | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/enrollments/hooks.ts diff --git a/src/enrollments/api.ts b/src/enrollments/api.ts index 1d04071..1a48622 100644 --- a/src/enrollments/api.ts +++ b/src/enrollments/api.ts @@ -3,15 +3,20 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { logError } from '@edx/frontend-platform/logging'; import { Learner, PaginatedResponse } from '../app/types'; -export const getCourseEnrollments = async ( +export const getCourseEnrollments = async ({ + partnerId, catalogId, courseId, pageIndex, pageSize, +}:{ partnerId: string, catalogId: string, - courseId: string, - page: number, - pageSize: number, -): Promise> => { + courseId: number, + pageIndex?: number, + pageSize?: number, +}): Promise> => { try { - const url = `${getConfig().LMS_BASE_URL}/corporate_access/api/v1/partners/${partnerId}/catalogs/${catalogId}/courses/${courseId}/enrollments/?page=${page}&page_size=${pageSize}`; + const url = new URL(`${getConfig().LMS_BASE_URL}/corporate_access/api/v1/partners/${partnerId}/catalogs/${catalogId}/courses/${courseId}/enrollments/`); + if (pageIndex) { url.searchParams.append('page', String(pageIndex)); } + if (pageSize) { url.searchParams.append('page_size', String(pageSize)); } + const response = await getAuthenticatedHttpClient().get(url); return camelCaseObject(response.data); } catch (error) { diff --git a/src/enrollments/hooks.ts b/src/enrollments/hooks.ts new file mode 100644 index 0000000..d231aa6 --- /dev/null +++ b/src/enrollments/hooks.ts @@ -0,0 +1,24 @@ +import { useQuery } from '@tanstack/react-query'; +import { getCourseEnrollments } from './api'; + +export const useCatalogCourseEnrollments = ({ + partnerId, catalogId, pageIndex, pageSize, courseId, +} : { + partnerId: string, + catalogId: string, + pageIndex?: number, + pageSize?: number, + courseId?: number, +}) => { + const { data, isLoading } = useQuery({ + queryKey: ['courseLearners', courseId], + queryFn: () => getCourseEnrollments({ + partnerId, catalogId, courseId: courseId!, pageIndex, pageSize, + }), + enabled: !!courseId, + }); + + return { + enrollments: data?.results || [], count: data?.count || 0, pageCount: data?.numPages, isLoading, + }; +}; From a55fe9c9e833648fa7bcf046357b055aed1fe2cd Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Thu, 18 Sep 2025 17:57:38 -0500 Subject: [PATCH 9/9] feat: use useCatalogCourseEnrollments for fetching enrollment data --- src/enrollments/CourseEnrollmentsPage.tsx | 11 +++++-- .../components/CourseEnrollmentsList.tsx | 29 +++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/enrollments/CourseEnrollmentsPage.tsx b/src/enrollments/CourseEnrollmentsPage.tsx index 04636dc..2877d7c 100644 --- a/src/enrollments/CourseEnrollmentsPage.tsx +++ b/src/enrollments/CourseEnrollmentsPage.tsx @@ -3,7 +3,7 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { usePartnerDetails } from '@src/partner/hooks'; import HeaderDescription from '@src/app/HeaderDescription'; import { paths } from '@src/constants'; -import { useCourseDetails } from '@src/courses/hooks'; +import { useCatalogCourses, useCourseDetails } from '@src/courses/hooks'; import AppLayout from '../app/AppLayout'; import CourseEnrollmentsList from './components/CourseEnrollmentsList'; import messages from './messages'; @@ -11,8 +11,13 @@ import messages from './messages'; const CourseEnrollmentsPage = () => { const intl = useIntl(); const { partnerId, catalogId, courseId } = useParams<{ partnerId: string, catalogId: string, courseId: string }>(); + + const { courses } = useCatalogCourses({ partnerId, catalogId, courseOverview: courseId }); + + const courseCatalogPK = courses.find((course) => course.courseRun.id === courseId)?.id; + const { partnerDetails } = usePartnerDetails({ partnerId }); - const { courseDetails } = useCourseDetails({ partnerId, catalogId, courseId }); + const { courseDetails } = useCourseDetails({ partnerId, catalogId, courseId: courseCatalogPK }); return ( @@ -30,7 +35,7 @@ const CourseEnrollmentsPage = () => { ]} /> )} - + ); }; diff --git a/src/enrollments/components/CourseEnrollmentsList.tsx b/src/enrollments/components/CourseEnrollmentsList.tsx index d7b350d..01298ad 100644 --- a/src/enrollments/components/CourseEnrollmentsList.tsx +++ b/src/enrollments/components/CourseEnrollmentsList.tsx @@ -1,37 +1,42 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; +import { FC } from 'react'; +import { useParams } from 'wouter'; import { useIntl } from '@edx/frontend-platform/i18n'; import { DataTable, TextFilter, } from '@openedx/paragon'; -import { useParams } from 'wouter'; -import { getCourseEnrollments } from '../api'; -import TableFooter from '../../app/TableFooter'; +import { usePagination } from '@src/hooks'; +import TableFooter from '@src/app/TableFooter'; import messages from '../messages'; +import { useCatalogCourseEnrollments } from '../hooks'; -const CourseEnrollmentsList = () => { +const CourseEnrollmentsList: FC<{ courseCatalogPK: number | undefined }> = ({ courseCatalogPK }) => { const intl = useIntl(); - const { partnerId, catalogId, courseId } = useParams(); + const { partnerId, catalogId } = useParams(); + const { pageSize, pageIndex, onPaginationChange } = usePagination(); - const { data, isLoading } = useSuspenseQuery({ - queryKey: ['courseLearners'], - queryFn: () => getCourseEnrollments(partnerId!, catalogId!, courseId!, 1, 10), + const { + enrollments, count, pageCount, isLoading, + } = useCatalogCourseEnrollments({ + partnerId: partnerId!, catalogId: catalogId!, courseId: courseCatalogPK, pageIndex: pageIndex + 1, pageSize, }); return (