diff --git a/.circleci/config.yml b/.circleci/config.yml
index aa0445c1ed..30e6fbba04 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -363,7 +363,7 @@ workflows:
filters:
branches:
only:
- - feature/dice-setup
+ - feat/badges-box
# This is beta env for production soft releases
- "build-prod-beta":
context : org-global
diff --git a/src/shared/actions/page/profile.js b/src/shared/actions/page/profile.js
index cd8d10f6fe..60e15d117b 100644
--- a/src/shared/actions/page/profile.js
+++ b/src/shared/actions/page/profile.js
@@ -21,11 +21,11 @@ async function getGamificationBadgesInit(handle) {
* @param {String} handle Topcoder member handle.
* @return {Action}
*/
-async function getGamificationBadgesDone(handle) {
+async function getGamificationBadgesDone(handle, limit) {
try {
const memberInfo = await fetch(`${config.API.V5}/members/${handle}`)
.then(response => response.json());
- const badges = await fetch(`${config.API.V5}/gamification/badges/assigned/${memberInfo.userId}?organization_id=${config.GAMIFICATION.ORG_ID}`)
+ const badges = await fetch(`${config.API.V5}/gamification/badges/assigned/${memberInfo.userId}?organization_id=${config.GAMIFICATION.ORG_ID}&limit=${limit || 4}`)
.then(response => response.json());
return {
diff --git a/src/shared/components/ProfileBadgesPage/index.jsx b/src/shared/components/ProfileBadgesPage/index.jsx
new file mode 100644
index 0000000000..fd69254b3a
--- /dev/null
+++ b/src/shared/components/ProfileBadgesPage/index.jsx
@@ -0,0 +1,120 @@
+import React, { useState } from 'react';
+import PT from 'prop-types';
+import { Link } from 'react-router-dom';
+import { get } from 'lodash';
+import { Modal } from 'topcoder-react-ui-kit';
+import IconClose from 'assets/images/tc-edu/icon-close-big.svg';
+import FallBackAwardIcon from 'assets/images/default-award.svg';
+import md from 'utils/markdown';
+import { format } from 'date-fns';
+import AwardModal from '../ProfilePage/Awards/AwardModal';
+
+import style from './styles.scss';
+
+const ProfileBadges = ({ badges, handleParam }) => {
+ const [showModal, setShowModal] = useState(false);
+ const [modalData, setModalData] = useState({});
+
+ return (
+
Community Awards & Honors
+
+ View All Badges
+
@@ -29,6 +38,10 @@ const Awards = ({ badges }) => {
if (description) {
description = md(description);
}
+ let awardedAt = _.get(reward, 'awarded_at');
+ if (awardedAt) {
+ awardedAt = format(new Date(awardedAt), 'PPP');
+ }
return (
{
title,
description,
imageUrl,
+ awardedAt,
});
}}
/>
@@ -77,6 +91,7 @@ Awards.defaultProps = {
Awards.propTypes = {
badges: PT.arrayOf(PT.shape()),
+ info: PT.shape().isRequired,
};
export default Awards;
diff --git a/src/shared/components/ProfilePage/Awards/styles.scss b/src/shared/components/ProfilePage/Awards/styles.scss
index 2fc895902b..8b272ce659 100644
--- a/src/shared/components/ProfilePage/Awards/styles.scss
+++ b/src/shared/components/ProfilePage/Awards/styles.scss
@@ -19,6 +19,13 @@
cursor: pointer;
margin-top: 5px;
}
+
+ .badgesPageLink {
+ text-transform: uppercase;
+ color: $listing-checkbox-green;
+ font-weight: 700;
+ font-family: Roboto, sans-serif;
+ }
}
.awards {
@@ -28,7 +35,7 @@
padding-bottom: 32px;
.header {
- padding: 32px 0 20px 32px;
+ padding: 32px 32px 20px 32px;
@include xs-to-sm {
padding: 16px 0 16px 16px;
diff --git a/src/shared/components/ProfilePage/index.jsx b/src/shared/components/ProfilePage/index.jsx
index c981a9fd20..60953d0d5d 100644
--- a/src/shared/components/ProfilePage/index.jsx
+++ b/src/shared/components/ProfilePage/index.jsx
@@ -229,7 +229,7 @@ class ProfilePage extends React.Component {
{
(config.GAMIFICATION.ENABLE_BADGE_UI && badges && (badges.rows || [])).length ? (
-
+
) : null
}
{tcAcademyCertifications.length > 0 && (
diff --git a/src/shared/containers/ProfileBadges.jsx b/src/shared/containers/ProfileBadges.jsx
new file mode 100644
index 0000000000..889fa40962
--- /dev/null
+++ b/src/shared/containers/ProfileBadges.jsx
@@ -0,0 +1,69 @@
+import React, { useEffect } from 'react';
+import PT from 'prop-types';
+import { connect } from 'react-redux';
+import profileActions from 'actions/page/profile';
+import MetaTags from 'components/MetaTags';
+import LoadingIndicator from 'components/LoadingIndicator';
+import ProfileBadgesPage from 'components/ProfileBadgesPage';
+import { isEmpty } from 'lodash';
+
+function ProfileBadgesContainer(props) {
+ const { handleParam, badges, loadBadges } = props;
+ const title = `${handleParam} | Community Profile | Topcoder`;
+ const description = `Meet Topcoder member ${handleParam} and view their community awards and honors.`;
+
+ useEffect(() => {
+ loadBadges(handleParam);
+ }, []);
+
+ return (
+
+
+ {
+ !isEmpty(badges) ? (
+
+ ) :
+ }
+
+ );
+}
+
+ProfileBadgesContainer.defaultProps = {
+ profile: null,
+};
+
+ProfileBadgesContainer.propTypes = {
+ profile: PT.shape(),
+ loadBadges: PT.func.isRequired,
+ handleParam: PT.string.isRequired,
+ badges: PT.shape().isRequired,
+};
+
+function mapStateToProps(state, ownProps) {
+ const profile = state.auth && state.auth.profile ? { ...state.auth.profile } : {};
+ return {
+ handleParam: ownProps.match.params.handle,
+ badges: state.page.profile[ownProps.match.params.handle]
+ ? state.page.profile[ownProps.match.params.handle].badges : {},
+ profile,
+ };
+}
+
+function mapDispatchToActions(dispatch) {
+ return {
+ loadBadges: (handle) => {
+ dispatch(profileActions.page.profile.getGamificationBadgesInit(handle));
+ dispatch(profileActions.page.profile.getGamificationBadgesDone(handle, 100));
+ },
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToActions,
+)(ProfileBadgesContainer);
diff --git a/src/shared/routes/ProfileBadges.jsx b/src/shared/routes/ProfileBadges.jsx
new file mode 100644
index 0000000000..d53ea77376
--- /dev/null
+++ b/src/shared/routes/ProfileBadges.jsx
@@ -0,0 +1,27 @@
+/**
+ * The loader of Profile webpack chunks.
+ */
+import path from 'path';
+import React from 'react';
+
+import LoadingPagePlaceholder from 'components/LoadingPagePlaceholder';
+import { AppChunk, webpack } from 'topcoder-react-utils';
+
+export default function ProfileLoader(props) {
+ return (
+
import(/* webpackChunkName: "profile-badges/chunk" */ 'containers/ProfileBadges')
+ .then(({ default: ProfileBadgesContainer }) => (
+
+ ))
+ }
+ renderPlaceholder={() => }
+ renderServer={() => {
+ const p = webpack.resolveWeak('containers/Profile');
+ const ProfileBadgesContainer = webpack.requireWeak(path.resolve(__dirname, p));
+ return ;
+ }}
+ />
+ );
+}
diff --git a/src/shared/routes/Topcoder/Routes.jsx b/src/shared/routes/Topcoder/Routes.jsx
index c45868ad5c..769904af87 100644
--- a/src/shared/routes/Topcoder/Routes.jsx
+++ b/src/shared/routes/Topcoder/Routes.jsx
@@ -31,6 +31,7 @@ import Notifications from './Notifications';
import Settings from '../Settings';
import HallOfFame from '../HallOfFame';
import Profile from '../Profile';
+import ProfileBadges from '../ProfileBadges';
import Scoreboard from '../tco/scoreboard';
import MemberSearch from '../../containers/MemberSearch';
@@ -89,6 +90,15 @@ export default function Topcoder() {
exact
path="/members/:handle([\w\-\[\].{} ]{2,15})"
/>
+ {
+ config.GAMIFICATION.ENABLE_BADGE_UI && (
+
+ )
+ }
}
path="/settings"