diff --git a/.env b/.env index 917bdea6..3650d330 100644 --- a/.env +++ b/.env @@ -16,6 +16,7 @@ ECOMMERCE_BASE_URL='' ENABLE_JUMPNAV='true' ENABLE_NOTICES='' ENTERPRISE_LEARNER_PORTAL_HOSTNAME='' +ENTERPRISE_LEARNER_PORTAL_URL='' EXAMS_BASE_URL='' FAVICON_URL='' IGNORED_ERROR_REGEX='' diff --git a/.env.development b/.env.development index 51b6f4d7..2eff90c9 100644 --- a/.env.development +++ b/.env.development @@ -16,6 +16,7 @@ ECOMMERCE_BASE_URL='http://localhost:18130' ENABLE_JUMPNAV='true' ENABLE_NOTICES='' ENTERPRISE_LEARNER_PORTAL_HOSTNAME='localhost:8734' +ENTERPRISE_LEARNER_PORTAL_URL='http://localhost:8734' EXAMS_BASE_URL='' FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico IGNORED_ERROR_REGEX='' diff --git a/.env.test b/.env.test index f442f0aa..b17621b0 100644 --- a/.env.test +++ b/.env.test @@ -16,6 +16,7 @@ ECOMMERCE_BASE_URL='http://localhost:18130' ENABLE_JUMPNAV='true' ENABLE_NOTICES='' ENTERPRISE_LEARNER_PORTAL_HOSTNAME='localhost:8734' +ENTERPRISE_LEARNER_PORTAL_URL='http://localhost:8734' EXAMS_BASE_URL='http://localhost:18740' FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico IGNORED_ERROR_REGEX='' @@ -48,3 +49,4 @@ TWITTER_URL='https://twitter.com/edXOnline' USER_INFO_COOKIE_NAME='edx-user-info' PRIVACY_POLICY_URL='http://localhost:18000/privacy' SHOW_UNGRADED_ASSIGNMENT_PROGRESS='' +ENTERPRISE_LEARNER_PORTAL_URL='http://localhost:Enterprise' diff --git a/src/courseware/course/course-exit/CourseCelebration.jsx b/src/courseware/course/course-exit/CourseCelebration.jsx index 7fe7e08c..e7c64495 100644 --- a/src/courseware/course/course-exit/CourseCelebration.jsx +++ b/src/courseware/course/course-exit/CourseCelebration.jsx @@ -25,12 +25,12 @@ import messages from './messages'; import { useModel } from '../../../generic/model-store'; import { requestCert } from '../../../course-home/data/thunks'; import ProgramCompletion from './ProgramCompletion'; -import DashboardFootnote from './DashboardFootnote'; import UpgradeFootnote from './UpgradeFootnote'; import SocialIcons from '../../social-share/SocialIcons'; import { logClick, logVisit } from './utils'; import { DashboardLink, IdVerificationSupportLink, ProfileLink } from '../../../shared/links'; -import CourseRecommendationsSlot from '../../../plugin-slots/CourseRecommendationsSlot'; +import DashboardFootnote from './DashboardFootnote'; +import { CourseRecommendationsSlot } from '../../../plugin-slots/CourseExitPluginSlots'; const LINKEDIN_BLUE = '#2867B2'; diff --git a/src/courseware/course/course-exit/CourseExit.jsx b/src/courseware/course/course-exit/CourseExit.jsx index 60d57200..385e8021 100644 --- a/src/courseware/course/course-exit/CourseExit.jsx +++ b/src/courseware/course/course-exit/CourseExit.jsx @@ -1,8 +1,5 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; -import { getConfig } from '@edx/frontend-platform'; -import { useIntl } from '@edx/frontend-platform/i18n'; -import { Button } from '@openedx/paragon'; import { useSelector } from 'react-redux'; import { Navigate } from 'react-router-dom'; @@ -10,13 +7,12 @@ import CourseCelebration from './CourseCelebration'; import CourseInProgress from './CourseInProgress'; import CourseNonPassing from './CourseNonPassing'; import { COURSE_EXIT_MODES, getCourseExitMode } from './utils'; -import messages from './messages'; import { unsubscribeFromGoalReminders } from './data/thunks'; +import { CourseExitViewCoursesPluginSlot } from '../../../plugin-slots/CourseExitPluginSlots'; import { useModel } from '../../../generic/model-store'; const CourseExit = () => { - const intl = useIntl(); const { courseId } = useSelector(state => state.courseware); const { certificateData, @@ -64,14 +60,7 @@ const CourseExit = () => { return ( <> -
- -
+ {body} ); diff --git a/src/courseware/course/course-exit/DashboardFootnote.jsx b/src/courseware/course/course-exit/DashboardFootnote.jsx index 7ac7a440..7edbd12a 100644 --- a/src/courseware/course/course-exit/DashboardFootnote.jsx +++ b/src/courseware/course/course-exit/DashboardFootnote.jsx @@ -1,47 +1,19 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; - -import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; -import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; -import { Hyperlink } from '@openedx/paragon'; +import { useIntl } from '@edx/frontend-platform/i18n'; import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'; -import { getConfig } from '@edx/frontend-platform'; - -import { useModel } from '../../../generic/model-store'; +import { DashboardFootnoteLinkPluginSlot } from '../../../plugin-slots/CourseExitPluginSlots'; import Footnote from './Footnote'; import messages from './messages'; -import { logClick } from './utils'; const DashboardFootnote = ({ variant }) => { const intl = useIntl(); - const { courseId } = useSelector(state => state.courseware); - const { org } = useModel('courseHomeMeta', courseId); - const { administrator } = getAuthenticatedUser(); - - const dashboardLink = ( - logClick(org, courseId, administrator, 'dashboard_footnote', { variant })} - > - {intl.formatMessage(messages.dashboardLink)} - - ); + const dashboardLink = (); return ( - )} + text={intl.formatMessage(messages.dashboardInfo, { dashboardLink })} /> ); }; diff --git a/src/courseware/course/course-exit/messages.ts b/src/courseware/course/course-exit/messages.ts index 297596fe..0c0d1ff1 100644 --- a/src/courseware/course/course-exit/messages.ts +++ b/src/courseware/course/course-exit/messages.ts @@ -76,6 +76,11 @@ const messages = defineMessages({ defaultMessage: 'Dashboard', description: 'Link to user’s dashboard', }, + dashboardInfo: { + id: 'courseCelebration.dashboardInfo', // for historical reasons + defaultMessage: 'You can access this course and its materials on your {dashboardLink}.', + description: "Text that precedes link to learner's dashboard", + }, endOfCourseDescription: { id: 'courseExit.endOfCourseDescription', defaultMessage: 'Unfortunately, you are not currently eligible for a certificate. You need to receive a passing grade to be eligible for a certificate.', diff --git a/src/index.jsx b/src/index.jsx index fafe7e6d..72c4992a 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -170,6 +170,7 @@ initialize({ CREDIT_HELP_LINK_URL: process.env.CREDIT_HELP_LINK_URL || null, DISCUSSIONS_MFE_BASE_URL: process.env.DISCUSSIONS_MFE_BASE_URL || null, ENTERPRISE_LEARNER_PORTAL_HOSTNAME: process.env.ENTERPRISE_LEARNER_PORTAL_HOSTNAME || null, + ENTERPRISE_LEARNER_PORTAL_URL: process.env.ENTERPRISE_LEARNER_PORTAL_URL || null, ENABLE_JUMPNAV: process.env.ENABLE_JUMPNAV || null, ENABLE_NOTICES: process.env.ENABLE_NOTICES || null, INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null, diff --git a/src/plugin-slots/CourseExitPluginSlots/CourseExitViewCoursesPluginSlot/README.md b/src/plugin-slots/CourseExitPluginSlots/CourseExitViewCoursesPluginSlot/README.md new file mode 100644 index 00000000..fb283eb9 --- /dev/null +++ b/src/plugin-slots/CourseExitPluginSlots/CourseExitViewCoursesPluginSlot/README.md @@ -0,0 +1,37 @@ +# Course Exit "View Courses" Button Plugin Slot + +### Slot ID: `org.openedx.frontend.learning.course_exit_view_courses.v1` +### Props: +* `content: { href }` + +## Description + +This slot is used for modifying "View Courses" button in the course exit screen + +## Example + +The following `env.config.jsx` will make the link link to `example.com` + +```js +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.learning.course_exit_view_courses.v1: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: (widget) => { + widget.content.href = 'http://www.example.com'; + return widget; + } + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/CourseExitPluginSlots/CourseExitViewCoursesPluginSlot/index.tsx b/src/plugin-slots/CourseExitPluginSlots/CourseExitViewCoursesPluginSlot/index.tsx new file mode 100644 index 00000000..1948f444 --- /dev/null +++ b/src/plugin-slots/CourseExitPluginSlots/CourseExitViewCoursesPluginSlot/index.tsx @@ -0,0 +1,37 @@ +import { Button } from '@openedx/paragon'; +import { getConfig } from '@edx/frontend-platform'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import messages from '../../../courseware/course/course-exit/messages'; + +interface Props { + href: string +} + +const ViewCoursesLink: React.FC = ({ href }: Props) => { + const intl = useIntl(); + return ( +
+ +
+ ); +}; + +export const CourseExitViewCoursesPluginSlot: React.FC = () => { + const href = `${getConfig().LMS_BASE_URL}/dashboard`; + return ( + + + + ); +}; diff --git a/src/plugin-slots/CourseRecommendationsSlot/README.md b/src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/README.md similarity index 100% rename from src/plugin-slots/CourseRecommendationsSlot/README.md rename to src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/README.md diff --git a/src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/index.tsx b/src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/index.tsx new file mode 100644 index 00000000..e869310b --- /dev/null +++ b/src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/index.tsx @@ -0,0 +1,15 @@ +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import CourseRecommendations from '../../../courseware/course/course-exit/CourseRecommendations'; + +interface Props { + variant: string; +} + +export const CourseRecommendationsSlot: React.FC = ({ variant }: Props) => ( + + + +); diff --git a/src/plugin-slots/CourseRecommendationsSlot/screenshot_custom.png b/src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/screenshot_custom.png similarity index 100% rename from src/plugin-slots/CourseRecommendationsSlot/screenshot_custom.png rename to src/plugin-slots/CourseExitPluginSlots/CourseRecommendationsSlot/screenshot_custom.png diff --git a/src/plugin-slots/CourseExitPluginSlots/DashboardFootnoteLinkPluginSlot/README.md b/src/plugin-slots/CourseExitPluginSlots/DashboardFootnoteLinkPluginSlot/README.md new file mode 100644 index 00000000..67aab098 --- /dev/null +++ b/src/plugin-slots/CourseExitPluginSlots/DashboardFootnoteLinkPluginSlot/README.md @@ -0,0 +1,40 @@ +# Course Exit Dashboard Footnote Link Plugin Slot + +### Slot ID: `org.openedx.frontend.learning.course_exit_dashboard_footnote_link.v1` +### Props: +* `variant` +* `content: { destination }` + +## Description + +This slot is used for modifying the link to the learner dashboard in the footnote on the course exit page + +## Example + +The following `env.config.jsx` will change the link to point to `example.com` + +![Screenshot of modified course celebration](./screenshot_custom.png) + +```js +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.learning.course_exit_dashboard_footnote_link.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: (widget) => { + widget.content.destination = 'http://www.example.com'; + return widget; + } + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/CourseExitPluginSlots/DashboardFootnoteLinkPluginSlot/index.tsx b/src/plugin-slots/CourseExitPluginSlots/DashboardFootnoteLinkPluginSlot/index.tsx new file mode 100644 index 00000000..4bb90095 --- /dev/null +++ b/src/plugin-slots/CourseExitPluginSlots/DashboardFootnoteLinkPluginSlot/index.tsx @@ -0,0 +1,49 @@ +import { Hyperlink } from '@openedx/paragon'; +import { getConfig } from '@edx/frontend-platform'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; +import messages from '../../../courseware/course/course-exit/messages'; +import { logClick } from '../../../courseware/course/course-exit/utils'; +import { useModel } from '../../../generic/model-store'; +import { useContextId } from '../../../data/hooks'; + +interface LinkProps { + variant: string; + destination: string; +} + +const DashboardFootnoteLink: React.FC = ({ variant, destination }: LinkProps) => { + const intl = useIntl(); + const courseId = useContextId(); + const { org } = useModel('courseHomeMeta', courseId); + const { administrator } = getAuthenticatedUser(); + return ( + logClick(org, courseId, administrator, 'dashboard_footnote', { variant })} + > + {intl.formatMessage(messages.dashboardLink)} + + ); +}; + +interface PluginProps { + variant: string +} + +export const DashboardFootnoteLinkPluginSlot: React.FC = ({ variant }: PluginProps) => { + const destination = `${getConfig().LMS_BASE_URL}/dashboard`; + return ( + + + + ); +}; diff --git a/src/plugin-slots/CourseExitPluginSlots/index.jsx b/src/plugin-slots/CourseExitPluginSlots/index.jsx new file mode 100644 index 00000000..235ac99a --- /dev/null +++ b/src/plugin-slots/CourseExitPluginSlots/index.jsx @@ -0,0 +1,9 @@ +import { DashboardFootnoteLinkPluginSlot } from './DashboardFootnoteLinkPluginSlot'; +import { CourseRecommendationsSlot } from './CourseRecommendationsSlot'; +import { CourseExitViewCoursesPluginSlot } from './CourseExitViewCoursesPluginSlot'; + +export { + DashboardFootnoteLinkPluginSlot, + CourseRecommendationsSlot, + CourseExitViewCoursesPluginSlot, +}; diff --git a/src/plugin-slots/CourseRecommendationsSlot/index.jsx b/src/plugin-slots/CourseRecommendationsSlot/index.jsx deleted file mode 100644 index c286ba51..00000000 --- a/src/plugin-slots/CourseRecommendationsSlot/index.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import PropTypes from 'prop-types'; -import { PluginSlot } from '@openedx/frontend-plugin-framework'; -import CourseRecommendations from '../../courseware/course/course-exit/CourseRecommendations'; - -const CourseRecommendationsSlot = ({ variant }) => ( - - - -); - -CourseRecommendationsSlot.propTypes = { - variant: PropTypes.string.isRequired, -}; - -export default CourseRecommendationsSlot;