diff --git a/.circleci/config.yml b/.circleci/config.yml
index a6edc20235..0eb81da181 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -349,14 +349,14 @@ workflows:
filters:
branches:
only:
- - sprig-lib
+ - free
# This is alternate dev env for parallel testing
- "build-test":
context : org-global
filters:
branches:
only:
- - PROD-3199
+ - TCA-768_member-profile-tca-certs
# This is alternate dev env for parallel testing
- "build-qa":
context : org-global
diff --git a/__tests__/shared/components/ProfilePage/__snapshots__/index.jsx.snap b/__tests__/shared/components/ProfilePage/__snapshots__/index.jsx.snap
index b9e6eccc2c..311b33847a 100644
--- a/__tests__/shared/components/ProfilePage/__snapshots__/index.jsx.snap
+++ b/__tests__/shared/components/ProfilePage/__snapshots__/index.jsx.snap
@@ -715,6 +715,7 @@ exports[`renders a full Profile correctly 1`] = `
}
}
tcAcademyCertifications={Array []}
+ tcAcademyCourses={Array []}
/>
`;
@@ -866,5 +867,6 @@ exports[`renders an empty Profile correctly 1`] = `
}
}
tcAcademyCertifications={Array []}
+ tcAcademyCourses={Array []}
/>
`;
diff --git a/src/assets/images/profile/tca-certificates/tca-certifications-badges-sprite.png b/src/assets/images/profile/tca-certificates/tca-certifications-badges-sprite.png
new file mode 100644
index 0000000000..b5711bf0d0
Binary files /dev/null and b/src/assets/images/profile/tca-certificates/tca-certifications-badges-sprite.png differ
diff --git a/src/shared/components/ProfilePage/ProfileModal/index.jsx b/src/shared/components/ProfilePage/ProfileModal/index.jsx
index 41a4f1bbfa..67cbabd98c 100644
--- a/src/shared/components/ProfilePage/ProfileModal/index.jsx
+++ b/src/shared/components/ProfilePage/ProfileModal/index.jsx
@@ -25,7 +25,7 @@ const ProfileModal = ({
{title}
-
diff --git a/src/shared/components/ProfilePage/ProfileModal/styles.scss b/src/shared/components/ProfilePage/ProfileModal/styles.scss
index a93949450d..03f12bb6be 100644
--- a/src/shared/components/ProfilePage/ProfileModal/styles.scss
+++ b/src/shared/components/ProfilePage/ProfileModal/styles.scss
@@ -30,6 +30,8 @@
justify-content: space-between;
align-items: center;
padding-bottom: 24px;
+ z-index: 1;
+ position: relative;
.title {
@include barlow-medium;
diff --git a/src/shared/components/ProfilePage/TcaCertificates/CertificationBadge/index.jsx b/src/shared/components/ProfilePage/TcaCertificates/CertificationBadge/index.jsx
new file mode 100644
index 0000000000..5e76f1f6a9
--- /dev/null
+++ b/src/shared/components/ProfilePage/TcaCertificates/CertificationBadge/index.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import PT from 'prop-types';
+
+import 'assets/images/profile/tca-certificates/tca-certifications-badges-sprite.png';
+import './styles.scss';
+
+const CertificationBadge = ({ type: badgeType, level, size }) => (
+
+);
+
+CertificationBadge.defaultProps = {
+ size: 'md',
+};
+
+CertificationBadge.propTypes = {
+ size: PT.oneOf(['md']),
+ level: PT.oneOf(['Beginner', 'Intermediate', 'Expert', 'All Levels']).isRequired,
+ type: PT.oneOf(['DATASCIENCE', 'DESIGN', 'DEV', 'DATABASE', 'INTERVIEW', 'QA', 'SECURITY']).isRequired,
+};
+
+
+export default CertificationBadge;
diff --git a/src/shared/components/ProfilePage/TcaCertificates/CertificationBadge/styles.scss b/src/shared/components/ProfilePage/TcaCertificates/CertificationBadge/styles.scss
new file mode 100644
index 0000000000..e3710be830
--- /dev/null
+++ b/src/shared/components/ProfilePage/TcaCertificates/CertificationBadge/styles.scss
@@ -0,0 +1,95 @@
+.tca-badge-wrap {
+ background-repeat: no-repeat;
+ background-position: 0 0;
+ background-size: 300px 283.6363px;
+ background-image: url('../../../../../assets/images/profile/tca-certificates/tca-certifications-badges-sprite.png');
+
+ &.size-md {
+ width: 48px;
+ height: 48px;
+ }
+
+ &:global(.badge-interview--beginner) {
+ background-position: -6px -6px;
+ }
+
+ &:global(.badge-interview--intermediate) {
+ background-position: -66px -6px;
+ }
+
+ &:global(.badge-interview--expert) {
+ background-position: -6px -63px;
+ }
+
+ &:global(.badge-datascience--beginner) {
+ background-position: -126px -6px;
+ }
+
+ &:global(.badge-datascience--intermediate) {
+ background-position: -66px -63px;
+ }
+
+ &:global(.badge-datascience--expert) {
+ background-position: -6px -119px;
+ }
+
+ &:global(.badge-database--beginner) {
+ background-position: -126px -63px;
+ }
+
+ &:global(.badge-database--intermediate) {
+ background-position: -126px -119px;
+ }
+
+ &:global(.badge-database--expert) {
+ background-position: -66px -119px;
+ }
+
+ &:global(.badge-design--beginner) {
+ background-position: -186px -6px;
+ }
+
+ &:global(.badge-design--intermediate) {
+ background-position: -186px -63px;
+ }
+
+ &:global(.badge-design--expert) {
+ background-position: -186px -119px;
+ }
+
+ &:global(.badge-dev--beginner) {
+ background-position: -6px -176px;
+ }
+
+ &:global(.badge-dev--intermediate) {
+ background-position: -66px -176px;
+ }
+
+ &:global(.badge-dev--expert) {
+ background-position: -126px -176px;
+ }
+
+ &:global(.badge-qa--beginner) {
+ background-position: -186px -176px;
+ }
+
+ &:global(.badge-qa--intermediate) {
+ background-position: -246px -6px;
+ }
+
+ &:global(.badge-qa--expert) {
+ background-position: -246px -63px;
+ }
+
+ &:global(.badge-security--beginner) {
+ background-position: -246px -119px;
+ }
+
+ &:global(.badge-security--intermediate) {
+ background-position: -246px -176px;
+ }
+
+ &:global(.badge-security--expert) {
+ background-position: -6px -233px;
+ }
+}
diff --git a/src/shared/components/ProfilePage/TcaCertificates/List/index.jsx b/src/shared/components/ProfilePage/TcaCertificates/List/index.jsx
index ae77b2203d..98d0efc0c1 100644
--- a/src/shared/components/ProfilePage/TcaCertificates/List/index.jsx
+++ b/src/shared/components/ProfilePage/TcaCertificates/List/index.jsx
@@ -2,13 +2,13 @@ import React from 'react';
import PT from 'prop-types';
import './styles.scss';
-import CourseBadge from '../CourseBadge';
const preventDefault = ev => ev.stopPropagation();
const List = ({
certificates,
onClick,
+ renderIcon,
}) => (
{certificates.map(certificate => (
@@ -16,22 +16,24 @@ const List = ({
styleName="list-item"
key={certificate.id}
onClick={() => onClick(certificate)}
- onKeyPress={() => onClick(certificate)}
+ onKeyDown={() => onClick(certificate)}
role="button"
tabIndex={-1}
>
-
+ {renderIcon(certificate)}
- {certificate.certificationTitle}
-
-
+ {certificate.resourceProvider && (
+
+ )}
))}
@@ -41,6 +43,7 @@ const List = ({
List.propTypes = {
certificates: PT.arrayOf(PT.shape()).isRequired,
onClick: PT.func.isRequired,
+ renderIcon: PT.element.isRequired,
};
export default List;
diff --git a/src/shared/components/ProfilePage/TcaCertificates/TcaCertificationCertificateModal/index.jsx b/src/shared/components/ProfilePage/TcaCertificates/TcaCertificationCertificateModal/index.jsx
new file mode 100644
index 0000000000..1849fac13b
--- /dev/null
+++ b/src/shared/components/ProfilePage/TcaCertificates/TcaCertificationCertificateModal/index.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import PT from 'prop-types';
+import { noop } from 'lodash/noop';
+import { config } from 'topcoder-react-utils';
+
+import ProfileModal from '../../ProfileModal';
+import styles from './styles.scss';
+
+const tcAcademyPath = `${config.PLATFORMUI_SITE_URL}${config.TC_ACADEMY_BASE_PATH}`;
+
+const TcaCertificationCertificateModal = ({
+ certificate,
+ onCancel,
+}) => (
+
+
+
+);
+
+TcaCertificationCertificateModal.defaultProps = {
+ onCancel: noop,
+};
+
+TcaCertificationCertificateModal.propTypes = {
+ certificate: PT.shape().isRequired,
+ onCancel: PT.func,
+};
+
+export default TcaCertificationCertificateModal;
diff --git a/src/shared/components/ProfilePage/TcaCertificates/TcaCertificationCertificateModal/styles.scss b/src/shared/components/ProfilePage/TcaCertificates/TcaCertificationCertificateModal/styles.scss
new file mode 100644
index 0000000000..59c60280d6
--- /dev/null
+++ b/src/shared/components/ProfilePage/TcaCertificates/TcaCertificationCertificateModal/styles.scss
@@ -0,0 +1,39 @@
+@import "~styles/mixins";
+
+.tca-modal {
+ display: flex;
+ flex-direction: column;
+ min-height: auto;
+ padding: 36px;
+ width: 980px;
+ max-width: 96vw;
+ background: transparent;
+
+ :global(.close-icon) {
+ color: #fff;
+
+ svg > path {
+ fill: currentColor;
+ }
+ }
+
+ @include xs-to-sm {
+ display: flex;
+ flex-direction: column;
+ max-width: none;
+ width: 100%;
+ }
+}
+
+.iframe {
+ display: block;
+ height: 788px;
+ max-height: 100%;
+ width: calc(100% + 37px + 37px);
+ margin: -102px -37px -36px;
+
+ @include xs-to-sm {
+ height: auto;
+ flex: 1;
+ }
+}
diff --git a/src/shared/components/ProfilePage/TcaCertificates/TcaCertificateModal/index.jsx b/src/shared/components/ProfilePage/TcaCertificates/TcaCourseCertificateModal/index.jsx
similarity index 84%
rename from src/shared/components/ProfilePage/TcaCertificates/TcaCertificateModal/index.jsx
rename to src/shared/components/ProfilePage/TcaCertificates/TcaCourseCertificateModal/index.jsx
index 4d9cfb8dd1..647f17c572 100644
--- a/src/shared/components/ProfilePage/TcaCertificates/TcaCertificateModal/index.jsx
+++ b/src/shared/components/ProfilePage/TcaCertificates/TcaCourseCertificateModal/index.jsx
@@ -8,7 +8,7 @@ import styles from './styles.scss';
const tcAcademyPath = `${config.PLATFORMUI_SITE_URL}${config.TC_ACADEMY_BASE_PATH}`;
-const TcaCertificateModal = ({
+const TcaCourseCertificateModal = ({
certificate,
onCancel,
memberHandle,
@@ -32,14 +32,14 @@ const TcaCertificateModal = ({
);
-TcaCertificateModal.defaultProps = {
+TcaCourseCertificateModal.defaultProps = {
onCancel: noop,
};
-TcaCertificateModal.propTypes = {
+TcaCourseCertificateModal.propTypes = {
certificate: PT.shape().isRequired,
onCancel: PT.func,
memberHandle: PT.string.isRequired,
};
-export default TcaCertificateModal;
+export default TcaCourseCertificateModal;
diff --git a/src/shared/components/ProfilePage/TcaCertificates/TcaCertificateModal/styles.scss b/src/shared/components/ProfilePage/TcaCertificates/TcaCourseCertificateModal/styles.scss
similarity index 95%
rename from src/shared/components/ProfilePage/TcaCertificates/TcaCertificateModal/styles.scss
rename to src/shared/components/ProfilePage/TcaCertificates/TcaCourseCertificateModal/styles.scss
index f553f424a4..9e8286961b 100644
--- a/src/shared/components/ProfilePage/TcaCertificates/TcaCertificateModal/styles.scss
+++ b/src/shared/components/ProfilePage/TcaCertificates/TcaCourseCertificateModal/styles.scss
@@ -18,7 +18,7 @@
.iframe {
display: block;
- height: 600px;
+ height: 760px;
width: 100%;
@include xs-to-sm {
diff --git a/src/shared/components/ProfilePage/TcaCertificates/index.jsx b/src/shared/components/ProfilePage/TcaCertificates/index.jsx
index 4f357177aa..08dddb9899 100644
--- a/src/shared/components/ProfilePage/TcaCertificates/index.jsx
+++ b/src/shared/components/ProfilePage/TcaCertificates/index.jsx
@@ -1,13 +1,18 @@
-import React, { useState } from 'react';
+import React, { Fragment, useState } from 'react';
import PT from 'prop-types';
import './styles.scss';
import TcaLogo from 'assets/images/profile/tca-certificates/tca-logo.svg';
import List from './List';
-import TcaCertificateModal from './TcaCertificateModal';
+import TcaCertificationCertificateModal from './TcaCertificationCertificateModal';
+import TcaCourseCertificateModal from './TcaCourseCertificateModal';
+import CertificationBadge from './CertificationBadge';
+import CourseBadge from './CourseBadge';
+
+const TcaCertificates = ({ certifications, courses, memberHandle }) => {
+ const [showCertificationCertificate, setShowCertificationCertificate] = useState(false);
+ const [showCourseCertificate, setShowCourseCertificate] = useState(false);
-const TcaCertificates = ({ certificates, memberHandle }) => {
- const [showCertificate, setShowCertificate] = useState(false);
return (
@@ -15,16 +20,44 @@ const TcaCertificates = ({ certificates, memberHandle }) => {
Topcoder Academy
-
setShowCertificate(certificate)}
- />
+ {certifications.length > 0 && (
+
+ Certifications
+ setShowCertificationCertificate(certificate)}
+ renderIcon={enrollment => (
+
+ )}
+ />
+
+ )}
+ {courses.length > 0 && (
+
+ Courses
+ setShowCourseCertificate(certificate)}
+ renderIcon={certificate => }
+ />
+
+ )}
- { showCertificate && (
- setShowCertificate(false)}
- certificate={showCertificate}
+ { showCertificationCertificate && (
+ setShowCertificationCertificate(false)}
+ certificate={showCertificationCertificate}
+ />
+ )}
+ { showCourseCertificate && (
+ setShowCourseCertificate(false)}
+ certificate={showCourseCertificate}
memberHandle={memberHandle}
/>
)}
@@ -33,11 +66,13 @@ const TcaCertificates = ({ certificates, memberHandle }) => {
};
TcaCertificates.defaultProps = {
- certificates: [],
+ certifications: [],
+ courses: [],
};
TcaCertificates.propTypes = {
- certificates: PT.arrayOf(PT.shape),
+ certifications: PT.arrayOf(PT.shape),
+ courses: PT.arrayOf(PT.shape),
memberHandle: PT.string.isRequired,
};
diff --git a/src/shared/components/ProfilePage/TcaCertificates/styles.scss b/src/shared/components/ProfilePage/TcaCertificates/styles.scss
index 25f502260d..2a4a85a7a6 100644
--- a/src/shared/components/ProfilePage/TcaCertificates/styles.scss
+++ b/src/shared/components/ProfilePage/TcaCertificates/styles.scss
@@ -1,7 +1,8 @@
@import "~styles/mixins";
.tca-certificates {
- margin-top: 32px;
+ margin-top: 68px;
+ margin-bottom: -36px;
background: $listing-filter-bg;
padding: 32px;
border-radius: 8px;
@@ -37,4 +38,19 @@
}
}
}
+
+ .category-title {
+ @include barlow-semi-bold;
+
+ text-transform: uppercase;
+ font-size: 18px;
+ line-height: 22px;
+ margin-top: 24px;
+
+ @include xs-to-md {
+ font-size: 16px;
+ line-height: 18px;
+ margin-top: 16px;
+ }
+ }
}
diff --git a/src/shared/components/ProfilePage/index.jsx b/src/shared/components/ProfilePage/index.jsx
index 60953d0d5d..02c3a080f4 100644
--- a/src/shared/components/ProfilePage/index.jsx
+++ b/src/shared/components/ProfilePage/index.jsx
@@ -151,6 +151,7 @@ class ProfilePage extends React.Component {
handleParam,
meta,
tcAcademyCertifications,
+ tcAcademyCourses,
} = this.props;
const {
@@ -232,9 +233,10 @@ class ProfilePage extends React.Component {
) : null
}
- {tcAcademyCertifications.length > 0 && (
+ {(tcAcademyCertifications.length + tcAcademyCourses.length) > 0 && (
)}
@@ -300,6 +302,7 @@ ProfilePage.defaultProps = {
stats: null,
badges: {},
tcAcademyCertifications: [],
+ tcAcademyCourses: [],
};
ProfilePage.propTypes = {
@@ -316,6 +319,7 @@ ProfilePage.propTypes = {
meta: PT.shape().isRequired,
clearSubtrackChallenges: PT.func.isRequired,
tcAcademyCertifications: PT.arrayOf(PT.shape()),
+ tcAcademyCourses: PT.arrayOf(PT.shape()),
};
function mapDispatchToProps(dispatch) {
diff --git a/src/shared/containers/Profile.jsx b/src/shared/containers/Profile.jsx
index 68755b7936..af011788f8 100644
--- a/src/shared/containers/Profile.jsx
+++ b/src/shared/containers/Profile.jsx
@@ -209,7 +209,10 @@ const mapStateToProps = (state, ownProps) => ({
lookupData: state.lookup,
badges: state.page.profile[ownProps.match.params.handle]
? state.page.profile[ownProps.match.params.handle].badges : {},
- tcAcademyCertifications: state.tcAcademy.certifications,
+ tcAcademyCertifications: _.get(state.tcAcademy, 'certifications.enrollments', []),
+ tcAcademyCourses: Array.isArray(state.tcAcademy.certifications)
+ ? state.tcAcademy.certifications
+ : _.get(state.tcAcademy, 'certifications.courses', []),
auth: {
...state.auth,
},