feat: add course end dashboard plugin slots (#1658)
* feat: add additional course end plugin slots * fix: bring plugin slot names in line with new naming scheme * refactor: change plugin files to tsx,remove propTypes * fixup! refactor: change plugin files to tsx,remove propTypes * fixup! fixup! refactor: change plugin files to tsx,remove propTypes * fixup! fixup! fixup! refactor: change plugin files to tsx,remove propTypes * fix: accidentally committed test code * fix: plugin-slot fixes * chore: add ENTERPRISE_LEARNER_PORTAL_URL env var
This commit is contained in:
1
.env
1
.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=''
|
||||
|
||||
@@ -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=''
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className="row w-100 mt-2 mb-4 justify-content-end">
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
href={`${getConfig().LMS_BASE_URL}/dashboard`}
|
||||
>
|
||||
{intl.formatMessage(messages.viewCoursesButton)}
|
||||
</Button>
|
||||
</div>
|
||||
<CourseExitViewCoursesPluginSlot />
|
||||
{body}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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 = (
|
||||
<Hyperlink
|
||||
style={{ textDecoration: 'underline' }}
|
||||
destination={`${getConfig().LMS_BASE_URL}/dashboard`}
|
||||
className="text-reset"
|
||||
onClick={() => logClick(org, courseId, administrator, 'dashboard_footnote', { variant })}
|
||||
>
|
||||
{intl.formatMessage(messages.dashboardLink)}
|
||||
</Hyperlink>
|
||||
);
|
||||
const dashboardLink = (<DashboardFootnoteLinkPluginSlot variant={variant} />);
|
||||
|
||||
return (
|
||||
<Footnote
|
||||
icon={faCalendarAlt}
|
||||
text={(
|
||||
<FormattedMessage
|
||||
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"
|
||||
values={{ dashboardLink }}
|
||||
/>
|
||||
)}
|
||||
text={intl.formatMessage(messages.dashboardInfo, { dashboardLink })}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
```
|
||||
@@ -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<Props> = ({ href }: Props) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div className="row w-100 mt-2 mb-4 justify-content-end">
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
href={href}
|
||||
>
|
||||
{intl.formatMessage(messages.viewCoursesButton)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const CourseExitViewCoursesPluginSlot: React.FC = () => {
|
||||
const href = `${getConfig().LMS_BASE_URL}/dashboard`;
|
||||
return (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.learning.course_exit_view_courses.v1"
|
||||
slotOptions={{
|
||||
mergeProps: true,
|
||||
}}
|
||||
>
|
||||
<ViewCoursesLink href={href} />
|
||||
</PluginSlot>
|
||||
);
|
||||
};
|
||||
@@ -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<Props> = ({ variant }: Props) => (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.learning.course_recommendations.v1"
|
||||
idAliases={['course_recommendations_slot']}
|
||||
>
|
||||
<CourseRecommendations variant={variant} />
|
||||
</PluginSlot>
|
||||
);
|
||||
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
@@ -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;
|
||||
```
|
||||
@@ -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<LinkProps> = ({ variant, destination }: LinkProps) => {
|
||||
const intl = useIntl();
|
||||
const courseId = useContextId();
|
||||
const { org } = useModel('courseHomeMeta', courseId);
|
||||
const { administrator } = getAuthenticatedUser();
|
||||
return (
|
||||
<Hyperlink
|
||||
style={{ textDecoration: 'underline' }}
|
||||
destination={destination}
|
||||
className="text-reset"
|
||||
onClick={() => logClick(org, courseId, administrator, 'dashboard_footnote', { variant })}
|
||||
>
|
||||
{intl.formatMessage(messages.dashboardLink)}
|
||||
</Hyperlink>
|
||||
);
|
||||
};
|
||||
|
||||
interface PluginProps {
|
||||
variant: string
|
||||
}
|
||||
|
||||
export const DashboardFootnoteLinkPluginSlot: React.FC = ({ variant }: PluginProps) => {
|
||||
const destination = `${getConfig().LMS_BASE_URL}/dashboard`;
|
||||
return (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.learning.course_exit_dashboard_footnote_link.v1"
|
||||
slotOptions={{
|
||||
mergeProps: true,
|
||||
}}
|
||||
>
|
||||
<DashboardFootnoteLink variant={variant} destination={destination} />
|
||||
</PluginSlot>
|
||||
);
|
||||
};
|
||||
9
src/plugin-slots/CourseExitPluginSlots/index.jsx
Normal file
9
src/plugin-slots/CourseExitPluginSlots/index.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { DashboardFootnoteLinkPluginSlot } from './DashboardFootnoteLinkPluginSlot';
|
||||
import { CourseRecommendationsSlot } from './CourseRecommendationsSlot';
|
||||
import { CourseExitViewCoursesPluginSlot } from './CourseExitViewCoursesPluginSlot';
|
||||
|
||||
export {
|
||||
DashboardFootnoteLinkPluginSlot,
|
||||
CourseRecommendationsSlot,
|
||||
CourseExitViewCoursesPluginSlot,
|
||||
};
|
||||
@@ -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 }) => (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.learning.course_recommendations.v1"
|
||||
idAliases={['course_recommendations_slot']}
|
||||
>
|
||||
<CourseRecommendations variant={variant} />
|
||||
</PluginSlot>
|
||||
);
|
||||
|
||||
CourseRecommendationsSlot.propTypes = {
|
||||
variant: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default CourseRecommendationsSlot;
|
||||
Reference in New Issue
Block a user