Skip to content

Commit ae38db0

Browse files
Merge pull request #6597 from topcoder-platform/tca-certifications-on-profile-page
Tca certifications on profile page
2 parents 25167d7 + 65075fe commit ae38db0

File tree

24 files changed

+475
-82
lines changed

24 files changed

+475
-82
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ workflows:
349349
filters:
350350
branches:
351351
only:
352-
- free
352+
- tca-certifications-on-profile-page
353353
# This is alternate dev env for parallel testing
354354
- "build-test":
355355
context : org-global

__tests__/shared/components/ProfilePage/__snapshots__/index.jsx.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ exports[`renders a full Profile correctly 1`] = `
713713
"unsubscribe": null,
714714
}
715715
}
716+
tcAcademyCertifications={Array []}
716717
/>
717718
`;
718719

@@ -862,5 +863,6 @@ exports[`renders an empty Profile correctly 1`] = `
862863
"unsubscribe": null,
863864
}
864865
}
866+
tcAcademyCertifications={Array []}
865867
/>
866868
`;

config/default.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ module.exports = {
444444
GIGS_PAGES_PATH: '/gigs',
445445
GIGS_LISTING_CACHE_TIME: 300, // in seconds
446446
START_PAGE_PATH: '/start',
447+
TC_ACADEMY_BASE_PATH: '/learn',
447448
GUIKIT: {
448449
DEBOUNCE_ON_CHANGE_TIME: 150,
449450
},
@@ -452,4 +453,5 @@ module.exports = {
452453
SDK_KEY: '7V4CJhurXT3Y3bnzv1hv1',
453454
},
454455
PLATFORM_SITE_URL: 'https://platform.topcoder-dev.com',
456+
PLATFORMUI_SITE_URL: 'https://platform-ui.topcoder-dev.com',
455457
};

config/development.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ module.exports = {
55
USER_SETTINGS: '', /* No dev server is available for saved searches */
66
},
77
PLATFORM_SITE_URL: 'https://platform.topcoder-dev.com',
8+
PLATFORMUI_SITE_URL: 'https://platform-ui.topcoder-dev.com',
89
};

