diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1105dead68..60f4822192 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -356,7 +356,7 @@ workflows:
filters:
branches:
only:
- - gig-cache-layer
+ - member-path-component
# This is beta env for production soft releases
- "build-prod-beta":
context : org-global
diff --git a/__tests__/shared/components/__snapshots__/Content.jsx.snap b/__tests__/shared/components/__snapshots__/Content.jsx.snap
index 36cc437614..f9d9c7553a 100644
--- a/__tests__/shared/components/__snapshots__/Content.jsx.snap
+++ b/__tests__/shared/components/__snapshots__/Content.jsx.snap
@@ -1075,6 +1075,16 @@ exports[`Matches shallow shapshot 1`] = `
- Demo of Blog Feed component
+
+
+ Member Path - Path Selector
+
+
+ — Demo for path selector component on member path page
+
`;
diff --git a/src/assets/images/member-path/icon-left.svg b/src/assets/images/member-path/icon-left.svg
new file mode 100644
index 0000000000..8261f5640c
--- /dev/null
+++ b/src/assets/images/member-path/icon-left.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/src/assets/images/member-path/icon-right.svg b/src/assets/images/member-path/icon-right.svg
new file mode 100644
index 0000000000..8f44a1718c
--- /dev/null
+++ b/src/assets/images/member-path/icon-right.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/src/assets/mock-data/member-path-selector-data.json b/src/assets/mock-data/member-path-selector-data.json
new file mode 100644
index 0000000000..7091a0dab1
--- /dev/null
+++ b/src/assets/mock-data/member-path-selector-data.json
@@ -0,0 +1,40 @@
+{
+ "title": "WHAT DO YOU WANT TO DO TODAY?",
+ "items": [
+ {
+ "title": "COMPETE",
+ "iconURL": "https://media-bucket-s3.s3.eu-central-1.amazonaws.com/sx/icon-compete.svg",
+ "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-compete-active.svg",
+ "contentText": "A competition based, gig economy, a typical 9-5 job for you",
+ "btnText": "Topcoder Challenges",
+ "btnURL": "https://www.topcoder.com/challenges",
+ "btnNewTab": true
+ }, {
+ "title": "BUILD SKILLS",
+ "iconURL": "https://media-bucket-s3.s3.eu-central-1.amazonaws.com/sx/icon-build-skills.svg",
+ "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-build-skills-active.svg",
+ "contentText": "Get stronger with Skill Builder Competitions",
+ "btnText": "Find a Competition",
+ "btnURL": "https://www.topcoder.com/challenges?bucket=openForRegistration&search=Topcoder%20Skill%20Builder%20Competition&tracks[DS]=true&tracks[Des]=true&tracks[Dev]=true&tracks[QA]=true&types[]=CH&types[]=F2F&types[]=TSK",
+ "btnNewTab": true
+ },
+ {
+ "title": "LEARN",
+ "iconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-learn.svg",
+ "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-learn-active.svg",
+ "contentText": "Tutorials and workshops that matter",
+ "btnText": "Thrive",
+ "btnURL": "https://www.topcoder.com/thrive",
+ "btnNewTab": true
+ },
+ {
+ "title": "GET A GIG",
+ "iconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-gig.svg",
+ "activeIconURL": "https://s3.eu-central-1.amazonaws.com/media-bucket-s3/sx/icon-get-a-gig-active.svg",
+ "contentText": "Find a freelance job with our help",
+ "btnText": "Gig Work",
+ "btnURL": "https://www.topcoder.com/gigs",
+ "btnNewTab": true
+ }
+ ]
+}
diff --git a/src/shared/components/Content/index.jsx b/src/shared/components/Content/index.jsx
index 60b76cd665..293cb35600 100644
--- a/src/shared/components/Content/index.jsx
+++ b/src/shared/components/Content/index.jsx
@@ -861,6 +861,16 @@ export default function Content() {
{' '}
- Demo of Blog Feed component
+
+
+ Member Path - Path Selector
+
+ {' '}
+ —
+ Demo for path selector component on member path page
+
);
diff --git a/src/shared/components/Contentful/AppComponent/index.jsx b/src/shared/components/Contentful/AppComponent/index.jsx
index 5d19f81fee..a012fb82e9 100644
--- a/src/shared/components/Contentful/AppComponent/index.jsx
+++ b/src/shared/components/Contentful/AppComponent/index.jsx
@@ -12,6 +12,7 @@ import Leaderboard from 'containers/tco/Leaderboard';
import RecruitCRMJobs from 'containers/Gigs/RecruitCRMJobs';
import EmailSubscribeForm from 'containers/EmailSubscribeForm';
import GSheet from 'containers/GSheet';
+import PathSelector from 'components/MemberPath/PathSelector';
const { fireErrorMessage } = errors;
@@ -43,6 +44,14 @@ export function AppComponentSwitch(appComponent) {
if (appComponent.fields.type === 'GSheet') {
return ;
}
+ if (appComponent.fields.type === 'MemberPath') {
+ return (
+
+ );
+ }
fireErrorMessage(`Unsupported app component type ${appComponent.fields.type}`, '');
return null;
}
diff --git a/src/shared/components/MemberPath/PathSelector/index.jsx b/src/shared/components/MemberPath/PathSelector/index.jsx
new file mode 100644
index 0000000000..ae16a7c202
--- /dev/null
+++ b/src/shared/components/MemberPath/PathSelector/index.jsx
@@ -0,0 +1,133 @@
+import PT from 'prop-types';
+import React, { useEffect, useState } from 'react';
+import cn from 'classnames';
+import ArrowLeftIcon from 'assets/images/member-path/icon-left.svg';
+import ArrowRightIcon from 'assets/images/member-path/icon-right.svg';
+import styles from './styles.scss';
+
+const colorStyles = [styles.purple, styles.blue, styles.teal, styles.orange];
+
+export default function PathSelector({
+ data,
+ animationTime,
+}) {
+ const [activeItemIndex, setActiveItemIndex] = useState(0);
+ const [timer, setTimer] = useState(null);
+
+ useEffect(
+ () => {
+ setTimer(setInterval(() => {
+ setActiveItemIndex(itemIndex => (itemIndex + 1) % data.items.length);
+ },
+ animationTime));
+ return () => {
+ if (timer) {
+ clearInterval(timer);
+ }
+ };
+ },
+ [],
+ );
+ const stopTimer = () => {
+ if (timer) {
+ clearInterval(timer);
+ setTimer(null);
+ }
+ };
+
+ const handleOptionSelect = index => () => {
+ setActiveItemIndex(index);
+ // Stop the animation if user selects an item
+ stopTimer();
+ };
+
+ const handlePrevButton = () => {
+ setActiveItemIndex(activeItemIndex - 1 < 0 ? data.items.length - 1 : activeItemIndex - 1);
+ // Stop the animation if user selects an item
+ stopTimer();
+ };
+
+ const handleNextButton = () => {
+ setActiveItemIndex((activeItemIndex + 1) % data.items.length);
+ // Stop the animation if user selects an item
+ stopTimer();
+ };
+
+ const items = data.items.map((item, index) => (
+
+ ));
+
+ return (
+
+ );
+}
+
+PathSelector.defaultProps = {
+ animationTime: 3000,
+};
+
+PathSelector.propTypes = {
+ data: PT.shape({
+ items: PT.arrayOf(PT.shape({
+ title: PT.string,
+ iconURL: PT.string,
+ activeIconURL: PT.string,
+ contentText: PT.string,
+ btnText: PT.string,
+ btnURL: PT.string,
+ btnNewTab: PT.bool,
+ })),
+ title: PT.string,
+ }).isRequired,
+ animationTime: PT.number,
+};
diff --git a/src/shared/components/MemberPath/PathSelector/styles.scss b/src/shared/components/MemberPath/PathSelector/styles.scss
new file mode 100644
index 0000000000..c59e1008cd
--- /dev/null
+++ b/src/shared/components/MemberPath/PathSelector/styles.scss
@@ -0,0 +1,242 @@
+@import "~styles/mixins";
+@import "~components/buttons/themed/tc";
+
+.container {
+ width: 100%;
+ height: 618px;
+
+ @include xs-to-sm {
+ height: 504px;
+ }
+}
+
+.header {
+ background-color: #011423;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ padding: 50px 32px;
+ height: 380px;
+
+ @include xs-to-sm {
+ padding: 20px 32px 28px 32px;
+ height: 298px;
+ }
+}
+
+.title {
+ @include barlow-condensed-medium;
+
+ color: $tc-white;
+ margin-bottom: 61px;
+ font-size: 48px;
+ text-align: center;
+
+ @include xs-to-sm {
+ margin-bottom: 20px;
+ font-size: 31px;
+ line-height: 33px;
+ }
+}
+
+.options {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .arrowIcon {
+ display: none;
+ }
+
+ @include xs-to-sm {
+ .arrowIcon {
+ display: block;
+ margin: 16px;
+ }
+ }
+}
+
+.option {
+ @include barlow-condensed-semi-bold;
+
+ font-size: 24px;
+ width: 168px;
+ height: 168px;
+ border-radius: 12px;
+ margin: 0 8px;
+ border: none;
+ background: $tc-white;
+ color: $tc-gray-70;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+
+ @include xs-to-sm {
+ &:not(.active) {
+ display: none;
+ }
+ }
+
+ .icon,
+ .activeIcon {
+ display: block;
+ height: 64px;
+ width: 64px;
+ margin-bottom: 19px;
+ content: unset;
+ }
+
+ .activeIcon {
+ display: none;
+ }
+
+ &.active {
+ color: $tc-white;
+ display: flex;
+
+ .activeIcon {
+ display: block;
+ }
+
+ .icon {
+ display: none;
+ }
+
+ @include md-to-xl {
+ &.purple {
+ background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%);
+
+ &::before {
+ background: linear-gradient(305.22deg, #e6477a 0.01%, #e3467c 100%);
+ }
+ }
+
+ &.blue {
+ background: linear-gradient(90deg, #26b3c5 0%, #2984bd 100%);
+
+ &::before {
+ background: linear-gradient(90deg, #27a2c2 0%, #27a1c1 100%);
+ }
+ }
+
+ &.teal {
+ background: linear-gradient(270deg, #26b3c5 0%, #06d6a0 100%);
+
+ &::before {
+ background: linear-gradient(270deg, #1ac0b7 0%, #1ac0b8 100%);
+ }
+ }
+
+ &.orange {
+ background: linear-gradient(90deg, #ffc43d 0%, #f37593 100%);
+
+ &::before {
+ background: linear-gradient(86.76deg, #f58186 0%, #f47e89 100%);
+ }
+ }
+ }
+
+ &::after {
+ content: '';
+ display: block;
+ position: absolute;
+ bottom: -58px;
+ width: 32px;
+ height: 16px;
+ border-left: 16px solid #011423;
+ border-right: 16px solid #011423;
+ border-top: 16px solid #011423;
+ border-bottom: 16px solid transparent;
+ }
+
+ &::before {
+ content: '';
+ display: block;
+ position: absolute;
+ bottom: -59px;
+ width: 31px;
+ height: 17px;
+ }
+
+ @include xs-to-sm {
+ background: $tc-white;
+ color: $tc-gray-70;
+
+ &::before,
+ &::after {
+ display: none;
+ }
+
+ .icon {
+ display: block;
+ }
+
+ .activeIcon {
+ display: none;
+ }
+ }
+ }
+}
+
+.content {
+ background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%);
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-direction: column;
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+ padding: 41px 0 49px;
+
+ &.purple {
+ background: linear-gradient(305deg, #9d41c9 0%, #ef476f 100%);
+ }
+
+ &.blue {
+ background: linear-gradient(90deg, #26b3c5 0%, #2984bd 100%);
+ }
+
+ &.teal {
+ background: linear-gradient(270deg, #26b3c5 0%, #06d6a0 100%);
+ }
+
+ &.orange {
+ background: linear-gradient(90deg, #ffc43d 0%, #f37593 100%);
+ }
+
+ @include xs-to-sm {
+ padding: 30px 0;
+ }
+}
+
+.contentText {
+ @include roboto-regular;
+
+ color: $tc-white;
+ font-size: 24px;
+ line-height: 29px;
+ margin-bottom: 26px;
+ padding: 0 40px;
+ text-align: center;
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include xs-to-sm {
+ margin-bottom: 15px;
+ font-size: 20px;
+ line-height: 30px;
+ }
+}
+
+.contentButton {
+ @include primary-borderless;
+ @include md;
+}
diff --git a/src/shared/components/examples/MemberPathSelector/index.jsx b/src/shared/components/examples/MemberPathSelector/index.jsx
new file mode 100644
index 0000000000..cb9d614720
--- /dev/null
+++ b/src/shared/components/examples/MemberPathSelector/index.jsx
@@ -0,0 +1,15 @@
+import PathSelector from 'components/MemberPath/PathSelector';
+import React from 'react';
+import mockData from 'assets/mock-data/member-path-selector-data.json';
+import './styles.scss';
+
+export default function MemberPathSelectorExample() {
+ return (
+
+
+ Member Path - Path Selector Component Preview
+
+
+
+ );
+}
diff --git a/src/shared/components/examples/MemberPathSelector/styles.scss b/src/shared/components/examples/MemberPathSelector/styles.scss
new file mode 100644
index 0000000000..7ee3b6eee8
--- /dev/null
+++ b/src/shared/components/examples/MemberPathSelector/styles.scss
@@ -0,0 +1,8 @@
+@import "~styles/mixins";
+
+.container {
+ background: white;
+ margin: 0 auto;
+ max-width: 800px;
+ padding: 8px;
+}
diff --git a/src/shared/routes/Examples/Examples.jsx b/src/shared/routes/Examples/Examples.jsx
index 4c23246982..25d9dca746 100644
--- a/src/shared/routes/Examples/Examples.jsx
+++ b/src/shared/routes/Examples/Examples.jsx
@@ -36,6 +36,7 @@ import ThriveArticlesFeedExample from 'components/examples/ThriveArticlesFeed';
import GigsFeedExample from 'components/examples/GigsFeed';
import TCOLeaderboardsExample from 'components/examples/TCOLeaderboards';
import ChallengesFeed from 'components/examples/ChallengesFeed';
+import MemberPathSelectorExample from 'components/examples/MemberPathSelector';
import {
Switch,
@@ -106,6 +107,7 @@ export default function Examples({
+
);
diff --git a/src/shared/routes/index.jsx b/src/shared/routes/index.jsx
index 3468ce0f0b..7ffc2b2642 100644
--- a/src/shared/routes/index.jsx
+++ b/src/shared/routes/index.jsx
@@ -134,13 +134,14 @@ function Routes({ communityId }) {
/>
(
-
+
+
-
+
)}
exact
path={config.START_PAGE_PATH}