Skip to content

Commit

Permalink
feat: updated course cards design
Browse files Browse the repository at this point in the history
  • Loading branch information
mahamakifdar19 committed Nov 3, 2023
1 parent 57b3161 commit 3538f2c
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,23 @@ class CourseSection extends React.Component {
notifications,
courseRunStatus,
isRevoked,
resumeCourseRunUrl,
...rest
}) => {
const courseRunProps = { courseRunStatus };
switch (courseRunStatus) {
case COURSE_STATUSES.inProgress:
courseRunProps.linkToCertificate = linkToCertificate;
courseRunProps.notifications = notifications;
courseRunProps.resumeCourseRunUrl = resumeCourseRunUrl;
break;
case COURSE_STATUSES.savedForLater:
courseRunProps.isRevoked = isRevoked;
courseRunProps.resumeCourseRunUrl = resumeCourseRunUrl;
break;
case COURSE_STATUSES.completed:
courseRunProps.linkToCertificate = linkToCertificate;
courseRunProps.resumeCourseRunUrl = resumeCourseRunUrl;
break;
default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,19 @@ import { Button } from '@edx/paragon';
import BaseCourseCard from './BaseCourseCard';

import { COURSE_STATUSES } from '../data';
import dayjs from '../../../../../utils/dayjs';

const AssignedCourseCard = (props) => {
const renderButtons = () => (
<Button className="btn-xs-block" variant="brand">
<Button className="btn-xs-block bg-danger text-light rounded-0" variant="outline-danger">
Enroll
</Button>
);
const { startDate } = props;
const formattedStartDate = startDate ? dayjs(startDate).format('MMMM Do, YYYY') : null;

const miscText = () => (
<small className="text-gray-500 font-weight-bold">
This course has been assigned to you by your learning administrator.&nbsp;&nbsp;
{formattedStartDate && `Start date - ${formattedStartDate}`}
</small>
);

return (
<BaseCourseCard
buttons={renderButtons()}
type={COURSE_STATUSES.assigned}
hasViewCertificateLink={false}
miscText={miscText()}
canUnenroll={false}
{...props}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
Dropdown, Badge, IconButton, Icon, Skeleton,
Dropdown, Badge, IconButton, Icon, Skeleton, OverlayTrigger, Tooltip,
} from '@edx/paragon';
import camelCase from 'lodash.camelcase';
import { AppContext } from '@edx/frontend-platform/react';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform/config';
import { MoreVert } from '@edx/paragon/icons';
import { MoreVert, InfoOutline } from '@edx/paragon/icons';

import dayjs from '../../../../../utils/dayjs';
import { EmailSettingsModal } from './email-settings';
Expand All @@ -29,7 +29,7 @@ const BADGE_PROPS_BY_COURSE_STATUS = {
children: 'Requested',
},
[COURSE_STATUSES.assigned]: {
variant: 'info',
variant: 'InfoOutline',
children: 'Assigned',
},
};
Expand Down Expand Up @@ -91,51 +91,6 @@ class BaseCourseCard extends Component {
return [...firstMenuItems, ...lastMenuItems];
};

getDateMessage = () => {
const { type, pacing, endDate } = this.props;
const formattedEndDate = endDate ? dayjs(endDate).format('MMMM D, YYYY') : null;
let message = '';
if (formattedEndDate) {
switch (type) {
case COURSE_STATUSES.inProgress: {
if (pacing === 'self') {
message += `Complete at your own speed before ${formattedEndDate}.`;
} else {
message += `Ends ${formattedEndDate}.`;
}
break;
}
case COURSE_STATUSES.upcoming:
case COURSE_STATUSES.completed:
case COURSE_STATUSES.savedForLater: {
const isCourseEnded = dayjs() > dayjs(endDate);
message += isCourseEnded ? 'Ended' : 'Ends';
message += ` ${formattedEndDate}.`;
break;
}
default:
break;
}
}
return message;
};

getCourseMiscText = () => {
const { pacing } = this.props;
const isCourseEnded = this.isCourseEnded();
const dateMessage = this.getDateMessage();
let message = '';
if (pacing) {
message += 'This course ';
message += isCourseEnded ? 'was ' : 'is ';
message += `${pacing}-led. `;
}
if (dateMessage) {
message += dateMessage;
}
return message;
};

setModalState = ({ key, open = false, options = {} }) => {
this.setState(state => ({
modals: {
Expand All @@ -148,11 +103,6 @@ class BaseCourseCard extends Component {
}));
};

isCourseEnded = () => {
const { endDate } = this.props;
return dayjs(endDate) < dayjs();
};

handleEmailSettingsButtonClick = () => {
const { courseRunId } = this.props;
const {
Expand Down Expand Up @@ -295,32 +245,17 @@ class BaseCourseCard extends Component {
return null;
};

renderAdditionalInfo = () => {
const { enterpriseConfig: { name } } = this.context;
renderAdditionalInfoOutline = () => {
const { type } = this.props;

if (type === COURSE_STATUSES.requested) {
return (
<small>
<small className="mt-2">
Please allow 5-10 business days for review.
If approved, you will receive an email to get started.
</small>
);
}

if (type === COURSE_STATUSES.assigned) {
return (
<small className="text-gray-300">
Enroll in the course in the next 14 days or before the course
start date, whichever is earlier.
</small>
);
}

if (name) {
return <small>Sponsored by {name}.</small>;
}

return null;
};

Expand All @@ -336,28 +271,80 @@ class BaseCourseCard extends Component {
return null;
};

renderCourseStartDate = () => {
const { startDate, mode } = this.props;
renderOrganizationName = () => {
const { orgName, mode } = this.props;

const isExecutiveEducation2UCourse = EXECUTIVE_EDUCATION_COURSE_MODES.includes(mode);
const execEdClass = isExecutiveEducation2UCourse ? 'text-light-300' : '';
const course = isExecutiveEducation2UCourse ? 'Executive Education' : 'Course';
const tooltipText = isExecutiveEducation2UCourse
? 'Executive Education courses are instructor-led, cohort-based, and follow a set schedule.'
: 'Courses are on-demand, self-paced, and include asynchronous online discussion.';

if (orgName) {
return (
<p className={`mb-2 font-weight-light ${execEdClass}`}>
{orgName} &#x2022; {course}
<OverlayTrigger
trigger={['hover', 'focus']}
placement="top"
overlay={(
<Tooltip variant="light" id="tooltip-top">
{tooltipText}
</Tooltip>
)}
>
<InfoOutline className="ml-2 mb-1" style={{ width: '1rem', height: '1rem' }} />
</OverlayTrigger>
</p>
);
}
return null;
};

renderStartDate = () => {
const { startDate } = this.props;
const formattedStartDate = startDate ? dayjs(startDate).format('MMMM Do, YYYY') : null;
const isCourseStarted = dayjs(startDate) <= dayjs();

if (isExecutiveEducation2UCourse && formattedStartDate) {
return <>&#x2022; Start date: {formattedStartDate}</>;
if (formattedStartDate && !isCourseStarted) {
return <span className="font-weight-light pr-2">Starts {formattedStartDate}</span>;
}
return null;
};

renderOrganizationName = () => {
const { orgName, mode } = this.props;
renderEndDate = () => {
const { endDate, type } = this.props;
const formattedEndDate = endDate ? dayjs(endDate).format('MMMM Do, YYYY') : null;
const isCourseStarted = dayjs(this.props.startDate) <= dayjs();

const isExecutiveEducation2UCourse = EXECUTIVE_EDUCATION_COURSE_MODES.includes(mode);
const execEdClass = isExecutiveEducation2UCourse ? 'text-light-300' : '';
if (orgName) {
return <p className={`mb-0 ${execEdClass}`}>{orgName} {isExecutiveEducation2UCourse && <>&#x2022; Executive Education</>} {this.renderCourseStartDate()}</p>;
if (formattedEndDate && isCourseStarted && type !== COURSE_STATUSES.completed) {
return <span className="font-weight-light pr-2">Ends {formattedEndDate}</span>;
}
return null;
};

renderEnrollByDate = () => {
const { enrollBy, type } = this.props;
const formattedEnrollByDate = enrollBy ? dayjs(enrollBy).format('MMMM Do, YYYY') : null;
const isNotEnrolled = type === COURSE_STATUSES.requested || type === COURSE_STATUSES.assigned;
// Determines if a user is not enrolled in a course based on the course enrollment status
// 'requested' and 'assigned' are not real course statuses.

if (formattedEnrollByDate && isNotEnrolled) {
return <>&#x2022;<span className="font-weight-light pl-2">Enroll by {formattedEnrollByDate}</span></>;
}
return null;
};

renderCourseInfoOutline = () => (
<p className="mt-2 mb-4">
{this.renderStartDate()}
{this.renderEndDate()}
{this.renderEnrollByDate()}
</p>
);

renderChildren = () => {
const { children } = this.props;
if (children) {
Expand All @@ -377,7 +364,7 @@ class BaseCourseCard extends Component {
if (buttons) {
return (
<div className="row">
<div className="col mb-3">
<div className="col mt-2">
{buttons}
</div>
</div>
Expand All @@ -394,7 +381,7 @@ class BaseCourseCard extends Component {

if (linkToCertificate) {
return (
<small className="mb-0">
<small className="mt-4 mb-0">
View your certificate on
{' '}
<a href={`${config.LMS_BASE_URL}/u/${username}`}>your profile →</a>
Expand All @@ -410,12 +397,7 @@ class BaseCourseCard extends Component {
if (miscText != null) {
return miscText;
}

return (
<small className="mb-0">
{this.getCourseMiscText()}
</small>
);
return null;
};

render() {
Expand All @@ -431,7 +413,7 @@ class BaseCourseCard extends Component {
const isExecutiveEducation2UCourse = EXECUTIVE_EDUCATION_COURSE_MODES.includes(mode);

return (
<div className={`dashboard-course-card py-4 border-bottom ${isExecutiveEducation2UCourse && 'exec-ed-course-card bg-dark-200 rounded-lg p-3 text-light-100'}`}>
<div className={`dashboard-course-card py-4 border-bottom ${isExecutiveEducation2UCourse && 'exec-ed-course-card rounded-lg p-3 text-light-100'}`}>
{isLoading ? (
<>
<div className="sr-only">Loading...</div>
Expand Down Expand Up @@ -460,12 +442,13 @@ class BaseCourseCard extends Component {
</div>
{this.renderSettingsDropdown(dropdownMenuItems)}
</div>
{this.renderCourseInfoOutline()}
{this.renderButtons()}
{this.renderChildren()}
<div className="course-misc-text row">
<div className={`col ${isExecutiveEducation2UCourse ? 'text-light-300' : 'text-gray'}`}>
{this.renderMiscText()}
{this.renderAdditionalInfo()}
{this.renderAdditionalInfoOutline()}
{hasViewCertificateLink && this.renderViewCertificateText()}
</div>
</div>
Expand Down Expand Up @@ -503,6 +486,7 @@ BaseCourseCard.propTypes = {
})),
isLoading: PropTypes.bool,
miscText: PropTypes.node,
enrollBy: PropTypes.string,
};

BaseCourseCard.contextType = AppContext;
Expand All @@ -522,6 +506,7 @@ BaseCourseCard.defaultProps = {
dropdownMenuItems: null,
isLoading: false,
miscText: null,
enrollBy: null,
};

export default BaseCourseCard;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const CompletedCourseCard = (props) => {
startDate,
endDate,
mode,
resumeCourseRunUrl,
} = props;
const config = getConfig();

Expand All @@ -34,6 +35,7 @@ const CompletedCourseCard = (props) => {
courseRunId={courseRunId}
mode={mode}
startDate={startDate}
resumeCourseRunUrl={resumeCourseRunUrl}
/>
);
};
Expand Down Expand Up @@ -86,13 +88,15 @@ CompletedCourseCard.propTypes = {
endDate: PropTypes.string,
startDate: PropTypes.string,
mode: PropTypes.string,
resumeCourseRunUrl: PropTypes.string,
};

CompletedCourseCard.defaultProps = {
linkToCertificate: null,
endDate: null,
startDate: null,
mode: null,
resumeCourseRunUrl: null,
};

export default CompletedCourseCard;
Loading

0 comments on commit 3538f2c

Please sign in to comment.