config/production.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,5 @@ module.exports = {
226226
TC_EDU_SEARCH_BAR_MAX_RESULTS_EACH_GROUP: 3,
227227
ENABLE_RECOMMENDER: true,
228228
PLATFORM_SITE_URL: 'https://platform.topcoder.com',
229+
PLATFORMUI_SITE_URL: 'https://platform-ui.topcoder.com',
229230
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@
153153
"supertest": "^3.1.0",
154154
"tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6.3",
155155
"tc-ui": "^1.0.12",
156-
"topcoder-react-lib": "1.2.7",
156+
"topcoder-react-lib": "1000.29.7",
157157
"topcoder-react-ui-kit": "2.0.1",
158158
"topcoder-react-utils": "0.7.8",
159159
"turndown": "^4.0.2",
Loading
Loading
Loading
Loading
Lines changed: 10 additions & 0 deletions
Loading
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import PT from 'prop-types';
3+
import { noop } from 'lodash/noop';
4+
import { Modal } from 'topcoder-react-ui-kit';
5+
import cn from 'classnames';
6+
7+
import IconClose from 'assets/images/icon-close-green.svg';
8+
import styles from './styles.scss';
9+
10+
const ProfileModal = ({
11+
children,
12+
title,
13+
onCancel,
14+
containerClassName,
15+
}) => (
16+
<Modal
17+
theme={{
18+
container: cn(styles['modal-container'], containerClassName),
19+
overlay: styles['modal-overlay'],
20+
}}
21+
onCancel={onCancel}
22+
>
23+
<React.Fragment>
24+
<div styleName="header">
25+
<h2 styleName="title">
26+
{title}
27+
</h2>
28+
<div styleName="icon" role="presentation" onClick={onCancel}>
29+
<IconClose />
30+
</div>
31+
</div>
32+
{children}
33+
</React.Fragment>
34+
</Modal>
35+
);
36+
37+
ProfileModal.defaultProps = {
38+
title: null,
39+
onCancel: noop,
40+
containerClassName: '',
41+
};
42+
43+
ProfileModal.propTypes = {
44+
children: PT.node.isRequired,
45+
title: PT.node,
46+
onCancel: PT.func,
47+
containerClassName: PT.string,
48+
};
49+
50+
export default ProfileModal;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@import "~styles/mixins";
2+
3+
.modal-overlay {
4+
background: #000;
5+
}
6+
7+
.modal-container-copilot,
8+
.modal-container {
9+
width: 1232px;
10+
min-height: 700px;
11+
max-width: 1232px;
12+
border-radius: 8px;
13+
padding: 32px 32px 0 32px;
14+
gap: 24px;
15+
16+
&.modal-container-copilot {
17+
height: auto;
18+
}
19+
20+
@include xs-to-sm {
21+
width: 100%;
22+
max-width: 100%;
23+
height: 100% !important;
24+
max-height: 100% !important;
25+
padding: 24px 16px 48px 16px;
26+
}
27+
28+
.header {
29+
display: flex;
30+
justify-content: space-between;
31+
align-items: center;
32+
padding-bottom: 24px;
33+
34+
.title {
35+
@include barlow-medium;
36+
37+
font-weight: 600;
38+
font-size: 22px;
39+
line-height: 26px;
40+
text-transform: uppercase;
41+
}
42+
43+
.icon {
44+
cursor: pointer;
45+
}
46+
}
47+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import PT from 'prop-types';
3+
4+
import DataScienceBadgeImg from 'assets/images/profile/tca-certificates/datascience-badge.png';
5+
import DesignBadgeImg from 'assets/images/profile/tca-certificates/design-badge.png';
6+
import DevelopBadgeImg from 'assets/images/profile/tca-certificates/develop-badge.png';
7+
import QaBadgeImg from 'assets/images/profile/tca-certificates/qa-badge.png';
8+
9+
import './styles.scss';
10+
11+
const badgesMap = {
12+
DATASCIENCE: DataScienceBadgeImg,
13+
DESIGN: DesignBadgeImg,
14+
DEV: DevelopBadgeImg,
15+
QA: QaBadgeImg,
16+
};
17+
18+
const CourseBadge = ({ type: badgeType, size }) => {
19+
const badgeImg = badgesMap[badgeType];
20+
21+
return (
22+
<div styleName={`tca-badge-wrap size-${size}`}>
23+
<img src={badgeImg} alt={badgeType} />
24+
</div>
25+
);
26+
};
27+
28+
CourseBadge.defaultProps = {
29+
size: 'md',
30+
};
31+
32+
CourseBadge.propTypes = {
33+
size: PT.oneOf(['md']),
34+
type: PT.oneOf(['DATASCIENCE', 'DESIGN', 'DEV', 'QA']).isRequired,
35+
};
36+
37+
38+
export default CourseBadge;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.tca-badge-wrap {
2+
&.size-md {
3+
width: 48px;
4+
height: 48px;
5+
}
6+
7+
img {
8+
display: block;
9+
width: 100%;
10+
height: 100%;
11+
object-fit: contain;
12+
}
13+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import PT from 'prop-types';
3+
4+
import './styles.scss';
5+
import CourseBadge from '../CourseBadge';
6+
7+
const preventDefault = ev => ev.stopPropagation();
8+
9+
const List = ({
10+
certificates,
11+
onClick,
12+
}) => (
13+
<div styleName="list">
14+
{certificates.map(certificate => (
15+
<div
16+
styleName="list-item"
17+
key={certificate.id}
18+
onClick={() => onClick(certificate)}
19+
onKeyPress={() => onClick(certificate)}
20+
role="button"
21+
tabIndex={-1}
22+
>
23+
<div styleName="list-item_badge">
24+
<CourseBadge type={certificate.certificationTrackType || 'DEV'} />
25+
</div>
26+
<div>
27+
<div styleName="list-item_title">
28+
{certificate.certificationTitle}
29+
</div>
30+
<div styleName="list-item_sub">
31+
<a href={`//${certificate.providerUrl}`} target="blank" rel="noopener" onClick={preventDefault}>
32+
by {certificate.provider}
33+
</a>
34+
</div>
35+
</div>
36+
</div>
37+
))}
38+
</div>
39+
);
40+
41+
List.propTypes = {
42+
certificates: PT.arrayOf(PT.shape()).isRequired,
43+
onClick: PT.func.isRequired,
44+
};
45+
46+
export default List;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
@import "~styles/mixins";
2+
3+
.list {
4+
display: flex;
5+
gap: 16px;
6+
margin-top: 24px;
7+
flex-wrap: wrap;
8+
9+
@include xs-to-sm {
10+
flex-direction: column;
11+
flex-wrap: nowrap;
12+
margin-top: 16px;
13+
}
14+
}
15+
16+
.list-item {
17+
background: $listing-white;
18+
border-radius: 8px;
19+
padding: 16px;
20+
display: flex;
21+
align-items: center;
22+
gap: 16px;
23+
width: 316px;
24+
transition: 0.25s ease-in-out;
25+
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
26+
cursor: pointer;
27+
28+
&:hover {
29+
box-shadow: 0 0 16px rgba(22, 103, 154, 0.5);
30+
}
31+
32+
@include xs-to-sm {
33+
width: 100%;
34+
}
35+
}
36+
37+
.list-item_badge {
38+
width: 48px;
39+
height: 48px;
40+
41+
svg {
42+
display: block;
43+
width: 48px;
44+
height: 48px;
45+
}
46+
}
47+
48+
.list-item_title {
49+
@include roboto-sans-regular;
50+
51+
font-size: 16px;
52+
line-height: 24px;
53+
font-weight: bold;
54+
}
55+
56+
.list-item_sub {
57+
@include roboto-sans-regular;
58+
59+
font-style: italic;
60+
font-size: 14px;
61+
line-height: 22px;
62+
color: $listing-placeholder-gray;
63+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import PT from 'prop-types';
3+
import { noop } from 'lodash/noop';
4+
import { config } from 'topcoder-react-utils';
5+
6+
import ProfileModal from '../../ProfileModal';
7+
import styles from './styles.scss';
8+
9+
const tcAcademyPath = `${config.PLATFORMUI_SITE_URL}${config.TC_ACADEMY_BASE_PATH}`;
10+
11+
const TcaCertificateModal = ({
12+
certificate,
13+
onCancel,
14+
memberHandle,
15+
}) => (
16+
<ProfileModal
17+
title="Topcoder Academy"
18+
onCancel={onCancel}
19+
containerClassName={styles['tca-certificate-modal']}
20+
>
21+
<iframe
22+
styleName="iframe"
23+
src={[
24+
tcAcademyPath,
25+
certificate.provider,
26+
certificate.certification,
27+
memberHandle,
28+
'certificate',
29+
].join('/')}
30+
title={certificate.certificationTitle}
31+
/>
32+
</ProfileModal>
33+
);
34+
35+
TcaCertificateModal.defaultProps = {
36+
onCancel: noop,
37+
};
38+
39+
TcaCertificateModal.propTypes = {
40+
certificate: PT.shape().isRequired,
41+
onCancel: PT.func,
42+
memberHandle: PT.string.isRequired,
43+
};
44+
45+
export default TcaCertificateModal;

0 commit comments

Comments
 (0)