diff --git a/src/components/dashboard/main-content/course-enrollments/course-cards/InProgressCourseCard.jsx b/src/components/dashboard/main-content/course-enrollments/course-cards/InProgressCourseCard.jsx index d4f6f249dc..253f957963 100644 --- a/src/components/dashboard/main-content/course-enrollments/course-cards/InProgressCourseCard.jsx +++ b/src/components/dashboard/main-content/course-enrollments/course-cards/InProgressCourseCard.jsx @@ -1,14 +1,22 @@ -import { useContext, useState } from 'react'; +/* eslint-disable no-unsafe-optional-chaining */ + +import { + useCallback, useContext, useEffect, useState, +} from 'react'; import PropTypes from 'prop-types'; import { useNavigate } from 'react-router-dom'; import { AppContext } from '@edx/frontend-platform/react'; +import { logError } from '@edx/frontend-platform/logging'; import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils'; import { defineMessages, FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; -import { Stack } from '@openedx/paragon'; +import { ProgressBar, Stack } from '@openedx/paragon'; +import { getConfig } from '@edx/frontend-platform/config'; import dayjs from '../../../../../utils/dayjs'; import BaseCourseCard, { getScreenReaderText } from './BaseCourseCard'; import { MarkCompleteModal } from './mark-complete-modal'; import ContinueLearningButton from './ContinueLearningButton'; +import { isExperimentVariant } from '../../../../../utils/optimizely'; +import { getProgressTabData } from './mark-complete-modal/data/service'; import Notification from './Notification'; @@ -43,6 +51,11 @@ const messages = defineMessages({ defaultMessage: 'Covered by your organization', description: 'Text for the course info outline upgrade covered by organization in the course card dropdown menu', }, + completion: { + id: 'enterprise.learner_portal.dashboard.enrollments.course.completion', + defaultMessage: '{courseProgress}% completed', + description: 'Course progress percentage completed', + }, }); function useLinkToCourse({ @@ -87,6 +100,32 @@ export const InProgressCourseCard = ({ const { data: enterpriseCustomer } = useEnterpriseCustomer(); const updateCourseEnrollmentStatus = useUpdateCourseEnrollmentStatus({ enterpriseCustomer }); const isExecutiveEducation = EXECUTIVE_EDUCATION_COURSE_MODES.includes(mode); + const config = getConfig(); + + const [completionSummary, setCompletionSummary] = useState({}); + const fetchProgressData = useCallback( + async (courseRun) => { + try { + const result = await getProgressTabData(courseRun); + setCompletionSummary(result.completionSummary); + } catch (error) { + logError(error); + } + }, + [], + ); + + useEffect(() => { + fetchProgressData(courseRunId); + }, [fetchProgressData, courseRunId]); + + const completeCount = completionSummary?.completeCount; + const numTotalUnits = completeCount + completionSummary?.incompleteCount + completionSummary?.lockedCount; + const completePercentage = completeCount ? Number(((completeCount / numTotalUnits) * 100).toFixed(0)) : 0; + const isExperimentVariation = isExperimentVariant( + config.PROGRESS_BAR_EXPERIMENT_ID, + config.PROGRESS_BAR_EXPERIMENT_VARIANT_ID, + ); const coursewareOrUpgradeLink = useLinkToCourse({ linkToCourse, @@ -245,6 +284,13 @@ export const InProgressCourseCard = ({ {...rest} > {renderNotifications()} + {isExperimentVariation && ( + + )} { } return getAuthenticatedHttpClient().patch(url); }; + +export async function getProgressTabData(courseId, targetUserId) { + let url = `${getConfig().LMS_BASE_URL}/api/course_home/progress/${courseId}`; + + if (targetUserId) { + url += `/${targetUserId}/`; + } + + try { + const { data } = await getAuthenticatedHttpClient().get(url); + const camelCasedData = camelCaseObject(data); + return camelCasedData; + } catch (error) { + logError(error); + return null; + } +} diff --git a/src/components/dashboard/main-content/course-enrollments/course-cards/styles/_CourseCard.scss b/src/components/dashboard/main-content/course-enrollments/course-cards/styles/_CourseCard.scss index ec68b1e509..2ed9f1717b 100644 --- a/src/components/dashboard/main-content/course-enrollments/course-cards/styles/_CourseCard.scss +++ b/src/components/dashboard/main-content/course-enrollments/course-cards/styles/_CourseCard.scss @@ -37,3 +37,17 @@ } } } + +.pgn__progress-annotated { + padding-bottom: 0 !important; + .pgn__progress-bar--success { + height: 15px; + } +} + +.pgn__progress-annotated .progress .pgn__progress-bar--success::after { + width: 22px; + height: 22px; + top: -0.25rem; + border-radius: 1rem; +} \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx index 548abe2365..0a7f59c432 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -59,6 +59,8 @@ initialize({ EXPERIMENT_2_VARIANT_2_ID: process.env.EXPERIMENT_2_VARIANT_2_ID || null, PREQUERY_SEARCH_EXPERIMENT_ID: process.env.PREQUERY_SEARCH_EXPERIMENT_ID || null, PREQUERY_SEARCH_EXPERIMENT_VARIANT_ID: process.env.PREQUERY_SEARCH_EXPERIMENT_VARIANT_ID || null, + PROGRESS_BAR_EXPERIMENT_ID: process.env.PROGRESS_BAR_EXPERIMENT_ID || null, + PROGRESS_BAR_EXPERIMENT_VARIANT_ID: process.env.PROGRESS_BAR_EXPERIMENT_VARIANT_ID || null, }); }, },