diff --git a/plugins/index.js b/plugins/index.js index e7c234cad..1dbf8fae7 100644 --- a/plugins/index.js +++ b/plugins/index.js @@ -13,3 +13,6 @@ export { export { IFRAME_PLUGIN, } from './data/constants'; +export { + default as PluginErrorBoundary, +} from './PluginErrorBoundary'; diff --git a/src/profile/ProfilePluginPage.jsx b/src/profile/ProfilePluginPage.jsx index b1b1baad8..9a35bad8c 100644 --- a/src/profile/ProfilePluginPage.jsx +++ b/src/profile/ProfilePluginPage.jsx @@ -1,31 +1,30 @@ +/* eslint-disable react/prop-types */ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { ensureConfig } from '@edx/frontend-platform'; -import { AppContext, ErrorBoundary } from '@edx/frontend-platform/react'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { AppContext } from '@edx/frontend-platform/react'; +import { injectIntl, intlShape, FormattedDate } from '@edx/frontend-platform/i18n'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons'; +import { + ActionRow, Avatar, Card, Hyperlink, Icon, +} from '@edx/paragon'; +import { HistoryEdu, VerifiedUser } from '@edx/paragon/icons'; + +import get from 'lodash.get'; + +import PluginCountry from './forms/PluginCountry'; import { Plugin } from '../../plugins'; // Actions import { fetchProfile, - saveProfile, - saveProfilePhoto, - deleteProfilePhoto, - openForm, - closeForm, - updateDraft, } from './data/actions'; // Components -import ProfileAvatar from './forms/ProfileAvatar'; -import Name from './forms/Name'; -import Country from './forms/Country'; -import Education from './forms/Education'; -import SocialLinks from './forms/SocialLinks'; -import Bio from './forms/Bio'; -import DateJoined from './DateJoined'; import PageLoading from './PageLoading'; // Selectors @@ -33,6 +32,7 @@ import { profilePageSelector } from './data/selectors'; // i18n import messages from './ProfilePage.messages'; +import eduMessages from './forms/Education.messages'; import withParams from '../utils/hoc'; @@ -45,143 +45,99 @@ function Fallback() { ); } -class ProfilePluginPage extends React.Component { - constructor(props, context) { - super(props, context); - - this.handleSaveProfilePhoto = this.handleSaveProfilePhoto.bind(this); - this.handleDeleteProfilePhoto = this.handleDeleteProfilePhoto.bind(this); - this.handleClose = this.handleClose.bind(this); - this.handleOpen = this.handleOpen.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleChange = this.handleChange.bind(this); - } +const platformDisplayInfo = { + facebook: { + icon: faFacebook, + name: '', + }, + twitter: { + icon: faTwitter, + name: '', + }, + linkedin: { + icon: faLinkedin, + name: '', + }, +}; +class ProfilePluginPage extends React.Component { componentDidMount() { this.props.fetchProfile(this.props.params.username); } - handleSaveProfilePhoto(formData) { - this.props.saveProfilePhoto(this.context.authenticatedUser.username, formData); - } - - handleDeleteProfilePhoto() { - this.props.deleteProfilePhoto(this.context.authenticatedUser.username); - } - - handleClose(formId) { - this.props.closeForm(formId); - } - - handleOpen(formId) { - this.props.openForm(formId); - } - - handleSubmit(formId) { - this.props.saveProfile(formId, this.context.authenticatedUser.username); - } - - handleChange(name, value) { - this.props.updateDraft(name, value); - } - - // Inserted into the DOM in two places (for responsive layout) - renderHeadingLockup() { - const { dateJoined } = this.props; - return ( - }> - -

{this.props.params.username}

- -
-
-
- ); - } - renderContent() { const { profileImage, - name, - visibilityName, country, - visibilityCountry, levelOfEducation, - visibilityLevelOfEducation, socialLinks, - visibilitySocialLinks, - bio, - visibilityBio, isLoadingProfile, + dateJoined, + intl, } = this.props; if (isLoadingProfile) { return ; } - const commonFormProps = { - openHandler: this.handleOpen, - closeHandler: this.handleClose, - submitHandler: this.handleSubmit, - changeHandler: this.handleChange, - }; - return ( - -
-
-
-
- -
-
-
-
- {this.renderHeadingLockup()} -
-
-
-
-
- - - - -
-
- -
-
-
+ }> + + + View public profile + + )} + actions={ + ( + + {socialLinks + .filter(({ socialLink }) => Boolean(socialLink)) + .map(({ platform, socialLink }) => ( + + ))} + + ) + } + /> + + +

