-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: reskin of Profile MFE main page
- Loading branch information
1 parent
ca2a79d
commit 38b8019
Showing
42 changed files
with
16,810 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Alert } from '@openedx/paragon'; | ||
import { FormattedMessage } from '@edx/frontend-platform/i18n'; | ||
import { getConfig } from '@edx/frontend-platform'; | ||
|
||
const AgeMessage = ({ accountSettingsUrl }) => ( | ||
<Alert | ||
variant="info" | ||
dismissible={false} | ||
show | ||
> | ||
<Alert.Heading id="profile.age.headline"> | ||
<FormattedMessage | ||
id="profile.age.cannotShare" | ||
defaultMessage="Your profile cannot be shared." | ||
description="Error message indicating that the user's profile cannot be shared" | ||
/> | ||
</Alert.Heading> | ||
<FormattedMessage | ||
id="profile.age.details" | ||
defaultMessage="To share your profile with other {siteName} learners, you must confirm that you are over the age of 13." | ||
description="Error message" | ||
tagName="p" | ||
values={{ | ||
siteName: getConfig().SITE_NAME, | ||
}} | ||
/> | ||
<Alert.Link href={accountSettingsUrl}> | ||
<FormattedMessage | ||
id="profile.age.set.date" | ||
defaultMessage="Set your date of birth" | ||
description="Label on a link to set birthday" | ||
/> | ||
</Alert.Link> | ||
</Alert> | ||
); | ||
|
||
AgeMessage.propTypes = { | ||
accountSettingsUrl: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default AgeMessage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import React from 'react'; | ||
|
||
const Banner = () => <div className="profile-page-bg-banner bg-primary d-md-block p-relative" />; | ||
|
||
export default Banner; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { FormattedMessage } from '@edx/frontend-platform/i18n'; | ||
|
||
const CertificateCount = ({ count }) => { | ||
if (count === 0) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<span className="small m-0 text-gray-800"> | ||
<FormattedMessage | ||
id="profile.certificatecount" | ||
defaultMessage="{certificate_count} certifications" | ||
description="A label for many certificates a user has" | ||
values={{ | ||
certificate_count: <span className="font-weight-bold"> {count} </span>, | ||
}} | ||
/> | ||
</span> | ||
); | ||
}; | ||
|
||
CertificateCount.propTypes = { | ||
count: PropTypes.number, | ||
}; | ||
CertificateCount.defaultProps = { | ||
count: 0, | ||
}; | ||
|
||
export default CertificateCount; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import React, { useCallback, useMemo } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { | ||
FormattedDate, FormattedMessage, useIntl, | ||
} from '@edx/frontend-platform/i18n'; | ||
import { Hyperlink } from '@openedx/paragon'; | ||
import { connect } from 'react-redux'; | ||
import get from 'lodash.get'; | ||
|
||
import { getConfig } from '@edx/frontend-platform'; | ||
import messages from './Certificates.messages'; | ||
|
||
// Assets | ||
import professionalCertificateSVG from './assets/professional-certificate.svg'; | ||
import verifiedCertificateSVG from './assets/verified-certificate.svg'; | ||
|
||
// Selectors | ||
import { certificatesSelector } from './data/selectors'; | ||
|
||
const Certificates = ({ | ||
certificates, | ||
}) => { | ||
const intl = useIntl(); | ||
|
||
// Memoizing the renderCertificate function to prevent unnecessary re-renders | ||
const renderCertificate = useCallback(({ | ||
certificateType, courseDisplayName, courseOrganization, modifiedDate, downloadUrl, courseId, | ||
}) => { | ||
const certificateIllustration = (() => { | ||
switch (certificateType) { | ||
case 'professional': | ||
case 'no-id-professional': | ||
return professionalCertificateSVG; | ||
case 'verified': | ||
return verifiedCertificateSVG; | ||
case 'honor': | ||
case 'audit': | ||
default: | ||
return null; | ||
} | ||
})(); | ||
|
||
return ( | ||
<div | ||
key={`${modifiedDate}-${courseId}`} | ||
className="col-auto d-flex align-items-center p-0" | ||
> | ||
<div className="col certificate p-4 border-light-400 bg-light-200 w-100 h-100"> | ||
<div | ||
className="certificate-type-illustration" | ||
style={{ backgroundImage: `url(${certificateIllustration})` }} | ||
/> | ||
<div className="card-body d-flex flex-column p-0 width-19625rem"> | ||
<div className="w-100 color-black"> | ||
<p className="small mb-0 font-weight-normal"> | ||
{intl.formatMessage(get( | ||
messages, | ||
`profile.certificates.types.${certificateType}`, | ||
messages['profile.certificates.types.unknown'], | ||
))} | ||
</p> | ||
<div className="h4 m-0 line-height-1575rem">{courseDisplayName}</div> | ||
<p className="small mb-0"> | ||
<FormattedMessage | ||
id="profile.certificate.organization.label" | ||
defaultMessage="From" | ||
/> | ||
</p> | ||
<p className="h5 mb-0">{courseOrganization}</p> | ||
<p className="small mb-0"> | ||
<FormattedMessage | ||
id="profile.certificate.completion.date.label" | ||
defaultMessage="Completed on {date}" | ||
values={{ | ||
date: <FormattedDate value={new Date(modifiedDate)} />, | ||
}} | ||
/> | ||
</p> | ||
</div> | ||
<div className="pt-3"> | ||
<Hyperlink | ||
destination={downloadUrl} | ||
target="_blank" | ||
showLaunchIcon={false} | ||
className="btn btn-primary btn-rounded font-weight-normal px-4 py-0625rem" | ||
> | ||
{intl.formatMessage(messages['profile.certificates.view.certificate'])} | ||
</Hyperlink> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}, [intl]); | ||
|
||
// Memoizing the renderCertificates to avoid recalculations | ||
const renderCertificates = useMemo(() => { | ||
if (!certificates || certificates.length === 0) { | ||
return ( | ||
<FormattedMessage | ||
id="profile.no.certificates" | ||
defaultMessage="You don't have any certificates yet." | ||
description="displays when user has no course completion certificates" | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="col"> | ||
<div className="row align-items-center pt-5 g-3rem"> | ||
{certificates.map(certificate => renderCertificate(certificate))} | ||
</div> | ||
</div> | ||
); | ||
}, [certificates, renderCertificate]); | ||
|
||
// Main Render | ||
return ( | ||
<div> | ||
<div className="col justify-content-start align-items-start g-5rem p-0"> | ||
<div className="col align-self-stretch height-2625rem justify-content-start align-items-start p-0"> | ||
<h2 className="font-weight-bold text-primary-500 m-0"> | ||
<FormattedMessage | ||
id="profile.your.certificates" | ||
defaultMessage="Your certificates" | ||
description="heading for the certificates section" | ||
/> | ||
</h2> | ||
</div> | ||
<div className="col justify-content-start align-items-start pt-2 p-0"> | ||
<p className="font-weight-normal text-gray-800 m-0 p-0"> | ||
<FormattedMessage | ||
id="profile.certificates.description" | ||
defaultMessage="Your learner records information is only visible to you. Only your username is visible to others on {siteName}." | ||
description="description of the certificates section" | ||
values={{ | ||
siteName: getConfig().SITE_NAME, | ||
}} | ||
/> | ||
</p> | ||
</div> | ||
</div> | ||
{renderCertificates} | ||
</div> | ||
); | ||
}; | ||
|
||
Certificates.propTypes = { | ||
|
||
// From Selector | ||
certificates: PropTypes.arrayOf(PropTypes.shape({ | ||
title: PropTypes.string, | ||
})), | ||
}; | ||
|
||
Certificates.defaultProps = { | ||
certificates: null, | ||
}; | ||
|
||
export default connect( | ||
certificatesSelector, | ||
{}, | ||
)(Certificates); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { defineMessages } from '@edx/frontend-platform/i18n'; | ||
|
||
const messages = defineMessages({ | ||
'profile.certificates.my.certificates': { | ||
id: 'profile.certificates.my.certificates', | ||
defaultMessage: 'My Certificates', | ||
description: 'A section of a user profile', | ||
}, | ||
'profile.certificates.view.certificate': { | ||
id: 'profile.certificates.view.certificate', | ||
defaultMessage: 'View Certificate', | ||
description: 'A call to action to view a certificate', | ||
}, | ||
'profile.certificates.types.verified': { | ||
id: 'profile.certificates.types.verified', | ||
defaultMessage: 'Verified Certificate', | ||
description: 'A type of certificate a user may have earned', | ||
}, | ||
'profile.certificates.types.professional': { | ||
id: 'profile.certificates.types.professional', | ||
defaultMessage: 'Professional Certificate', | ||
description: 'A type of certificate a user may have earned', | ||
}, | ||
'profile.certificates.types.unknown': { | ||
id: 'profile.certificates.types.unknown', | ||
defaultMessage: 'Certificate', | ||
description: 'The string to display when a certificate is of an unknown type', | ||
}, | ||
}); | ||
|
||
export default messages; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n'; | ||
|
||
const DateJoined = ({ date }) => { | ||
if (date == null) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<span className="small mb-0 text-gray-800"> | ||
<FormattedMessage | ||
id="profile.datejoined.member.since" | ||
defaultMessage="Member since {year}" | ||
description="A label for how long the user has been a member" | ||
values={{ | ||
year: <span className="font-weight-bold"> <FormattedDate value={new Date(date)} year="numeric" /> </span>, | ||
}} | ||
/> | ||
</span> | ||
); | ||
}; | ||
|
||
DateJoined.propTypes = { | ||
date: PropTypes.string, | ||
}; | ||
DateJoined.defaultProps = { | ||
date: null, | ||
}; | ||
|
||
export default DateJoined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React from 'react'; | ||
import { FormattedMessage } from '@edx/frontend-platform/i18n'; | ||
|
||
const NotFoundPage = () => ( | ||
<div className="container-fluid d-flex py-5 justify-content-center align-items-start text-center"> | ||
<p className="my-0 py-5 text-muted" style={{ maxWidth: '32em' }}> | ||
<FormattedMessage | ||
id="profile.notfound.message" | ||
defaultMessage="The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again." | ||
description="error message when a page does not exist" | ||
/> | ||
</p> | ||
</div> | ||
); | ||
|
||
export default NotFoundPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import React, { Component } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
export default class PageLoading extends Component { | ||
renderSrMessage() { | ||
if (!this.props.srMessage) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<span className="sr-only"> | ||
{this.props.srMessage} | ||
</span> | ||
); | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<div | ||
className="d-flex justify-content-center align-items-center flex-column" | ||
style={{ | ||
height: '50vh', | ||
}} | ||
> | ||
<div className="spinner-border text-primary" role="status"> | ||
{this.renderSrMessage()} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
PageLoading.propTypes = { | ||
srMessage: PropTypes.string.isRequired, | ||
}; |
Oops, something went wrong.