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`
+
+
+
+```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;