{this.props.params.username}

+ +
+ + + +

+ since +

+
+ + +

+ {intl.formatMessage(get( + eduMessages, + `profile.education.levels.${levelOfEducation}`, + eduMessages['profile.education.levels.o'], + ))} +

+
+
+
); } @@ -195,52 +151,46 @@ class ProfilePluginPage extends React.Component { } } +const SocialLink = ({ url, name, platform }) => ( + + + {name} + +); + +const StaticListItem = ({ url, name, platform }) => ( + +); + ProfilePluginPage.contextType = AppContext; ProfilePluginPage.propTypes = { // Account data dateJoined: PropTypes.string, - // Bio form data - bio: PropTypes.string, - yearOfBirth: PropTypes.number, - visibilityBio: PropTypes.string.isRequired, - // Country form data country: PropTypes.string, - visibilityCountry: PropTypes.string.isRequired, // Education form data levelOfEducation: PropTypes.string, - visibilityLevelOfEducation: PropTypes.string.isRequired, - - // Name form data - name: PropTypes.string, - visibilityName: PropTypes.string.isRequired, // Social links form data socialLinks: PropTypes.arrayOf(PropTypes.shape({ platform: PropTypes.string, socialLink: PropTypes.string, })), - visibilitySocialLinks: PropTypes.string.isRequired, // Other data we need profileImage: PropTypes.shape({ src: PropTypes.string, isDefault: PropTypes.bool, }), - saveState: PropTypes.oneOf([null, 'pending', 'complete', 'error']), isLoadingProfile: PropTypes.bool.isRequired, // Actions fetchProfile: PropTypes.func.isRequired, - saveProfile: PropTypes.func.isRequired, - saveProfilePhoto: PropTypes.func.isRequired, - deleteProfilePhoto: PropTypes.func.isRequired, - openForm: PropTypes.func.isRequired, - closeForm: PropTypes.func.isRequired, - updateDraft: PropTypes.func.isRequired, // Router params: PropTypes.shape({ @@ -252,14 +202,10 @@ ProfilePluginPage.propTypes = { }; ProfilePluginPage.defaultProps = { - saveState: null, profileImage: {}, - name: null, - yearOfBirth: null, levelOfEducation: null, country: null, socialLinks: [], - bio: null, dateJoined: null, }; @@ -267,11 +213,5 @@ export default connect( profilePageSelector, { fetchProfile, - saveProfilePhoto, - deleteProfilePhoto, - saveProfile, - openForm, - closeForm, - updateDraft, }, )(injectIntl(withParams(ProfilePluginPage))); diff --git a/src/profile/forms/PluginCountry.jsx b/src/profile/forms/PluginCountry.jsx new file mode 100644 index 000000000..540d71c84 --- /dev/null +++ b/src/profile/forms/PluginCountry.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { injectIntl } from '@edx/frontend-platform/i18n'; +import { Icon } from '@edx/paragon'; +import { LocationOn } from '@edx/paragon/icons'; + +// Selectors +import { countrySelector } from '../data/selectors'; + +// eslint-disable-next-line react/prefer-stateless-function +class PluginCountry extends React.Component { + render() { + const { + country, + countryMessages, + } = this.props; + + return ( +
+ +

{countryMessages[country]}

+
+ ); + } +} + +PluginCountry.propTypes = { + country: PropTypes.string, + countryMessages: PropTypes.objectOf(PropTypes.string).isRequired, +}; + +PluginCountry.defaultProps = { + country: null, +}; + +export default connect( + countrySelector, + {}, +)(injectIntl(PluginCountry)); diff --git a/src/profile/index.scss b/src/profile/index.scss index cb362b1e3..d6eba2d32 100644 --- a/src/profile/index.scss +++ b/src/profile/index.scss @@ -162,4 +162,28 @@ position: relative; } } + + .pgn-icons-cell-vertical { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + margin: 1px; + } + .pgn-icons-cell-horizontal { + display: flex; + flex-direction: row; + justify-content: center; + margin: 1px; + } + + .profile-plugin-avatar { + @include media-breakpoint-up(xs) { + max-width: 12rem; + margin-right: 0; + margin-top: -4rem; + margin-bottom: 1rem; + } + } + }