- { imageUrl ?
:
}
-
-
-
- {info.handle}
-
-
- {country}
- {Boolean(wins) && (
-
- {' '}
- |
- {' '}
- {wins}
- {' '}
- Wins
-
- ) }
-
-
- Member Since
- {' '}
- {moment(info.createdAt).format('MMMM, YYYY')}
-
+ { imageUrl
+ ?
+ : (
+ // eslint-disable-next-line global-require
+
+ )
+}
- {
- info.tracks && info.tracks.length > 0
- && (
-
-
+
+
+
+ {handle}
- )
- }
- { info.description && (
-
- {info.description}
-
- ) }
-
+
- );
- }
-}
+
+ );
+};
+
ProfileHeader.defaultProps = {
- copilot: false,
- hasMM: false,
- country: '',
info: {},
- onShowBadges: noop,
- showBadgesButton: false,
- wins: 0,
};
ProfileHeader.propTypes = {
- copilot: PT.bool,
- hasMM: PT.bool,
- country: PT.string,
info: PT.shape(),
- onShowBadges: PT.func,
- showBadgesButton: PT.bool,
- wins: PT.number,
};
export default ProfileHeader;
diff --git a/src/shared/components/ProfilePage/Header/styles.scss b/src/shared/components/ProfilePage/Header/styles.scss
index 31c3d7ef79..7537655f7e 100644
--- a/src/shared/components/ProfilePage/Header/styles.scss
+++ b/src/shared/components/ProfilePage/Header/styles.scss
@@ -1,188 +1,120 @@
@import "~styles/mixins";
-.badge-link {
- cursor: pointer;
- outline: none;
-}
-
-.track-icon {
- height: 40px;
- width: 40px;
-}
-
.container {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- p.description {
- word-wrap: break-word;
- margin-top: 30px;
- font-size: 15px;
- line-height: 24px;
+ .curve {
+ background: linear-gradient(263.22deg, #038664 2.65%, #075f96 98.14%);
+ background-repeat: no-repeat;
+ background-position: center center;
width: 100%;
-
- @include roboto-medium;
-
- text-align: center;
- color: $tc-gray-90;
- }
-
- .info {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- h1.handle {
- text-align: center;
- font-size: 28px;
- line-height: 34px;
-
- @include roboto-medium;
-
- &.level-1 {
- color: $tc-level-1;
- }
-
- &.level-2 {
- color: $tc-level-2;
- }
-
- &.level-3 {
- color: $tc-level-3;
- }
-
- &.level-4 {
- color: $tc-level-4;
- }
-
- &.level-5 {
- color: $tc-level-5;
- }
- }
-
- h3.tenure {
- margin-top: 5px;
- font-size: 12px;
- line-height: 14px;
-
- @include roboto-light;
-
- color: $tc-gray-40;
- text-align: center;
- text-transform: uppercase;
- }
-
- h3.location-challenges {
- margin-top: 20px;
- font-size: 12px;
- line-height: 14px;
-
- @include roboto-medium;
-
- text-align: center;
- text-transform: uppercase;
+ max-width: $screen-max;
+ margin: 0 auto;
+ height: 115px;
+ position: absolute;
+ top: 0;
+
+ @include xs-to-sm {
+ height: 173px;
}
- p.description {
- margin-top: 12px;
- max-width: 600px;
- text-align: left;
+ &::before {
+ content: "";
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 60px;
+ background: url(assets/images/profile/header-overlay.svg);
+ background-repeat: no-repeat;
+
+ @include xs-to-sm {
+ height: 36px;
+ width: 100%;
+ bottom: -10px;
+ background-repeat: round;
+ }
}
}
+}
- img.profile-circle {
- border-radius: 50%;
- display: inline;
- width: 140px;
- height: 140px;
- }
-
- h5 {
- color: $tc-gray-50;
+.header-container {
+ margin: 20px 32px 0 32px;
+ position: relative;
+ display: flex;
- &.tracks {
- text-transform: uppercase;
- }
+ .profile-image {
+ width: 120px;
+ height: 120px;
+ border: 3px solid $tc-white;
+ border-radius: 50%;
- &.location {
- margin-top: 10px;
+ @include xs-to-sm {
+ position: absolute;
+ top: 88px;
+ left: 32vw;
}
}
- .edit {
- width: 99px;
- margin-top: 30px;
- text-align: center;
-
- @include roboto-bold;
+ @include xs-to-sm {
+ width: 100%;
+ margin: 20px 0 0 0;
}
+}
- .tracks-links {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-top: 40px;
- width: 100%;
+.header-content {
+ margin-left: 28px;
+
+ .member-handle {
+ @include roboto-medium;
- .tracks {
- display: flex;
- flex-direction: row;
- justify-content: center;
+ font-weight: 500;
+ color: $tc-white;
+ font-size: 32px;
+ line-height: 32px;
- .track {
- cursor: pointer;
- display: flex;
- width: 100%;
- flex-wrap: wrap;
- flex-direction: column;
- align-items: center;
- margin: 0 10px;
-
- .track-icon {
- background-size: 40px;
- width: 40px;
- }
-
- .text {
- @include roboto-light;
-
- color: $tc-gray-40;
- font-size: 12px;
- line-height: 12px;
- margin-top: 11px;
- text-transform: uppercase;
- text-align: center;
- word-wrap: break-word;
- }
- }
+ @include xs-to-sm {
+ font-size: 20px;
+ line-height: 28px;
}
}
- .links {
- margin-top: 30px;
- width: 100%;
+ .verified-member {
+ margin-top: 10px;
display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- .link,
- .link:visited {
- @include roboto-regular;
+ span {
+ @include roboto-bold;
- color: $tc-gray-75;
+ font-weight: 700;
+ color: $tc-white;
font-size: 12px;
- line-height: 14px;
- text-decoration: none;
+ line-height: 16px;
text-transform: uppercase;
- margin-left: 10px;
- margin-right: 10px;
- transition: color 0.2s;
+ margin-top: 5px;
+ margin-left: 4px;
+ letter-spacing: 1px;
+
+ @include xs-to-sm {
+ font-size: 10px;
+ line-height: 12px;
+ }
+ }
- &:hover {
- color: $tc-dark-blue-110;
+ .info {
+ margin-top: 5px;
+ margin-left: 5px;
+ width: 13px;
+ height: 13px;
+ cursor: pointer;
+
+ @include xs-to-sm {
+ margin-top: 3px;
}
}
}
}
+
+.tooltip-content {
+ padding: 10px;
+ background-color: #2a2a2a;
+ border-radius: 4px;
+}
diff --git a/src/shared/components/ProfilePage/MemberInfo/index.jsx b/src/shared/components/ProfilePage/MemberInfo/index.jsx
new file mode 100644
index 0000000000..cf0a4bad47
--- /dev/null
+++ b/src/shared/components/ProfilePage/MemberInfo/index.jsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import PT from 'prop-types';
+import moment from 'moment';
+
+import './styles.scss';
+
+const MemberInfo = ({ country, info, wins }) => (
+
+
+ { country }
+
+
+
+ Member Since
+ {' '}
+ {moment(info.createdAt).format('MMM YYYY')}
+
+
+
+
COMPETITION ACTIVITY
+
+ {Boolean(wins) && (
+
+
+ {wins}
+ {' '}
+ WINS
+
+
+
+ )
+ }
+
+
+
+);
+
+MemberInfo.defaultProps = {
+ country: '',
+ info: {},
+ wins: 0,
+};
+
+MemberInfo.propTypes = {
+ country: PT.string,
+ info: PT.shape(),
+ wins: PT.number,
+};
+
+export default MemberInfo;
diff --git a/src/shared/components/ProfilePage/MemberInfo/styles.scss b/src/shared/components/ProfilePage/MemberInfo/styles.scss
new file mode 100644
index 0000000000..2de245ca76
--- /dev/null
+++ b/src/shared/components/ProfilePage/MemberInfo/styles.scss
@@ -0,0 +1,50 @@
+@import "~styles/mixins";
+
+.member-info {
+ .country {
+ span {
+ @include barlow-bold;
+
+ font-weight: 600;
+ font-size: 18px;
+ line-height: 22px;
+ color: $tco-black;
+ text-transform: uppercase;
+ }
+ }
+
+ .tenure {
+ @include roboto-regular;
+
+ font-weight: 400;
+ font-size: 16px;
+ line-height: 24px;
+ color: $tco-black;
+ margin-top: 8px;
+ }
+
+ .activity {
+ margin-top: 24px;
+
+ h3 {
+ @include barlow-bold;
+
+ font-weight: 600;
+ font-size: 18px;
+ line-height: 22px;
+ color: $tco-black;
+ text-transform: uppercase;
+ }
+ }
+
+ .wins {
+ @include barlow-condensed;
+
+ font-weight: 500;
+ color: $profile-member-wins;
+ font-size: 48px;
+ line-height: 50px;
+ margin-top: 8px;
+ text-transform: uppercase;
+ }
+}
diff --git a/src/shared/components/ProfilePage/MemberTracks/TrackItem/index.jsx b/src/shared/components/ProfilePage/MemberTracks/TrackItem/index.jsx
new file mode 100644
index 0000000000..f664d31249
--- /dev/null
+++ b/src/shared/components/ProfilePage/MemberTracks/TrackItem/index.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import PT from 'prop-types';
+import { v4 as uuidv4 } from 'uuid';
+
+import './styles.scss';
+
+const TrackItem = ({ trackName }) => (
+
+ {trackName}
+
+);
+
+
+TrackItem.propTypes = {
+ trackName: PT.string.isRequired,
+};
+
+export default TrackItem;
diff --git a/src/shared/components/ProfilePage/MemberTracks/TrackItem/styles.scss b/src/shared/components/ProfilePage/MemberTracks/TrackItem/styles.scss
new file mode 100644
index 0000000000..949ad63e24
--- /dev/null
+++ b/src/shared/components/ProfilePage/MemberTracks/TrackItem/styles.scss
@@ -0,0 +1,20 @@
+@import "~styles/mixins";
+
+.track-item {
+ padding: 4px 10px;
+ border: 2px solid $listing-checkbox-green;
+ border-radius: 4px;
+
+ @include xs-to-sm {
+ padding: 2px 10px;
+ }
+
+ span {
+ @include roboto-medium;
+
+ font-weight: 500;
+ font-size: 16px;
+ line-height: 24px;
+ text-align: center;
+ }
+}
diff --git a/src/shared/components/ProfilePage/MemberTracks/index.jsx b/src/shared/components/ProfilePage/MemberTracks/index.jsx
new file mode 100644
index 0000000000..9b9d6436f1
--- /dev/null
+++ b/src/shared/components/ProfilePage/MemberTracks/index.jsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import PT from 'prop-types';
+import { indexOf } from 'lodash';
+
+import './styles.scss';
+import TrackItem from './TrackItem';
+
+const MemberTracks = ({
+ copilot,
+ info,
+ hasMM,
+}) => {
+ const { tracks } = info;
+
+ const trackMap = {
+ DEVELOP: 'Developer',
+ DESIGN: 'Designer',
+ DATA_SCIENCE: 'Data Scientist',
+ };
+
+ return (
+
+ {
+ tracks && tracks.length > 0
+ && (
+
+ {
+ [...info.tracks, ...(indexOf(info.tracks, 'DATA_SCIENCE') === -1 && hasMM ? ['DATA_SCIENCE'] : []), ...(copilot ? ['COPILOT'] : [])].map(track => (
+
+ ))
+ }
+
+ )
+ }
+
+ );
+};
+
+
+MemberTracks.defaultProps = {
+ copilot: false,
+ hasMM: false,
+ info: {},
+};
+
+MemberTracks.propTypes = {
+ copilot: PT.bool,
+ hasMM: PT.bool,
+ info: PT.shape(),
+};
+
+export default MemberTracks;
diff --git a/src/shared/components/ProfilePage/MemberTracks/styles.scss b/src/shared/components/ProfilePage/MemberTracks/styles.scss
new file mode 100644
index 0000000000..25b67643b1
--- /dev/null
+++ b/src/shared/components/ProfilePage/MemberTracks/styles.scss
@@ -0,0 +1,11 @@
+@import "~styles/mixins";
+
+.member-tracks {
+ display: flex;
+ margin-top: 19px;
+ gap: 16px;
+
+ @include xs-to-sm {
+ gap: 8px;
+ }
+}
diff --git a/src/shared/components/ProfilePage/Skill/index.jsx b/src/shared/components/ProfilePage/Skill/index.jsx
deleted file mode 100644
index ad2e27adee..0000000000
--- a/src/shared/components/ProfilePage/Skill/index.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Skill Component. Displays an icon and label dynamically based on data.
- */
-import React from 'react';
-import PT from 'prop-types';
-import { truncate } from 'lodash';
-
-import FallbackIcon from 'assets/images/profile/skills/id-develop.svg';
-import VerifiedBadgeIcon from 'assets/images/verified-skill-badge.svg';
-import { isomorphy } from 'topcoder-react-utils';
-
-import './styles.scss';
-
-let assets;
-if (isomorphy.isClientSide()) {
- assets = require.context('assets/images/profile/skills', false, /svg/);
-}
-
-const Skill = ({
- tagId,
- tagName,
- isVerified,
-}) => (
-
-
- { assets && assets.keys().includes(`./id-${tagId}.svg`) ?
:
}
-
-
-
- {truncate(tagName, 20)}
-
- { isVerified &&
}
-
-
-);
-
-Skill.propTypes = {
- tagId: PT.string.isRequired,
- tagName: PT.string.isRequired,
- isVerified: PT.bool.isRequired,
-};
-
-export default Skill;
diff --git a/src/shared/components/ProfilePage/Skill/styles.scss b/src/shared/components/ProfilePage/Skill/styles.scss
deleted file mode 100644
index d41c275f87..0000000000
--- a/src/shared/components/ProfilePage/Skill/styles.scss
+++ /dev/null
@@ -1,70 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- display: flex;
- flex-direction: column;
- align-items: center;
- height: 106px;
- width: 80px;
-
- @media (min-width: 768px) {
- width: 100px;
- height: 128px;
- }
-
- a:hover {
- text-decoration: none;
- }
-
- .skill-icon {
- position: relative;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- background-color: $tc-gray-neutral-light;
- padding: 10px;
- border: 1px solid $tc-gray-10;
- height: 80px;
- width: 80px;
-
- @media (min-width: 768px) {
- height: 100px;
- width: 100px;
- }
-
- img {
- width: 48px;
- height: 48px;
-
- @media only screen and (min-width: 768px) {
- width: 60px;
- height: 60px;
- }
- }
- }
-
- .name-wrapper {
- margin-top: 8px;
- display: flex;
- align-items: center;
- justify-content: center;
-
- .name {
- @include roboto-light;
-
- font-size: 12px;
- color: $tc-gray-90;
- text-align: center;
-
- @media (min-width: 768px) {
- font-size: 14px;
- }
- }
-
- .verified-badge {
- padding-left: 5px;
- font-size: 0;
- }
- }
-}
diff --git a/src/shared/components/ProfilePage/Skills/List/index.jsx b/src/shared/components/ProfilePage/Skills/List/index.jsx
new file mode 100644
index 0000000000..29234cdbfc
--- /dev/null
+++ b/src/shared/components/ProfilePage/Skills/List/index.jsx
@@ -0,0 +1,75 @@
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React, { useEffect, useState } from 'react';
+import PT from 'prop-types';
+
+import VerifiedIconWhite from 'assets/images/profile/verified-icon-white.svg';
+
+import './styles.scss';
+
+const List = ({
+ skills,
+ isVerified,
+ isMobile,
+}) => {
+ const MAX_VISIBLE = isMobile ? 3 : 5;
+
+ const [isWrapped, setIsWrapped] = useState(false);
+ const [visibleSkills, setVisibleSkills] = useState([]);
+
+ useEffect(() => {
+ if (skills.length > MAX_VISIBLE) {
+ setIsWrapped(true);
+ setVisibleSkills(skills.slice(0, MAX_VISIBLE));
+ } else {
+ setIsWrapped(false);
+ setVisibleSkills(skills);
+ }
+ }, []);
+
+ const showHiddenSkills = () => {
+ setVisibleSkills(skills);
+ setIsWrapped(false);
+ };
+
+ return (
+
+ {
+ visibleSkills.map(({
+ tagId, tagName, hidden,
+ }) => (
+ !hidden
+ && (
+
+
+ {
+ isVerified &&
+ }
+
{tagName}
+
+
+ )
+ ))
+ }
+ {
+ isWrapped && (
+
+ +{skills.length - visibleSkills.length}
+
+ )
+ }
+
+ );
+};
+
+List.propTypes = {
+ skills: PT.arrayOf(PT.shape()).isRequired,
+ isVerified: PT.bool.isRequired,
+ isMobile: PT.bool.isRequired,
+};
+
+export default List;
diff --git a/src/shared/components/ProfilePage/Skills/List/styles.scss b/src/shared/components/ProfilePage/Skills/List/styles.scss
new file mode 100644
index 0000000000..e43df3f141
--- /dev/null
+++ b/src/shared/components/ProfilePage/Skills/List/styles.scss
@@ -0,0 +1,50 @@
+@import "~styles/mixins";
+
+.list {
+ display: flex;
+ gap: 4px;
+ margin-top: 8px;
+ flex-wrap: wrap;
+}
+
+.skill {
+ background-color: $profile-skill-badge;
+ width: fit-content;
+ padding: 4px 8px;
+ border-radius: 4px;
+ display: flex;
+
+ .verified-icon {
+ margin-right: 5px;
+ display: flex;
+ }
+
+ span {
+ @include roboto-medium;
+
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 16px;
+ letter-spacing: 0.5px;
+ color: $tc-white;
+ white-space: nowrap;
+ }
+}
+
+.add-button {
+ border-radius: 4px;
+ border: 2px solid $profile-skill-badge;
+ cursor: pointer;
+ width: fit-content;
+ padding: 1px 6px;
+
+ span {
+ @include roboto-medium;
+
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 16px;
+ letter-spacing: 0.5px;
+ color: $profile-skill-badge;
+ }
+}
diff --git a/src/shared/components/ProfilePage/Skills/index.jsx b/src/shared/components/ProfilePage/Skills/index.jsx
new file mode 100644
index 0000000000..2ed82e4f0a
--- /dev/null
+++ b/src/shared/components/ProfilePage/Skills/index.jsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import PT from 'prop-types';
+
+import './styles.scss';
+import _ from 'lodash';
+import VerifiedBadge from 'assets/images/profile/verified-icon.svg';
+import List from './List';
+
+const Skills = ({ skills, isMobile }) => {
+ const verifiedSkills = _.filter(skills, skill => _.includes(skill.sources, 'CHALLENGE'));
+ const userEnteredSkills = _.filter(skills, skill => !_.includes(skill.sources, 'CHALLENGE'));
+
+ return (
+
+
+ Skills
+
+
+
+
+
+
+
+
+
+ = Topcoder Verified
+
+
+
+ );
+};
+
+Skills.defaultProps = {
+ skills: [],
+};
+
+Skills.propTypes = {
+ skills: PT.arrayOf(PT.shape),
+ isMobile: PT.bool.isRequired,
+};
+
+export default Skills;
diff --git a/src/shared/components/ProfilePage/Skills/styles.scss b/src/shared/components/ProfilePage/Skills/styles.scss
new file mode 100644
index 0000000000..2182c04193
--- /dev/null
+++ b/src/shared/components/ProfilePage/Skills/styles.scss
@@ -0,0 +1,30 @@
+@import "~styles/mixins";
+
+.skills {
+ margin-top: 24px;
+
+ .title {
+ @include barlow-bold;
+
+ font-weight: 600;
+ font-size: 16px;
+ line-height: 18px;
+ color: $tco-black;
+ text-transform: uppercase;
+ }
+}
+
+.info {
+ @include roboto-regular;
+
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 22px;
+ color: $tco-black;
+ margin-top: 8px;
+ display: flex;
+
+ span {
+ margin-left: 4px;
+ }
+}
diff --git a/src/shared/components/ProfilePage/index.jsx b/src/shared/components/ProfilePage/index.jsx
index 56be6153de..6ec47bde38 100644
--- a/src/shared/components/ProfilePage/index.jsx
+++ b/src/shared/components/ProfilePage/index.jsx
@@ -6,22 +6,16 @@
import _ from 'lodash';
import React from 'react';
import PT from 'prop-types';
-import { PrimaryButton } from 'topcoder-react-ui-kit';
-import Sticky from 'react-stickynode';
import { isomorphy } from 'topcoder-react-utils';
-import Robot from 'assets/images/robot-happy.svg';
-
-import BadgesModal from './BadgesModal';
-import ExternalLink, { dataMap } from './ExternalLink';
+import { dataMap } from './ExternalLink';
import Header from './Header';
-import Skill from './Skill';
-
-import style from './styles.scss';
-import StatsCategory from './StatsCategory';
+import MemberTracks from './MemberTracks';
-// Number of skills to show before a 'VIEW MORE' button is created
-const MAX_SKILLS = 10;
+import './styles.scss';
+import Skills from './Skills';
+import MemberInfo from './MemberInfo';
+import Activity from './Activity';
/**
* Inspects a subtrack and determines if the member is active
@@ -36,7 +30,8 @@ const isActiveSubtrack = (subtrack) => {
}
if (subtrack.rank && subtrack.rank.rating > 0) {
return true;
- } if (_.isNumber(subtrack.submissions)) {
+ }
+ if (_.isNumber(subtrack.submissions)) {
return subtrack.submissions > 0;
}
return subtrack.submissions && subtrack.submissions.submissions > 0;
@@ -46,9 +41,7 @@ class ProfilePage extends React.Component {
constructor(props) {
super(props);
this.state = {
- badgesModalOpen: false,
isMobile: false,
- skillsExpanded: false,
};
this.handleResize = this.handleResize.bind(this);
@@ -79,10 +72,12 @@ class ProfilePage extends React.Component {
if (copilot && stats && stats.COPILOT && stats.COPILOT.fulfillment) {
activeTracks.push({
name: 'COPILOT',
- subTracks: [{
- fulfillment: stats.COPILOT.fulfillment,
- name: 'COPILOT',
- }],
+ subTracks: [
+ {
+ fulfillment: stats.COPILOT.fulfillment,
+ name: 'COPILOT',
+ },
+ ],
});
}
@@ -94,7 +89,10 @@ class ProfilePage extends React.Component {
subTracks.push({ ...stats[track].SRM, name: 'SRM' });
}
if (stats && stats[track] && stats[track].MARATHON_MATCH) {
- subTracks.push({ ...stats[track].MARATHON_MATCH, name: 'MARATHON MATCH' });
+ subTracks.push({
+ ...stats[track].MARATHON_MATCH,
+ name: 'MARATHON MATCH',
+ });
}
subTracks.forEach((subtrack) => {
@@ -103,10 +101,11 @@ class ProfilePage extends React.Component {
}
});
if (active.length > 0) {
- const sorted = _.orderBy(active, [
- s => s.wins,
- s => (s.rank ? s.rank.rating : 0),
- ], ['desc', 'desc']);
+ const sorted = _.orderBy(
+ active,
+ [s => s.wins, s => (s.rank ? s.rank.rating : 0)],
+ ['desc', 'desc'],
+ );
activeTracks.push({ name: track, subTracks: sorted });
}
});
@@ -120,7 +119,6 @@ class ProfilePage extends React.Component {
render() {
const {
- achievements,
copilot,
externalAccounts,
externalLinks,
@@ -130,11 +128,7 @@ class ProfilePage extends React.Component {
lookupData,
} = this.props;
- const {
- badgesModalOpen,
- isMobile,
- skillsExpanded,
- } = this.state;
+ const { isMobile } = this.state;
let { info } = this.props;
@@ -146,169 +140,67 @@ class ProfilePage extends React.Component {
let country = '';
if (_.has(lookupData, 'countries') && lookupData.countries.length > 0) {
const countryCode = _.isEmpty(_.get(info, 'homeCountryCode'))
- ? _.get(info, 'competitionCountryCode') : _.get(info, 'homeCountryCode');
+ ? _.get(info, 'competitionCountryCode')
+ : _.get(info, 'homeCountryCode');
- const result = _.find(lookupData.countries,
- c => countryCode && c.countryCode === countryCode.toUpperCase());
+ const result = _.find(
+ lookupData.countries,
+ c => countryCode && c.countryCode === countryCode.toUpperCase(),
+ );
country = _.isEmpty(result) ? '' : result.country;
}
// Convert skills from object to an array for easier iteration
- let skills = propSkills ? _.map(propSkills, (skill, tagId) => ({ tagId, ...skill })) : [];
- const showMoreButton = skills.length > MAX_SKILLS;
- if (!skillsExpanded) {
- skills = skills.slice(0, MAX_SKILLS);
- }
-
- let externals = externalAccounts ? _.map(_.pick(externalAccounts, _.map(dataMap, 'provider')), (data, type) => ({ type, data })) : [];
+ const skills = propSkills
+ ? _.map(propSkills, (skill, tagId) => ({ tagId, ...skill }))
+ : [];
+
+ let externals = externalAccounts
+ ? _.map(
+ _.pick(externalAccounts, _.map(dataMap, 'provider')),
+ (data, type) => ({ type, data }),
+ )
+ : [];
if (externalLinks) {
- externalLinks.map(data => externals.push(({ type: 'weblink', data })));
+ externalLinks.map(data => externals.push({ type: 'weblink', data }));
externals = _.filter(externals, 'data');
externals = _.sortBy(externals, 'type');
}
- const activeTracks = this.getActiveTracks();
// no rating MM
const hasMM = challenges && challenges.length;
return (
- {
- badgesModalOpen
- && (
-
this.setState({ badgesModalOpen: false })}
- />
- )
- }
-
-
-
-
-
- this.setState({ badgesModalOpen: true })}
- showBadgesButton={achievements && achievements.length > 0}
- wins={_.get((stats && stats[0]) || {}, 'wins', 0)}
- />
-
-
+
+
+
+
+
+
+
+ {!_.isEmpty(skills) && (
+
+ )}
+
+ {info.description && (
+
{info.description}
+ )}
-
- {
- _.isEmpty(skills) && _.isEmpty(activeTracks) && _.isEmpty(externals)
- && (
-
-
- BEEP. BEEP. HELLO!
-
-
-
- Seems like this member doesn’t have much information to share yet.
-
-
- )
- }
- {
- !_.isEmpty(skills)
- && (
-
-
-
- Skills
-
-
- {
- skills.map(({
- tagId, tagName, hidden, sources,
- }) => (
- !hidden
- && (
-
-
-
- )
- ))
- }
-
- {
- showMoreButton && !skillsExpanded
- && (
-
this.setState({ skillsExpanded: true })}
- theme={style}
- >
- VIEW ALL
-
- )
- }
- {
- skillsExpanded
- && (
-
this.setState({ skillsExpanded: false })}
- theme={style}
- >
- VIEW LESS
-
- )
- }
-
-
- )
- }
- {
- !_.isEmpty(stats) && (
-
-
-
- )
- }
- {
- !_.isEmpty(externals)
- && (
-
-
- On The Web
-
-
- {
- externals.map(external => (
-
- ))
- }
-
-
- )
- }
+
+
+
);
}
@@ -317,14 +209,12 @@ class ProfilePage extends React.Component {
ProfilePage.defaultProps = {
externalAccounts: null,
externalLinks: null,
- achievements: [],
challenges: null,
skills: null,
stats: null,
};
ProfilePage.propTypes = {
- achievements: PT.arrayOf(PT.shape()),
copilot: PT.bool.isRequired,
externalAccounts: PT.shape(),
challenges: PT.arrayOf(PT.shape()),
diff --git a/src/shared/components/ProfilePage/styles.scss b/src/shared/components/ProfilePage/styles.scss
index fabab8207d..869f3efe00 100644
--- a/src/shared/components/ProfilePage/styles.scss
+++ b/src/shared/components/ProfilePage/styles.scss
@@ -1,517 +1,56 @@
@import "~styles/mixins";
-@mixin module-s {
- margin-top: 6px;
- background-color: $tc-white;
-}
-
-@mixin module-l {
- @include module-s;
-
- max-width: 1242px;
- margin-left: 10px;
- margin-right: 10px;
- padding-top: 30px;
-
- @media only screen and (min-width: 900px) {
- padding-top: 30px;
- }
-}
-
-.empty-profile {
- @include roboto-light;
-
- align-items: center;
- background-color: $tc-white;
- color: $tc-gray-80;
- display: flex;
- flex-direction: column;
- font-size: 18px;
- height: 330px;
- justify-content: space-around;
- padding-top: 3 * $base-unit;
- text-align: center;
-
- h2 {
- font-size: 22px;
- font-weight: 500;
- }
-}
-
.outer-container {
- background: $tc-gray-neutral-light;
- width: 100%;
-
- @include xs-to-sm {
- padding: 8px 10px 20px;
- }
-
- @include md {
- padding: 30px 14px 30px 20px;
- }
-
- @include lg-to-xl {
- padding: 30px 64px;
- }
-}
-
-.button {
- font-size: 12px;
- height: 30px;
- margin-bottom: 30px;
- min-height: 30px;
- line-height: 28px;
- padding: 0 10px;
-}
-
-.profile-container {
- align-items: center;
- display: flex;
- justify-content: space-around;
- flex-direction: column;
width: 100%;
+ max-width: $screen-max;
+ margin: 0 auto;
}
-hr {
- width: 100%;
- border: 1px solid $tc-gray-20;
- margin-bottom: 15px;
-}
-
-// Main container
-// This is the flex container; we convert it to 2 column layout on >767px width
-.about-container {
+.content {
+ margin: 0 32px 32px 32px;
display: flex;
- flex-direction: column;
width: 100%;
- @include module-l;
-
- background-color: transparent;
- padding-top: 0;
-
- @media (min-width: 768px) {
- padding-top: 30px;
- flex-direction: row;
+ @include xs-to-md {
+ flex-direction: column;
+ margin: 180px 16px 16px 16px;
}
}
-.external-links-container {
- background-color: $tc-white;
- margin-top: 0;
- padding-top: 0;
- text-align: center;
-
- h3 {
- font-size: 18px;
- line-height: 21px;
- margin-bottom: 20px;
-
- @include roboto-medium;
+.left-content {
+ width: 73%;
+ padding-right: 45px;
- text-transform: uppercase;
- }
-}
+ .description {
+ @include roboto-regular;
-.external-links {
- display: flex;
- flex-flow: row wrap;
- justify-content: flex-start;
- margin-top: 1px;
- padding: 0 30px 30px 30px;
-}
+ font-weight: 400;
+ font-size: 16px;
+ line-height: 24px;
+ color: $tco-black;
+ margin-top: 24px;
-// Both containers are flex column, then flex fows on >767px
-// User data container
-.profile-header-container {
- background: transparent;
- width: 100%;
- max-width: 100%;
- margin-bottom: 5px;
- display: flex;
- position: relative;
-
- @media (min-width: 768px) {
- max-width: 368px;
- margin: 0 auto;
- padding-right: 10px;
- display: block;
- position: relative;
- }
-
- // Sticky container
- .sticky-container {
- background-color: $tc-white;
- display: flex;
- justify-content: center;
- width: calc(100vw - 40px);
-
- @media (min-width: 768px) {
- display: block;
- box-sizing: border-box;
- width: 368px;
- margin: 0;
+ @include xs-to-sm {
+ padding-bottom: 32px;
+ border-bottom: 2px solid $listing-gray;
}
-
- padding: 30px 34px;
- margin: 0 10px;
- transition: 0.2s all;
}
-}
-
-// Where we show the info
-.profile-about-container {
- display: flex;
- flex-direction: column;
- width: 100%;
- max-width: 883px;
- padding: 0 10px;
- .skills {
+ @include xs-to-md {
+ padding-right: 16px;
width: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- align-self: center;
- background: $tc-white;
- text-align: center;
- margin-left: auto;
- margin-right: auto;
-
- .list {
- display: flex;
- flex-direction: row;
- justify-content: center;
- flex-wrap: wrap;
- width: 100%;
- margin-left: auto;
- margin-bottom: 15px;
- margin-right: 15px;
-
- .skill {
- margin-top: 20px;
- margin-right: 15px;
-
- &:first-of-type {
- margin-left: 15px;
- }
-
- a:hover {
- cursor: default;
- }
-
- + .skill {
- margin-left: 15px;
- }
- }
- }
-
- .dimmed {
- width: 100%;
- margin-top: -70px;
- height: 100px;
- background: rgba(0, 0, 0, 0.6);
- opacity: 1;
- transition: all 0.5s;
- -webkit-transition: all 0.5s;
- }
-
- h1 {
- font-size: 25px;
- margin: auto;
- }
-
- button.more {
- margin-bottom: 20px;
- }
- }
-
- .activity {
- font-size: 18px;
- line-height: 21px;
-
- @include roboto-medium;
-
- text-transform: uppercase;
- margin-top: 20px;
- }
-
- .categories {
- @include module-l;
-
- margin-left: auto;
- margin-right: auto;
- padding-top: 15px;
- align-self: center;
- background: $tc-white;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- align-items: center;
-
- .track {
- margin-top: 22.4px;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding-bottom: 30px;
-
- &.noclick {
- cursor: default;
- }
-
- .name {
- margin: auto;
- font-size: 20px;
- line-height: 24px;
- margin-bottom: 10.6px;
-
- @include roboto-extra-light;
- }
-
- svg {
- height: 20px;
- width: 20px;
- margin-right: 7px;
- margin-bottom: -2px;
- }
-
- .subtrack {
- text-decoration: none;
- color: $tc-black;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- width: 300px;
- height: 52px;
- border-bottom: 1px solid $tc-gray-neutral-dark;
-
- &.first {
- border-top: 1px solid $tc-gray-neutral-dark;
- }
-
- &:hover {
- background-color: $tc-gray-neutral-dark;
- }
-
- .name {
- margin-left: 15px;
- align-self: center;
- flex-basis: 250px;
- text-align: left;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- font-size: 14px;
- line-height: 18px;
- text-transform: uppercase;
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
- margin-top: 12px;
-
- @include roboto-light;
- }
-
- .ranking {
- margin: auto;
- margin-right: 15px;
- color: $tc-gray-80;
- display: flex;
- flex-direction: column;
- align-items: flex-end;
-
- @include roboto-medium;
-
- .number {
- text-align: center;
- font-size: 13.4px;
- line-height: 16px;
-
- span.square {
- position: absolute;
- margin-left: 5px;
- width: 6px;
- height: 6px;
- }
- }
-
- .tag {
- text-transform: uppercase;
- margin: 5px auto 0 auto;
-
- @include roboto-regular;
-
- font-size: 12px;
- line-height: 10px;
- color: $tc-gray-60;
- }
- }
-
- .arrow {
- @include roboto-extra-light;
-
- color: $tc-gray-80;
- height: 12px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
- }
-
- .icon {
- width: 80px;
- height: 80px;
- margin: auto;
- }
- }
}
}
-@media (min-width: 768px) {
- .external-links-container {
- h3 {
- font-size: 24px;
- line-height: 30px;
- }
- }
-
- .profile-about-container {
- .activity {
- font-size: 24px;
- line-height: 30px;
- margin-top: 30px;
- }
-
- .skills {
- display: flex;
- flex-direction: column;
- padding-top: 0;
- padding-bottom: 0;
-
- .list {
- display: flex;
- flex-direction: row;
- width: 100%;
- padding-left: 30px;
- padding-right: 30px;
- margin: 0 auto 30px auto;
-
- .skill {
- margin-top: 30px;
- margin-right: 15px;
- margin-left: 15px;
- }
-
- + .skill {
- margin-left: 15px;
- }
-
- &:nth-child(3n + 1) {
- margin-left: 15px !important;
- border: 1px solid lime;
- }
-
- .leftButton,
- .rightButton {
- height: 100px;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- &:hover {
- cursor: pointer;
- }
- }
- }
-
- button.more {
- margin-bottom: 30px;
- }
- }
-
- .categories {
- margin-top: 1px;
- padding-top: 0;
+.right-content {
+ border-left: 2px solid #e9e9e9;
+ padding-left: 50px;
+ margin-top: -27px;
- .activity {
- font-size: 28px;
- line-height: 34px;
-
- @include roboto-bold;
-
- text-transform: uppercase;
- }
-
- .track {
- width: 100%;
- padding-top: 30px;
-
- .name {
- svg {
- height: 30px;
- width: 30px;
- margin-right: 7px;
- margin-bottom: -4px;
- }
-
- background-size: 30px;
- background-position: 16px 4px;
- font-weight: 200;
- font-size: 24px;
- line-height: 30px;
- margin-bottom: 30px;
- margin-top: 0;
-
- @include roboto-medium;
- }
-
- .subtrack {
- align-items: center;
- width: 75%;
- height: 60px;
-
- .name {
- font-size: 18px;
- flex-basis: 350px;
- line-height: 24px;
- margin-top: 17px;
- margin-bottom: 20px;
- }
-
- .ranking {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
-
- .number {
- font-size: 18px;
- line-height: 23px;
- }
-
- .tag {
- margin-top: 2px;
- font-size: 12px;
- line-height: 14px;
- color: $tc-gray-60;
- }
- }
-
- .arrow {
- color: $tc-gray-30;
- height: 15px;
- margin-right: 10px;
- }
- }
-
- .icon {
- width: 80px;
- height: 80px;
- margin: auto;
- }
- }
- }
+ @include xs-to-sm {
+ border-left: 0;
+ margin-top: 0;
+ padding-left: 0;
+ padding-top: 32px;
}
}
diff --git a/src/styles/_mixins/_variables.scss b/src/styles/_mixins/_variables.scss
index 646c5a514b..1d0be4396c 100644
--- a/src/styles/_mixins/_variables.scss
+++ b/src/styles/_mixins/_variables.scss
@@ -4,6 +4,7 @@ $tco-black: #2a2a2a;
/* @media */
$screem-md: 1376px;
$screen-sm: 768px;
+$screen-max: 1376px;
/* member rating colors */
$member-gray: #555;
@@ -39,3 +40,8 @@ $listing-checkbox-green: #137d60;
$listing-checkbox-blue: #2c95d7;
$listing-white: #fff;
$listing-avatar-white: #f0f0f0;
+
+/* profile page colors */
+$profile-skill-badge: #227681;
+$profile-member-wins: #1e94a3;
+$profile-border-gray: #e9e9e9;