@@ -110,7 +110,7 @@ export class MemberTalkCloud extends React.Component {
}}
>
this.onSelect(index + ITEMS_ON_LEFT_SIDE + 1)}
/>
diff --git a/src/shared/components/Contentful/Viewport/index.jsx b/src/shared/components/Contentful/Viewport/index.jsx
index f8c5e6a4c8..52a79427c2 100644
--- a/src/shared/components/Contentful/Viewport/index.jsx
+++ b/src/shared/components/Contentful/Viewport/index.jsx
@@ -29,6 +29,7 @@ import MemberCard from 'components/Contentful/MemberCard';
import Article from 'components/Contentful/Article';
import { isomorphy } from 'topcoder-react-utils';
import MemberTalkCloud from 'components/Contentful/MemberTalkCloud';
+import Masonry from 'react-masonry-css';
// AOS
import AOS from 'aos';
@@ -40,6 +41,7 @@ import columnTheme from './themes/column.scss';
import rowTheme from './themes/row.scss';
import gridTheme from './themes/grid.scss';
import zurichTheme from './themes/zurich.scss';
+import masonryTheme from './themes/masonry.scss';
const { fireErrorMessage } = errors;
@@ -73,6 +75,7 @@ const THEMES = {
'Row with Max-Width': rowTheme,
Grid: gridTheme,
Zurich: zurichTheme,
+ Masonry: masonryTheme,
};
/* Loads viewport content assets. */
@@ -87,11 +90,33 @@ function ViewportContentLoader(props) {
baseUrl,
viewportId,
animationOnScroll,
+ masonryConfig,
} = props;
let {
extraStylesForContainer,
} = props;
+ const getInner = data => contentIds.map((id) => {
+ const type = data.entries.items[id].sys.contentType.sys.id;
+ const Component = COMPONENTS[type];
+ if (Component) {
+ return (
+
+ );
+ }
+ return fireErrorMessage(
+ 'Unsupported content type from contentful',
+ '',
+ );
+ });
+
const theme = THEMES[themeName];
if (!theme) {
fireErrorMessage('Unsupported theme name from contentful', '');
@@ -129,26 +154,11 @@ function ViewportContentLoader(props) {
animation={animation}
>
{
- contentIds.map((id) => {
- const type = data.entries.items[id].sys.contentType.sys.id;
- const Component = COMPONENTS[type];
- if (Component) {
- return (
-
- );
- }
- return fireErrorMessage(
- 'Unsupported content type from contentful',
- '',
- );
- })
+ themeName === 'Masonry' && masonryConfig ? (
+
+ {getInner(data)}
+
+ ) : getInner(data)
}
);
@@ -169,6 +179,7 @@ ViewportContentLoader.defaultProps = {
gap: 10,
}),
animationOnScroll: null,
+ masonryConfig: null,
};
ViewportContentLoader.propTypes = {
@@ -182,6 +193,7 @@ ViewportContentLoader.propTypes = {
grid: PT.shape(),
baseUrl: PT.string.isRequired,
animationOnScroll: PT.shape(),
+ masonryConfig: PT.shape(),
};
/* Loads the main viewport entry. */
@@ -234,6 +246,7 @@ export function ViewportLoader(props) {
}}
baseUrl={baseUrl}
animationOnScroll={viewport.fields.animationOnScroll}
+ masonryConfig={viewport.fields.masonryConfig}
/>
);
})}
diff --git a/src/shared/components/Contentful/Viewport/themes/masonry.scss b/src/shared/components/Contentful/Viewport/themes/masonry.scss
new file mode 100644
index 0000000000..6f3dd73412
--- /dev/null
+++ b/src/shared/components/Contentful/Viewport/themes/masonry.scss
@@ -0,0 +1,23 @@
+$gutterSize: 20px;
+
+.container {
+ flex-direction: column;
+ width: 100%;
+
+ :global {
+ .viewport-masonry-grid {
+ display: flex;
+ margin-left: -$gutterSize; /* gutter size offset */
+ width: auto;
+ }
+
+ .viewport-masonry-grid_column {
+ padding-left: $gutterSize; /* gutter size */
+ background-clip: padding-box;
+ }
+
+ .viewport-masonry-grid_column > * {
+ margin-bottom: $gutterSize;
+ }
+ }
+}
diff --git a/src/shared/components/Dashboard/Announcement/index.jsx b/src/shared/components/Dashboard/Announcement/index.jsx
deleted file mode 100644
index 50adc7c9b1..0000000000
--- a/src/shared/components/Dashboard/Announcement/index.jsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import _ from 'lodash';
-import LoadingIndicator from 'components/LoadingIndicator';
-import moment from 'moment';
-import PT from 'prop-types';
-import React from 'react';
-import YouTubeVideo from 'components/YouTubeVideo';
-import { PrimaryButton } from 'topcoder-react-ui-kit';
-import MarkdownRenderer from 'components/MarkdownRenderer';
-
-import style from './style.scss';
-
-export default function Announcement({
- assets,
- announcement,
- hidePreviewMetaData,
- loading,
- preview,
- show,
- switchShow,
-}) {
- if (loading) {
- return (
-
-
-
- );
- }
-
- if (!announcement || !announcement.fields) return null;
-
- const {
- backgroundImage,
- backgroundImagePosition,
- fontColor,
- maxTextWidth,
- publicTitle,
- readMore,
- readMoreLabel,
- endDate,
- startDate,
- text,
- title,
- type,
- youTubeVideoUrl,
- } = announcement.fields;
-
- const {
- createdAt,
- revision,
- updatedAt,
- } = announcement.sys;
-
- let res;
-
- if (!show) {
- res = (
-
-
switchShow(true)}
- onKeyPress={() => switchShow(true)}
- role="button"
- styleName="hide"
- tabIndex={0}
- >
- +
-
- { type ? (
-
- {type}
-
- ) : null }
-
- {publicTitle || title}
-
-
- {publicTitle || title}
-
-
- );
- } else {
- let background = _.get(backgroundImage, 'sys.id');
- if (background) background = assets[background].fields.file.url;
-
- res = (
-
- { preview ? (
-
- Preview
-
- ) : null }
-
-
switchShow(false)}
- onKeyPress={() => switchShow(false)}
- role="button"
- styleName="hide"
- tabIndex={0}
- >
- ×
-
- { type ? (
-
- {type}
-
- ) : null }
-
- {publicTitle || title}
-
-
-
-
- {
- readMore ? (
-
- {readMoreLabel || 'Read more'}
-
- ) : null
- }
-
- {
- youTubeVideoUrl ? (
-
- ) : null
- }
-
- );
- }
-
- if (preview && !hidePreviewMetaData) {
- res = (
-
-
- Created:
- {moment(createdAt).toLocaleString()}
-
-
- Last update:
- {moment(updatedAt).toLocaleString()}
-
-
- Revision:
- {revision}
-
-
- Display start date:
- {moment(startDate).toLocaleString()}
-
-
- Display end date:
- {moment(endDate).toLocaleString()}
-
-
- {res}
-
-
- );
- }
-
- return res;
-}
-
-Announcement.defaultProps = {
- assets: {},
- preview: false,
-};
-
-Announcement.propTypes = {
- assets: PT.shape(),
- announcement: PT.shape({
- fields: PT.shape({
- backgroundImage: PT.shape({
- sys: PT.shape({
- id: PT.string.isRequired,
- }).isRequired,
- }),
- backgroundImagePosition: PT.string,
- fontColor: PT.string,
- publicTitle: PT.string,
- readMore: PT.string,
- text: PT.string,
- title: PT.string,
- type: PT.string,
- youTubeVideoUrl: PT.string,
- }),
- }).isRequired,
- hidePreviewMetaData: PT.bool.isRequired,
- loading: PT.bool.isRequired,
- preview: PT.bool,
-};
diff --git a/src/shared/components/Dashboard/Announcement/style.scss b/src/shared/components/Dashboard/Announcement/style.scss
deleted file mode 100644
index 32cfdf83a3..0000000000
--- a/src/shared/components/Dashboard/Announcement/style.scss
+++ /dev/null
@@ -1,155 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- align-items: flex-start;
- background-position: 50% 50%;
- background-size: cover;
- border-bottom: 1px solid $tc-gray-neutral-dark;
- display: flex;
- min-height: 480px;
- max-width: $screen-lg;
- overflow: hidden;
- padding: 30px;
- position: relative;
- width: 100%;
-
- @include xs-to-sm {
- flex-direction: column;
- }
-}
-
-.details {
- align-items: flex-start;
- display: flex;
- flex: 1;
- flex-direction: column;
-}
-
-.hide {
- @include roboto-regular;
-
- background: $tc-gray-80;
- color: $tc-white;
- cursor: pointer;
- font-size: 38px;
- font-weight: bold;
- height: 64px;
- line-height: 64px;
- position: absolute;
- right: 0;
- text-align: center;
- top: 0;
- width: 64px;
-}
-
-.loading {
- padding: 30px;
-}
-
-.previewLabel {
- color: $tc-purple;
- font-size: 300px;
- font-weight: bold;
- left: 0;
- line-height: 100%;
- opacity: 0.2;
- pointer-events: none;
- position: absolute;
- right: 0;
- text-align: center;
- transform: translateY(-50%);
- top: 50%;
- vertical-align: bottom;
- z-index: 1000;
-
- @include xs-to-lg {
- font-size: 22vw;
- }
-}
-
-.readMore {
- margin: 20px 0 0 !important;
-}
-
-.text {
- @include tc-typography;
-
- *:not(button):not(a):not(div.disabled) {
- color: inherit !important;
- }
-}
-
-.title {
- @include tc-title;
-
- margin-bottom: 10px;
-}
-
-.type {
- @include tc-label-md;
-
- background: $tc-red;
- border-radius: 4px;
- color: $tc-white;
- padding: 5px 10px;
- margin-bottom: 30px;
- white-space: nowrap;
-}
-
-.video {
- flex: 1;
- margin: 35px 30px;
-
- @include xs-to-sm {
- align-self: stretch;
- margin-bottom: 0;
- }
-}
-
-.hidden {
- align-items: center;
- background: $tc-gray-80;
- display: flex;
- height: 64px;
- overflow: hidden;
- padding: 0 94px 0 30px;
- position: relative;
- width: 100%;
-
- .text {
- @include tc-heading-md;
-
- color: $tc-white;
- font-weight: 100;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .title {
- @include tc-heading-md;
-
- color: $tc-white;
- font-weight: 400;
- margin: 0 15px 0 0;
- white-space: nowrap;
- }
-
- .type {
- margin: 0 30px 0 0;
- }
-}
-
-.preview {
- @include tc-body-md;
-
- background: $tc-purple-10;
- border: 1px solid $tc-purple-110;
- color: $tc-purple-110;
- padding: 30px;
-}
-
-.previewContent {
- background: $tc-white;
- margin-top: 20px;
-}
diff --git a/src/shared/components/Dashboard/BlogFeed/index.jsx b/src/shared/components/Dashboard/BlogFeed/index.jsx
new file mode 100644
index 0000000000..c808b3b693
--- /dev/null
+++ b/src/shared/components/Dashboard/BlogFeed/index.jsx
@@ -0,0 +1,58 @@
+/**
+ * Blog Feed component
+ */
+
+import LoadingIndicator from 'components/LoadingIndicator';
+import PT from 'prop-types';
+import React from 'react';
+import './styles.scss';
+import { config } from 'topcoder-react-utils';
+import BlogArticlesIcon from 'assets/images/icon-blog-articles.svg';
+
+export default function BlogFeed({
+ blogs,
+ loading,
+ theme,
+}) {
+ return (
+
+
+
+ {loading ?
+ : blogs.map(blog => (
+
+ ))}
+
+
+ );
+}
+
+BlogFeed.defaultProps = {
+ blogs: [],
+ theme: 'light',
+};
+
+BlogFeed.propTypes = {
+ blogs: PT.arrayOf(PT.shape()),
+ loading: PT.bool.isRequired,
+ theme: PT.oneOf(['dark', 'light']),
+};
diff --git a/src/shared/components/Dashboard/BlogFeed/styles.scss b/src/shared/components/Dashboard/BlogFeed/styles.scss
new file mode 100644
index 0000000000..483ba5a12d
--- /dev/null
+++ b/src/shared/components/Dashboard/BlogFeed/styles.scss
@@ -0,0 +1,104 @@
+@import "~styles/mixins";
+
+.loading {
+ height: 195px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include xs-to-sm {
+ height: 285px;
+ }
+}
+
+.container {
+ @include roboto-regular;
+
+ color: $tc-gray-90;
+ padding: 13px 12px 3px 12px;
+ border-radius: 8px;
+ margin-bottom: 20px;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-bottom: 10px;
+
+ .title {
+ @include barlow-semi-bold;
+
+ display: flex;
+ font-size: 16px;
+ align-items: center;
+
+ .icon {
+ fill: $tc-gray-90;
+ width: 17px;
+ height: 17px;
+ margin-right: 9px;
+ }
+ }
+
+ .allLink {
+ color: $dashboard-light-link;
+ text-decoration: underline;
+ font-size: 13px;
+ line-height: 20px;
+
+ &:hover {
+ text-decoration: none !important;
+ }
+
+ span {
+ @include xs-to-sm {
+ display: none;
+ }
+ }
+ }
+}
+
+.blogs {
+ width: 100%;
+
+ .row {
+ display: flex;
+ font-size: 14px;
+ line-height: 18px;
+ align-items: flex-start;
+ justify-content: space-between;
+ padding: 10px 0;
+ border-top: 1px solid $tc-gray-05;
+
+ &:first-child {
+ border: none;
+ }
+ }
+}
+
+.light {
+ background-color: $dashboard-light-card-bg;
+}
+
+.container.dark {
+ color: $tc-white;
+ background: $dashboard-dark-card-bg;
+
+ .header {
+ .icon {
+ fill: $tc-white;
+ }
+
+ .allLink {
+ color: $dashboard-dark-link;
+ }
+ }
+
+ .blogs {
+ .row:not(:first-child) {
+ border-top: 1px solid $dashboard-dark-bg;
+ }
+ }
+}
diff --git a/src/shared/components/Dashboard/Challenges/index.jsx b/src/shared/components/Dashboard/Challenges/index.jsx
new file mode 100644
index 0000000000..d8a6bfbc3c
--- /dev/null
+++ b/src/shared/components/Dashboard/Challenges/index.jsx
@@ -0,0 +1,60 @@
+import _ from 'lodash';
+import LoadingIndicator from 'components/LoadingIndicator';
+import PT from 'prop-types';
+import React from 'react';
+
+import { config } from 'topcoder-react-utils';
+
+import './styles.scss';
+
+export default function ChallengesFeed({
+ challenges,
+ loading,
+ theme,
+}) {
+ return (
+
+
+
+ {loading ?
+ : challenges.map(challenge => (
+
+
+ {challenge.name}
+
+
+
+ {`$${_.sum(challenge.prizeSets
+ .map(item => _.sum(item.prizes.map(prize => prize.value)))).toLocaleString()}`}
+
+
+
+ ))}
+
+
+ );
+}
+
+ChallengesFeed.defaultProps = {
+ challenges: [],
+ theme: 'light',
+};
+
+ChallengesFeed.propTypes = {
+ challenges: PT.arrayOf(PT.shape()),
+ loading: PT.bool.isRequired,
+ theme: PT.oneOf(['dark', 'light']),
+};
diff --git a/src/shared/components/Dashboard/Challenges/styles.scss b/src/shared/components/Dashboard/Challenges/styles.scss
new file mode 100644
index 0000000000..aa3e21ff4a
--- /dev/null
+++ b/src/shared/components/Dashboard/Challenges/styles.scss
@@ -0,0 +1,127 @@
+@import "~styles/mixins";
+
+$dashboard-teal: #219174;
+$dashboard-dark-card-bg: #363636;
+$dashboard-dark-link: #5fb7ee;
+$dashboard-dark-bg: #2a2a2a;
+
+.loading {
+ height: 195px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include xs-to-sm {
+ height: 285px;
+ }
+}
+
+.container {
+ @include roboto-regular;
+
+ color: $tc-gray-90;
+ margin-bottom: 30px;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 12px 0;
+
+ .title {
+ @include barlow-semi-bold;
+
+ font-size: 16px;
+ }
+
+ .allLink {
+ color: $dashboard-teal;
+ font-size: 13px;
+ line-height: 20px;
+
+ &:hover {
+ text-decoration: none !important;
+ }
+
+ span {
+ @include xs-to-sm {
+ display: none;
+ }
+ }
+ }
+}
+
+.challenges {
+ background-color: $tc-white;
+ border-radius: 8px;
+ width: 100%;
+
+ .row {
+ display: flex;
+ font-size: 14px;
+ line-height: 18px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 0;
+ margin: 0 14px;
+ border-top: 1px solid $tc-gray-05;
+
+ &:first-child {
+ border: none;
+ }
+ }
+
+ a {
+ margin-right: 8px;
+ max-width: 70%;
+ text-overflow: ellipsis;
+ }
+
+ .prize {
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: flex-end;
+ justify-content: center;
+ }
+
+ .amount {
+ text-align: right;
+ }
+
+ @include xs-to-sm {
+ .prize {
+ display: flex;
+ flex: 1 0 90px;
+ flex-direction: column;
+ }
+
+ .amount {
+ white-space: nowrap;
+ }
+ }
+}
+
+.light {
+ background-color: transparent;
+}
+
+.container.dark {
+ color: $tc-white;
+
+ .header {
+ .allLink {
+ color: $dashboard-dark-link;
+ text-decoration: underline;
+ }
+ }
+
+ .challenges {
+ background-color: $dashboard-dark-card-bg;
+
+ .row:not(:first-child) {
+ border-top: 1px solid $dashboard-dark-bg;
+ }
+ }
+}
diff --git a/src/shared/components/Dashboard/CommunityBlog/Card/index.jsx b/src/shared/components/Dashboard/CommunityBlog/Card/index.jsx
deleted file mode 100644
index 868c8f63c6..0000000000
--- a/src/shared/components/Dashboard/CommunityBlog/Card/index.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import PT from 'prop-types';
-import React from 'react';
-
-import './style.scss';
-
-export default function Card({
- link,
- text,
- title,
-}) {
- const normalizedLink = link.replace(
- /^https:\/\/wwwtc\.staging\.wpengine\.com/,
- 'https://www.topcoder.com',
- );
- return (
-
- );
-}
-
-Card.propTypes = {
- link: PT.string.isRequired,
- text: PT.string.isRequired,
- title: PT.string.isRequired,
-};
diff --git a/src/shared/components/Dashboard/CommunityBlog/Card/style.scss b/src/shared/components/Dashboard/CommunityBlog/Card/style.scss
deleted file mode 100644
index 8607b9cd1c..0000000000
--- a/src/shared/components/Dashboard/CommunityBlog/Card/style.scss
+++ /dev/null
@@ -1,95 +0,0 @@
-@import "~styles/mixins";
-
-.content a {
- text-decoration: underline;
-}
-
-.container {
- border: 1px solid $tc-gray-neutral-dark;
- border-radius: 4px;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- margin: 10px;
- overflow: hidden;
- position: relative;
- width: 100%;
-
- a,
- a:active {
- color: $tc-dark-blue-110;
- }
-
- a:hover {
- color: $tc-dark-blue-70;
- }
-
- a:visited {
- color: $tc-purple-110;
- }
-
- @include md {
- &:nth-child(3) {
- display: none;
- }
- }
-}
-
-.content {
- @include tc-body-sm;
-
- height: 360px;
- overflow: hidden;
- padding: 20px 30px;
-
- h1,
- h2,
- h3,
- h4,
- h5,
- h6 {
- @include tc-heading-sm;
-
- margin: 15px 0 0;
- }
-
- img {
- height: auto;
- max-width: 100%;
- }
-}
-
-.fade {
- background: linear-gradient(180deg, transparent, $tc-gray-neutral-light 80%);
- bottom: 0;
- height: 200px;
- left: 0;
- position: absolute;
- right: 0;
-}
-
-.readMore {
- @include tc-label-lg;
-
- bottom: 15px;
- color: $tc-dark-blue-110;
- left: 30px;
- position: absolute;
-
- &,
- &:active,
- &:hover,
- &:visited {
- color: $tc-dark-blue-110;
- }
-}
-
-.title {
- @include tc-heading-md;
-
- background: $tc-gray-neutral-light;
- color: $tc-dark-blue-110;
- font-weight: 300;
- min-height: 100px;
- padding: 20px 30px;
-}
diff --git a/src/shared/components/Dashboard/CommunityBlog/index.jsx b/src/shared/components/Dashboard/CommunityBlog/index.jsx
deleted file mode 100644
index 2be16dea43..0000000000
--- a/src/shared/components/Dashboard/CommunityBlog/index.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import LoadingIndicator from 'components/LoadingIndicator';
-import PT from 'prop-types';
-import React from 'react';
-import { config } from 'topcoder-react-utils';
-
-import Card from './Card';
-
-import './style.scss';
-
-export default function CommunityBlog({
- isLoading,
- posts,
-}) {
- return (
-
-
- From the Community Blog
-
-
- {
- isLoading ? : (
- posts.slice(0, 3).map(post => (
-
- ))
- )
- }
-
-
-
- );
-}
-
-CommunityBlog.propTypes = {
- isLoading: PT.bool.isRequired,
- posts: PT.arrayOf(PT.shape({
- 'content:encoded': PT.string.isRequired,
- description: PT.string.isRequired,
- link: PT.string.isRequired,
- title: PT.string.isRequired,
- })).isRequired,
-};
diff --git a/src/shared/components/Dashboard/CommunityBlog/style.scss b/src/shared/components/Dashboard/CommunityBlog/style.scss
deleted file mode 100644
index 1250f274f5..0000000000
--- a/src/shared/components/Dashboard/CommunityBlog/style.scss
+++ /dev/null
@@ -1,44 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- padding: 60px 0 30px;
-}
-
-.content {
- display: flex;
- padding: 10px 20px;
-
- @include xs-to-sm {
- flex-direction: column;
- }
-}
-
-.link {
- color: $tc-dark-blue-110;
-
- &:active,
- &:visited {
- color: $tc-dark-blue-110;
- }
-
- &:hover {
- color: $tc-dark-blue-70;
- }
-}
-
-.linksContainer {
- @include tc-body-md;
-
- padding: 20px 20px 0;
- text-align: center;
-}
-
-.title {
- @include roboto-regular;
-
- text-align: center;
- font-size: 20px;
- line-height: 35px;
- color: $tc-black;
- margin: 10px 0;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/ChallengeFilter/index.jsx b/src/shared/components/Dashboard/CurrentActivity/ChallengeFilter/index.jsx
deleted file mode 100644
index 1bcf387354..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/ChallengeFilter/index.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-/* eslint jsx-a11y/no-static-element-interactions:0 */
-
-import LoadingIndicator from 'components/LoadingIndicator';
-import React from 'react';
-import PT from 'prop-types';
-
-import FilterIcon from '../../../../../assets/images/filter-icon.svg';
-
-import './style.scss';
-
-export default function ChallengeFilter({
- challengeFilter,
- communities,
- communitiesLoading,
- expand,
- expanded,
- switchChallengeFilter,
-}) {
- if (!expanded) {
- return (
-
expand(true)}
- styleName="button"
- >
-
-
- );
- }
-
- return (
-
expand(false)}
- styleName="container"
- >
- {
- communitiesLoading ? (
-
- ) : (
- communities.map(community => (
-
switchChallengeFilter(community.communityId)}
- onKeyPress={() => switchChallengeFilter(community.communityId)}
- styleName={`row ${community.communityId === challengeFilter ? 'selected' : ''}`}
- >
-
- {community.communityName}
-
-
- {community.number}
-
-
- ))
- )
- }
-
- );
-}
-
-ChallengeFilter.defaultProps = {
- communities: [],
- expanded: false,
-};
-
-ChallengeFilter.propTypes = {
- challengeFilter: PT.string.isRequired,
- communities: PT.arrayOf(PT.shape({
- communityId: PT.string.isRequired,
- communityName: PT.string.isRequired,
- number: PT.number.isRequired,
- })),
- communitiesLoading: PT.bool.isRequired,
- expand: PT.func.isRequired,
- expanded: PT.bool,
- switchChallengeFilter: PT.func.isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/ChallengeFilter/style.scss b/src/shared/components/Dashboard/CurrentActivity/ChallengeFilter/style.scss
deleted file mode 100644
index 29ee51bf2e..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/ChallengeFilter/style.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-@import '~styles/mixins';
-
-.button {
- @include tc-heading-xl;
-
- background: $tc-dark-blue;
- border-radius: 4px;
- color: $tc-white;
- cursor: pointer;
- height: 45px;
- margin: 10px;
- padding: 5px 10px;
- min-width: 45px;
- text-align: center;
-}
-
-.container {
- background: $tc-dark-blue;
- border-radius: 4px;
- padding: 10px;
- position: absolute;
- right: 10px;
- top: 10px;
- width: 300px;
-}
-
-.row {
- @include tc-label-sm;
-
- color: $tc-white;
- display: flex;
- justify-content: space-between;
- padding: 5px 12px;
- margin: 2px 0;
- cursor: pointer;
- border-radius: 4px;
-
- &.selected,
- &:hover {
- background-color: $tc-white;
- color: $tc-dark-blue-110;
- }
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Challenges/ChallengeCard/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Challenges/ChallengeCard/index.jsx
deleted file mode 100644
index 98d1699988..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Challenges/ChallengeCard/index.jsx
+++ /dev/null
@@ -1,272 +0,0 @@
-import _ from 'lodash';
-import moment from 'moment';
-import 'moment-duration-format';
-import NumRegistrants from
- 'components/challenge-listing/ChallengeCard/NumRegistrants';
-import NumSubmissions from
- 'components/challenge-listing/ChallengeCard/NumSubmissions';
-import PT from 'prop-types';
-import React from 'react';
-import {
- getTimeLeft,
-} from 'utils/challenge-detail/helper';
-
-import { config, Link } from 'topcoder-react-utils';
-
-import { COMPETITION_TRACKS } from 'utils/tc';
-
-import {
- Button,
- DataScienceTrackEventTag,
- DesignTrackEventTag,
- DevelopmentTrackEventTag,
- QATrackEventTag,
-} from 'topcoder-react-ui-kit';
-
-import style from './style.scss';
-
-export default function ChallengeCard({
- challenge,
- selectChallengeDetailsTab,
- setChallengeListingFilter,
- // unregisterFromChallenge,
- userResources,
- challengeTypesMap,
-}) {
- const {
- phases,
- legacy,
- id,
- status,
- userDetails,
- type,
- track,
- } = challenge;
-
- const typeId = _.findKey(challengeTypesMap, { name: type });
-
- let EventTag;
- switch (track) {
- case COMPETITION_TRACKS.DS:
- EventTag = DataScienceTrackEventTag;
- break;
- case COMPETITION_TRACKS.DES:
- EventTag = DesignTrackEventTag;
- break;
- case COMPETITION_TRACKS.DEV:
- EventTag = DevelopmentTrackEventTag;
- break;
- case COMPETITION_TRACKS.QA:
- EventTag = QATrackEventTag;
- break;
- default:
- throw new Error('Wrong competition track value');
- }
-
- const STALLED_MSG = 'Stalled';
- const DRAFT_MSG = 'In Draft';
-
- const forumEndpoint = track === COMPETITION_TRACKS.DES
- ? `/?module=ThreadList&forumID=${legacy.forumId}`
- : `/?module=Category&categoryID=${legacy.forumId}`;
-
- const isTco = challenge.events
- && challenge.events.find(x => x.key && x.key.match(/tco\d{2}/));
-
- const roles = _.get(userDetails, 'roles') || [];
- const role = _.find(userResources, { id }) || {};
-
- const showDirectLink = _.intersection(roles, [
- 'Approver',
- 'Copilot',
- ]).length;
-
- let showOrLink = _.intersection(roles, [
- 'Approver',
- 'Copilot',
- 'Reviewer',
- ]).length;
-
- const submitter = role.name === 'Submitter';
- const submitted = _.get(userDetails, 'hasUserSubmittedForReview');
- const nextPhase = phases && _.last(phases);
-
- const nextPhaseType = _.get(nextPhase, 'phaseType');
-
- if (submitted && _.intersection(nextPhaseType, [
- 'Appeals',
- 'Appeal Response',
- ]).length) showOrLink = true;
-
- const isChallengeOpen = status === 'Active';
-
- const allPhases = phases || [];
- let statusPhase = allPhases
- .filter(p => p.name !== 'Registration' && p.isOpen)
- .sort((a, b) => moment(a.scheduledEndDate).diff(b.scheduledEndDate))[0];
-
- if (!statusPhase && type === 'First2Finish' && allPhases.length) {
- statusPhase = _.clone(allPhases[0]);
- statusPhase.name = 'Submission';
- }
-
- let phaseMessage = STALLED_MSG;
- if (statusPhase) phaseMessage = statusPhase.name;
- else if (status === 'Draft') phaseMessage = DRAFT_MSG;
-
- let msgStyleModifier = '';
- const now = moment();
- const deadlineEnd = moment(nextPhase.scheduledEndDate);
- const late = deadlineEnd.diff(now) <= 0;
- const statusMsg = phaseMessage;
- const deadlineMsg = getTimeLeft(statusPhase, 'to go').text;
-
- if (late || phaseMessage === STALLED_MSG) msgStyleModifier = ' alert';
-
- return (
-
-
-
-
- setImmediate(
- () => setChallengeListingFilter({ types: [typeId] }),
- )
- }
- theme={{ button: style.tag }}
- to={`/challenges?filter[types][0]=${
- encodeURIComponent(typeId)}`}
- >
- {type}
-
- {
- isTco ? (
-
- TCO
-
- ) : null
- }
-
-
- {challenge.name}
-
-
-
-
-
-
- Forum
-
-
-
-
- {statusMsg}
-
-
- {deadlineMsg}
-
- {
- showDirectLink ? (
-
- Direct
-
- ) : null
- }
- {
- showOrLink ? (
-
- Online Review
-
- ) : null
- }
- {
- submitter && isChallengeOpen && phaseMessage !== STALLED_MSG ? (
-
- Submit
-
- ) : null
- }
- {
- /*
- submitter && !submitted ? (
-
unregisterFromChallenge(id)}
- >Unregister
- ) : null
- */
- }
-
-
-
-
- );
-}
-
-ChallengeCard.defaultProps = {
- userResources: [],
-};
-
-ChallengeCard.propTypes = {
- challenge: PT.shape({
- legacy: PT.shape({
- forumId: PT.oneOfType([PT.number, PT.string]),
- }).isRequired,
- id: PT.oneOfType([PT.number, PT.string]).isRequired,
- name: PT.string.isRequired,
- phases: PT.any,
- registrationStartDate: PT.any,
- status: PT.any,
- userDetails: PT.any,
- events: PT.any,
- type: PT.string,
- track: PT.string.isRequired,
- }).isRequired,
- selectChallengeDetailsTab: PT.func.isRequired,
- setChallengeListingFilter: PT.func.isRequired,
- // unregisterFromChallenge: PT.func.isRequired,
- userResources: PT.arrayOf(PT.shape()),
- challengeTypesMap: PT.shape().isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Challenges/ChallengeCard/style.scss b/src/shared/components/Dashboard/CurrentActivity/Challenges/ChallengeCard/style.scss
deleted file mode 100644
index ae8d89d09a..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Challenges/ChallengeCard/style.scss
+++ /dev/null
@@ -1,118 +0,0 @@
-@import '~styles/mixins';
-
-.button {
- min-width: 150px !important;
-}
-
-.challengeTabLinks {
- color: $tc-gray-40;
- display: flex;
- justify-content: space-between;
- padding: 10px;
-}
-
-.container {
- border: 1px solid $tc-gray-neutral-dark;
- border-radius: 4px;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- margin: 10px;
- max-width: 250px;
- min-height: 360px;
- overflow: hidden;
- width: 100%;
-}
-
-.deadlineMsg {
- @include tc-label-sm;
-
- color: $tc-gray-80;
- margin: 0 0 20px;
- min-height: 15px;
-
- &.warning {
- color: $tc-red-110;
- }
-
- &.alert {
- color: $tc-red-110;
- font-weight: bold;
- }
-}
-
-.forumLink {
- @include tc-label-sm;
-
- vertical-align: top;
-
- &,
- &:active,
- &:focus,
- &:visited {
- color: $tc-gray-40;
- }
-
- &:hover {
- color: $tc-dark-blue-110;
- }
-}
-
-.header {
- background: $tc-gray-neutral-light;
- min-height: 85px;
- padding: 10px;
-}
-
-.roles {
- @include tc-label-md;
-
- background: $tc-gray-neutral-light;
- min-height: 40px;
- overflow: hidden;
- padding: 10px;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.statusMsg {
- @include tc-heading-md;
- @include roboto-light;
-
- &.alert {
- color: $tc-red-110;
- font-weight: 300;
- }
-}
-
-.statusPanel {
- align-items: center;
- display: flex;
- flex-direction: column;
- padding: 20px;
-}
-
-.tag {
- margin: 0 5px 0 0;
-}
-
-.tags {
- margin-bottom: 5px;
-}
-
-.title {
- @include tc-label-md;
-
- display: block;
-
- &,
- &:active,
- &:focus,
- &:visited {
- color: $tc-black;
- }
-
- &:hover {
- color: $tc-dark-blue-110;
- }
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Challenges/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Challenges/index.jsx
deleted file mode 100644
index 653d33a91c..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Challenges/index.jsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import _ from 'lodash';
-import LoadingIndicator from 'components/LoadingIndicator';
-import moment from 'moment';
-import PT from 'prop-types';
-import React from 'react';
-import Sticky from 'react-stickynode';
-import { config, Link } from 'topcoder-react-utils';
-
-import { challenge as challengeUtils } from 'topcoder-react-lib';
-
-import ChallengeCard from './ChallengeCard';
-import ChallengeFilter from '../ChallengeFilter';
-
-import style from './style.scss';
-
-const Filter = challengeUtils.filter;
-
-export default function Challenges({
- challengeFilter,
- challenges,
- challengesLoading,
- communities,
- communitiesLoading,
- selectChallengeDetailsTab,
- setChallengeListingFilter,
- showChallengeFilter,
- switchChallengeFilter,
- switchShowChallengeFilter,
- unregisterFromChallenge,
- userResources,
- challengeTypesMap,
-}) {
- if (challengesLoading) {
- return (
-
-
-
- );
- }
-
- let filteredChallenges = challenges;
- if (challengeFilter) {
- let filter = communities.find(x => x.communityId === challengeFilter);
- if (filter) {
- filter = Filter.getFilterFunction(filter.challengeFilter);
- filteredChallenges = challenges.filter(x => filter(x));
- }
- }
-
- const now = moment();
- filteredChallenges = _.clone(filteredChallenges);
- for (let i = 0; i < filteredChallenges.length; i += 1) {
- const ch = filteredChallenges[i];
- const nextPhase = ch.currentPhases && _.last(ch.currentPhases);
- if (nextPhase) {
- const deadlineEnd = moment(nextPhase.scheduledEndDate);
- ch.dashboardPriority = deadlineEnd.diff(now);
- } else if (moment(ch.registrationStartDate).isAfter(now)) {
- ch.dashboardPriority = moment(ch.registrationStartDate).diff(now);
- } else if (ch.status === 'Completed') {
- ch.dashboardPriority = Number.MAX_VALUE;
- } else ch.dashboardPriority = -Number.MAX_VALUE;
- }
- filteredChallenges.sort((a, b) => a.dashboardPriority - b.dashboardPriority);
-
- return (
-
-
-
- {
- filteredChallenges.length ? (
- filteredChallenges.map(item => (
-
- ))
- ) : (
-
- {
- challengeFilter ? (
- 'You have no active challenges in the selected community'
- ) : (
-
-
- You have no active challenges at this moment. What are
- you interested in?
-
-
- Competitive Programming
-
- ?
-
-
- Data Science
-
- ?
-
-
- Design
-
- ?
-
-
- Software Development
-
- ?
-
-
- )
- }
-
- )
- }
-
-
-
-
-
-
- );
-}
-
-Challenges.defaultProps = {
- userResources: [],
-};
-
-Challenges.propTypes = {
- challengeFilter: PT.string.isRequired,
- challenges: PT.arrayOf(PT.object).isRequired,
- challengesLoading: PT.bool.isRequired,
- communities: PT.arrayOf(PT.object).isRequired,
- communitiesLoading: PT.bool.isRequired,
- selectChallengeDetailsTab: PT.func.isRequired,
- setChallengeListingFilter: PT.func.isRequired,
- showChallengeFilter: PT.bool.isRequired,
- switchChallengeFilter: PT.func.isRequired,
- switchShowChallengeFilter: PT.func.isRequired,
- unregisterFromChallenge: PT.func.isRequired,
- userResources: PT.arrayOf(PT.shape()),
- challengeTypesMap: PT.shape().isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Challenges/style.scss b/src/shared/components/Dashboard/CurrentActivity/Challenges/style.scss
deleted file mode 100644
index 771ce5b0f6..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Challenges/style.scss
+++ /dev/null
@@ -1,45 +0,0 @@
-@import "~styles/mixins";
-
-.challenges {
- display: flex;
- flex: 1;
- flex-wrap: wrap;
- justify-content: center;
-}
-
-.container {
- padding: 20px 20px 0;
-}
-
-.innerContainer {
- display: flex;
- position: relative;
-
- @include xs-to-sm {
- align-items: flex-end;
- flex-direction: column-reverse;
- }
-}
-
-.loading {
- margin: 40px 20px 30px;
-}
-
-.msg {
- @include tc-heading-md;
-
- max-width: 640px;
- padding: 20px 0 0;
-
- a,
- a:active,
- a:focus,
- a:hover,
- a:visited {
- color: $tc-dark-blue-110;
- }
-}
-
-.sticky {
- width: 65px;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Communities/Card/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Communities/Card/index.jsx
deleted file mode 100644
index d9a65dd2e4..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Communities/Card/index.jsx
+++ /dev/null
@@ -1,111 +0,0 @@
-/* eslint-disable global-require, import/no-dynamic-require */
-
-import LoadingIndicator from 'components/LoadingIndicator';
-import PT from 'prop-types';
-import React from 'react';
-
-import { getSubCommunityBaseUrl } from 'utils/tc';
-
-import './style.scss';
-
-export default function CommunityTile(props) {
- const { community, stats, statsLoading /* , registered */ } = props;
-
- const baseUrl = getSubCommunityBaseUrl(community);
-
- return (
-
-
-
-
- {community.communityName}
-
-
- {community.description}
-
-
- Learn more
-
-
-
-
- {
- statsLoading ? (
-
- ) : (
-
-
-
- {(stats.numMembers || 0).toLocaleString()}
-
-
- Members
-
-
-
-
- {stats.numChallenges || 0}
-
-
- Challenges
-
-
-
-
- {stats.openPrizes || '$0'}
-
-
- Purse Cash
-
-
-
- )
- }
-
-
-
-
- );
-}
-
-CommunityTile.propTypes = {
- stats: PT.shape(),
- statsLoading: PT.bool.isRequired,
- community: PT.shape({
- communityId: PT.string.isRequired,
- communityName: PT.string.isRequired,
- description: PT.string.isRequired,
- image: PT.string.isRequired,
- }),
- // registered: PT.bool,
-};
-
-CommunityTile.defaultProps = {
- stats: {},
- community: {},
- // registered: false,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Communities/Card/style.scss b/src/shared/components/Dashboard/CurrentActivity/Communities/Card/style.scss
deleted file mode 100644
index 0a577eb2ee..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Communities/Card/style.scss
+++ /dev/null
@@ -1,154 +0,0 @@
-@import '~styles/mixins';
-
-.container {
- border-radius: 3 * $corner-radius;
- margin-bottom: 20px;
- display: flex;
- overflow: hidden;
- flex-direction: row;
- height: 234px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- box-shadow: 0 0 1px 1px $tc-gray-neutral-dark;
-}
-
-.left {
- width: 50%;
- padding: 35px 40px 0;
- position: relative;
-
- img {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
-}
-
-.name {
- color: $tc-white;
- font-weight: bold;
- font-size: 30px;
- position: relative;
- z-index: 1;
-}
-
-.learn-more {
- position: relative;
- z-index: 1;
- display: block;
- height: 43px;
- width: 128px;
- line-height: 43px;
- border-radius: 5px;
- background: $tc-white;
- text-align: center;
-
- &,
- &:hover,
- &:visited {
- color: #0096ff;
- }
-}
-
-.desc {
- position: relative;
- z-index: 1;
- color: $tc-white;
- margin: 20px 0;
- line-height: 24px;
-}
-
-.right {
- width: 50%;
- padding: 22px 32px 0 40px;
- background-color: $tc-white;
-}
-
-.stats {
- padding-left: 20px;
- padding-bottom: 30px;
- border-bottom: 1px solid #f0f0f0;
- margin-bottom: 30px;
-}
-
-.stats-item {
- margin-right: 50px;
-}
-
-.statsInner {
- display: flex;
- flex-direction: row;
-}
-
-.value {
- font-size: 36px;
- font-weight: 300;
- line-height: 60px;
-}
-
-.label {
- font-size: 16px;
- color: #666;
-}
-
-.actions {
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.reg,
-.unreg {
- margin-right: 40px;
- height: 43px;
- width: 107px;
- display: block;
- border-radius: 5px;
- text-align: center;
- line-height: 41px;
- font-size: 14px;
- cursor: pointer;
- flex-shrink: 0;
-}
-
-.reg {
- background-color: #0096ff;
- color: $tc-white;
- border: 1px solid #0096ff;
-
- &:hover {
- background-color: $tc-white;
- color: #0096ff;
- }
-}
-
-.unreg {
- background-color: $tc-white;
- color: #e66e66;
- border: 1px solid #e66e66;
-
- &:hover {
- background-color: #e66e66;
- color: $tc-white;
- }
-}
-
-.link {
- font-weight: 300;
- font-size: 14px;
- line-height: 14px;
-
- &,
- &:hover,
- &:visited {
- color: #0096ff;
- }
-}
-
-.pipe {
- margin: 0 14px;
- width: 1px;
- height: 14px;
- background-color: #0096ff;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Communities/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Communities/index.jsx
deleted file mode 100644
index 3810152343..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Communities/index.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import LoadingIndicator from 'components/LoadingIndicator';
-import PT from 'prop-types';
-import React from 'react';
-
-import Card from './Card';
-
-import './style.scss';
-
-export default function Communities({
- communities,
- communitiesLoading,
- communityStats,
-}) {
- if (communitiesLoading) {
- return (
-
-
-
- );
- }
-
- return (
-
- {
- communities.length ? (
- communities.map((c) => {
- const stats = communityStats[c.communityId] || {};
- return (
- /* !this.state.showMyCommunityOnly || this.isCommunityRegstered(c)) && */
-
-
-
- );
- })
- ) : (
-
- You have not joined any Topcoder sub-community yet.
-
- )
- }
-
- );
-}
-
-Communities.propTypes = {
- communities: PT.arrayOf(PT.object).isRequired,
- communitiesLoading: PT.bool.isRequired,
- communityStats: PT.shape().isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Communities/style.scss b/src/shared/components/Dashboard/CurrentActivity/Communities/style.scss
deleted file mode 100644
index e99d14c375..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Communities/style.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- padding: 20px 30px;
-}
-
-.loading {
- padding: 40px 20px 30px;
-}
-
-.msg {
- @include tc-heading-md;
-
- padding: 20px 0 0;
- text-align: center;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Header/Option/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Header/Option/index.jsx
deleted file mode 100644
index e585918639..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Header/Option/index.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import PT from 'prop-types';
-import React from 'react';
-
-import './style.scss';
-
-export default function Option({ select, selected, title }) {
- let containerStyle = 'container';
- if (selected) containerStyle += ' selected';
-
- return (
-
-
- {title}
-
- {
- selected ?
: null
- }
-
- );
-}
-
-Option.propTypes = {
- select: PT.func.isRequired,
- selected: PT.bool.isRequired,
- title: PT.string.isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Header/Option/style.scss b/src/shared/components/Dashboard/CurrentActivity/Header/Option/style.scss
deleted file mode 100644
index 483ae75a27..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Header/Option/style.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- @include tc-heading-md;
-
- color: $tc-gray-50;
- cursor: pointer;
- padding: 0 10px;
- margin: 0 10px;
- text-transform: uppercase;
- white-space: nowrap;
-}
-
-.selected {
- color: $tc-black;
- cursor: default;
- font-weight: 700;
-}
-
-.selectedUnderline {
- background: $tc-dark-blue-70;
- color: $tc-black;
- cursor: default;
- height: 2px;
- margin: 10px auto;
- width: 70%;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Header/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Header/index.jsx
deleted file mode 100644
index a5909d417a..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Header/index.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import Carousel from 'components/XCarousel';
-import PT from 'prop-types';
-import React from 'react';
-
-import { TABS } from 'actions/page/dashboard';
-
-import Option from './Option';
-
-import './style.scss';
-
-export default function Header({
- // numChallenges,
- // numCommunities,
- switchTab,
- tab,
-}) {
- /**
- * Temporary hide My Active Challenges - community-app#5004
- */
- /*
- let myChallengesTitle = 'My Active Challenges';
- if (numChallenges) myChallengesTitle += ` (${numChallenges})`;
- */
-
- // let myCommunitiesTitle = 'My Communities';
- // if (numCommunities) myCommunitiesTitle += ` (${numCommunities})`;
-
- return (
-
-
-
- {/* {/* Temporary hide My Active Challenges - community-app#5004
- switchTab(TABS.MY_ACTIVE_CHALLENGES)}
- title={myChallengesTitle}
- />
- */}
- {/* Hide My Communities community-app#5288
- switchTab(TABS.COMMUNITIES)}
- title={myCommunitiesTitle}
- />
- */}
- switchTab(TABS.SRMS)}
- title="SRMs"
- />
-
-
-
- );
-}
-
-Header.propTypes = {
- // numChallenges: PT.number.isRequired,
- // numCommunities: PT.number.isRequired,
- switchTab: PT.func.isRequired,
- tab: PT.string.isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Header/style.scss b/src/shared/components/Dashboard/CurrentActivity/Header/style.scss
deleted file mode 100644
index 5622733757..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Header/style.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- display: flex;
- justify-content: center;
-}
-
-.option {
- @include tc-heading-lg;
-
- margin: 0 20px;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/Srms/index.jsx b/src/shared/components/Dashboard/CurrentActivity/Srms/index.jsx
deleted file mode 100644
index a72bcfe909..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Srms/index.jsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import LoadingIndicator from 'components/LoadingIndicator';
-import PT from 'prop-types';
-import React from 'react';
-import SRM from '../../SRM';
-
-import './style.scss';
-
-export default function Srms({
- srms,
- srmsLoading,
-}) {
- if (srmsLoading) {
- return (
-
-
-
- );
- }
-
- return (
-
-
-
- );
-}
-
-Srms.propTypes = {
- srms: PT.arrayOf(PT.object).isRequired,
- srmsLoading: PT.bool.isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/Srms/style.scss b/src/shared/components/Dashboard/CurrentActivity/Srms/style.scss
deleted file mode 100644
index 922e7ae93c..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/Srms/style.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- padding: 20px;
-}
-
-.loading {
- padding: 40px 20px;
-}
diff --git a/src/shared/components/Dashboard/CurrentActivity/index.jsx b/src/shared/components/Dashboard/CurrentActivity/index.jsx
deleted file mode 100644
index 0beb9f9ca0..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/index.jsx
+++ /dev/null
@@ -1,164 +0,0 @@
-/* eslint jsx-a11y/no-noninteractive-element-interactions:0 */
-
-import React from 'react';
-import PT from 'prop-types';
-import _ from 'lodash';
-import { TABS } from 'actions/page/dashboard';
-
-// import { challenge as challengeUtils } from 'topcoder-react-lib';
-
-// import Challenges from './Challenges';
-// import Communities from './Communities';
-import Header from './Header';
-import Srms from './Srms';
-
-import './styles.scss';
-
-// const Filter = challengeUtils.filter;
-
-/* eslint-disable react/no-unused-state */
-
-export default class MyChallenges extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTab: 0,
- viewMode: 'tile',
- selectedCommunityId: '',
- showMyCommunityOnly: false,
- };
- this.setViewMode = this.setViewMode.bind(this);
- this.setTab = this.setTab.bind(this);
- this.selectCommunity = this.selectCommunity.bind(this);
- }
-
- setViewMode(viewMode) {
- this.setState({
- viewMode,
- });
- }
-
- setTab(activeTab) {
- this.setState({
- activeTab,
- });
- }
-
- selectCommunity(id) {
- this.setState({
- selectedCommunityId: id,
- });
- }
-
- render() {
- const {
- // challengeFilter,
- // challenges,
- // challengesLoading,
- // communities,
- // communitiesLoading,
- // communityStats,
- // selectChallengeDetailsTab,
- // setChallengeListingFilter,
- // showChallengeFilter,
- srms,
- srmsLoading,
- // switchChallengeFilter,
- // switchShowChallengeFilter,
- switchTab,
- tab,
- // unregisterFromChallenge,
- // userGroups,
- // userResources,
- // challengeTypesMap,
- } = this.props;
-
- /* Hide My Communities community-app#5288
- const myCommunities = communities.filter(x => _.intersection(userGroups, x.groupIds).length)
- .map((community) => {
- const filter = Filter.getFilterFunction(community.challengeFilter);
- const res = _.clone(community);
- res.number = challenges.filter(x => filter(x)).length;
- return res;
- });
- */
-
- return (
-
-
- { /*
- tab === TABS.MY_ACTIVE_CHALLENGES ? (
-
- ) : null
- */}
- {/* Hide My Communities community-app#5288
- tab === TABS.COMMUNITIES ? (
-
- ) : null
- */}
- {
- tab === TABS.SRMS ? (
-
- ) : null
- }
-
- );
- }
-}
-
-MyChallenges.defaultProps = {
- // challenges: [],
- // userResources: [],
-};
-
-MyChallenges.propTypes = {
- // challengeFilter: PT.string.isRequired,
- // challenges: PT.arrayOf(PT.object),
- // challengesLoading: PT.bool.isRequired,
- // communities: PT.arrayOf(PT.object).isRequired,
- // communitiesLoading: PT.bool.isRequired,
- // communityStats: PT.shape().isRequired,
- // selectChallengeDetailsTab: PT.func.isRequired,
- // setChallengeListingFilter: PT.func.isRequired,
- // showChallengeFilter: PT.bool.isRequired,
- srms: PT.arrayOf(PT.object).isRequired,
- srmsLoading: PT.bool.isRequired,
- // switchChallengeFilter: PT.func.isRequired,
- // switchShowChallengeFilter: PT.func.isRequired,
- switchTab: PT.func.isRequired,
- tab: PT.oneOf(_.values(TABS)).isRequired,
- // unregisterFromChallenge: PT.func.isRequired,
- // userGroups: PT.arrayOf(PT.string).isRequired,
- // userResources: PT.arrayOf(PT.shape()),
- // challengeTypesMap: PT.shape().isRequired,
-};
diff --git a/src/shared/components/Dashboard/CurrentActivity/styles.scss b/src/shared/components/Dashboard/CurrentActivity/styles.scss
deleted file mode 100644
index a7a7ce6bd8..0000000000
--- a/src/shared/components/Dashboard/CurrentActivity/styles.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-@import '~styles/mixins';
-
-.container {
- border-bottom: 1px solid $tc-gray-neutral-dark;
- padding: 60px 0 30px;
-}
diff --git a/src/shared/components/Dashboard/GigsFeed/index.jsx b/src/shared/components/Dashboard/GigsFeed/index.jsx
new file mode 100644
index 0000000000..3750a100b6
--- /dev/null
+++ b/src/shared/components/Dashboard/GigsFeed/index.jsx
@@ -0,0 +1,63 @@
+/* eslint-disable no-nested-ternary */
+/**
+ * Gigs Feed component
+ */
+import LoadingIndicator from 'components/LoadingIndicator';
+import PT from 'prop-types';
+import React from 'react';
+import './styles.scss';
+import { config } from 'topcoder-react-utils';
+
+export default function GigsFeed({
+ gigs,
+ loading,
+ theme,
+}) {
+ const formatRateType = rateType => `/${rateType === 'weekly' ? 'week' : rateType}`;
+ return (
+
+
+
+ {loading ?
+ : gigs.message ?
{gigs.message} : gigs.map(gig => (
+
+
{gig.title}
+
+
+
+ ${`${(gig.minSalary || 0).toLocaleString()} - $${
+ (gig.maxSalary || 0).toLocaleString()}`
+ }
+
+ {formatRateType(gig.rateType)}
+
+
+ ))}
+
+
+ );
+}
+
+GigsFeed.defaultProps = {
+ gigs: [],
+ theme: 'light',
+};
+
+GigsFeed.propTypes = {
+ gigs: PT.arrayOf(PT.shape()),
+ loading: PT.bool.isRequired,
+ theme: PT.oneOf(['dark', 'light']),
+};
diff --git a/src/shared/components/Dashboard/GigsFeed/styles.scss b/src/shared/components/Dashboard/GigsFeed/styles.scss
new file mode 100644
index 0000000000..060b9e0dbe
--- /dev/null
+++ b/src/shared/components/Dashboard/GigsFeed/styles.scss
@@ -0,0 +1,130 @@
+@import "~styles/mixins";
+
+.loading {
+ height: 195px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include xs-to-sm {
+ height: 285px;
+ }
+}
+
+.container {
+ @include roboto-regular;
+
+ color: $tc-gray-90;
+ margin-bottom: 30px;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 12px 0;
+
+ .title {
+ @include barlow-semi-bold;
+
+ font-size: 16px;
+ }
+
+ .allLink {
+ color: $dashboard-teal;
+ font-size: 13px;
+ line-height: 20px;
+
+ &:hover {
+ text-decoration: none !important;
+ }
+
+ span {
+ @include xs-to-sm {
+ display: none;
+ }
+ }
+ }
+}
+
+.gigs {
+ padding: 0 14px;
+ background-color: $tc-white;
+ border-radius: 8px;
+ height: 195px !important;
+ width: 100%;
+
+ @include xs-to-sm {
+ height: 285px !important;
+ }
+
+ .row {
+ display: flex;
+ font-size: 14px;
+ line-height: 18px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 0;
+ border-top: 1px solid $tc-gray-05;
+
+ a {
+ margin-right: 8px;
+ }
+
+ .salary {
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: flex-end;
+ justify-content: center;
+ }
+
+ .amount {
+ text-align: right;
+ }
+
+ .rateType {
+ margin-left: 7px;
+ text-align: right;
+ }
+
+ &:first-child {
+ border: none;
+ }
+
+ @include xs-to-sm {
+ .salary {
+ display: flex;
+ flex: 1 0 90px;
+ flex-direction: column;
+ }
+
+ .amount {
+ white-space: nowrap;
+ }
+ }
+ }
+}
+
+.light {
+ background: transparent;
+}
+
+.container.dark {
+ color: $tc-white;
+
+ .header {
+ .allLink {
+ color: $dashboard-dark-link;
+ text-decoration: underline;
+ }
+ }
+
+ .gigs {
+ background: $dashboard-dark-card-bg;
+
+ .row {
+ border-top: 1px solid $dashboard-dark-bg;
+ }
+ }
+}
diff --git a/src/shared/components/Dashboard/Header/Badge/index.jsx b/src/shared/components/Dashboard/Header/Badge/index.jsx
deleted file mode 100644
index 8f9ee80d3c..0000000000
--- a/src/shared/components/Dashboard/Header/Badge/index.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import _ from 'lodash';
-import PT from 'prop-types';
-import React from 'react';
-
-import BadgePredixCommunity from
- 'assets/images/dashboard/badge-predix-community.png';
-import BadgeSrmEngagementHonor from
- 'assets/images/dashboard/badge-srm-engagement-honor.png';
-import BadgeTco17Champion from
- 'assets/images/dashboard/badge-tco17-gold.svg';
-import BadgeTco17Finalist from
- 'assets/images/dashboard/badge-tco17-finalist.svg';
-import BadgeTco18Finalist from
- 'assets/images/dashboard/badge-tco18-finalist.svg';
-
-import './style.scss';
-
-export const MAP = {
- 'Algorithm Target': 'algorithmTarget',
- 'CoECI Client Badge': 'coEciClientBadge',
- 'Crowd for Good': 'crowdForGood',
- 'Designer of the Month': 'designerOfTheMonth',
- 'Digital Run Top Five': 'digitalRunTopFive',
- 'Digital Run Winner': 'digitalRunWinner',
- 'iOS Community': 'iosCommunity',
- 'Marathon Match Winner': 'marathonMatchWinner',
- 'Member of the Month': 'memberOfTheMonth',
- 'Predix Community': 'predixCommunity',
- 'Solved Hard Div1 Problem in SRM': 'solvedHardDiv1ProblemInSrm',
- 'Solved Hard Div2 Problem in SRM': 'solvedHardDiv2ProblemInSrm',
- 'SRM Engagement Honor': 'srmEngagementHonor',
- 'SRM Winner Div 1': 'srmWinnerDiv1',
- 'SRM Winner Div 2': 'srmWinnerDiv2',
- 'Studio Cup Top Five': 'studioCupTopFive',
- 'Studio Cup Winner': 'studioCupWinner',
- 'Studio Mentor': 'studioMentor',
- 'Studio Reviewer': 'studioReviewer',
- 'Studio Screener': 'studioScreener',
- 'Studio Spec Reviewer': 'studioSpecReviewer',
- 'Studio Spirit': 'studioSpirit',
- 'TCO17 Champion': 'tco17Champion',
- 'TCO17 Finalist': 'tco17Finalist',
- 'TCO18 Finalist': 'tco18Finalist',
- 'TopCoder Reviewer': 'topcoderReviewer',
-};
-
-/* Holds the mapping between achievement names and optional XL size badges. */
-export const XL_MAP = {
- 'Predix Community': BadgePredixCommunity,
- 'SRM Engagement Honor': BadgeSrmEngagementHonor,
- 'TCO17 Champion':
,
- 'TCO17 Finalist':
,
- 'TCO18 Finalist':
,
-};
-
-export default function Badge({
- badge,
- showXl,
- title,
- xlBadge,
-}) {
- let xlBadgeNode;
- if (xlBadge) {
- xlBadgeNode = (
-
{
- e.stopPropagation();
- showXl();
- }}
- styleName="xlBadgeNode"
- >
-
{
- e.stopPropagation();
- }}
- styleName="xlBadgeHider"
- />
- {
- _.isString(xlBadge) ? (
-
- ) : xlBadge
- }
-
- );
- }
- return (
-
showXl(true)}
- onMouseEnter={() => showXl(true)}
- onMouseLeave={() => showXl()}
- styleName={`badge ${badge}`}
- title={title}
- >
- {xlBadgeNode}
-
- );
-}
-
-Badge.defaultProps = {
- xlBadge: '',
-};
-
-Badge.propTypes = {
- badge: PT.string.isRequired,
- showXl: PT.func.isRequired,
- title: PT.string.isRequired,
- xlBadge: PT.oneOfType([PT.node, PT.string]),
-};
diff --git a/src/shared/components/Dashboard/Header/Badge/style.scss b/src/shared/components/Dashboard/Header/Badge/style.scss
deleted file mode 100644
index f1918f91df..0000000000
--- a/src/shared/components/Dashboard/Header/Badge/style.scss
+++ /dev/null
@@ -1,154 +0,0 @@
-@import "~styles/mixins";
-
-.badge {
- background: url('assets/images/badges.png');
- height: 48px;
- margin: 1px 5px;
- position: relative;
- width: 48px;
-}
-
-.xlBadge {
- height: 66%;
- width: 66%;
-}
-
-.algorithmTarget {
- background-position: -48px -576px;
-}
-
-.coEciClientBadge {
- background-position: -146px 48px;
-}
-
-.crowdForGood {
- background-position: 0 -672px;
-}
-
-.designerOfTheMonth {
- background-position: -48px -240px;
-}
-
-.digitalRunTopFive {
- background-position: 0 -288px;
-}
-
-.digitalRunWinner {
- background-position: 0 -384px;
-}
-
-.iosCommunity {
- background-position: -95px -672px;
-}
-
-.marathonMatchWinner {
- background-position: 0 -576px;
-}
-
-.memberOfTheMonth {
- background-position: -48px -240px;
-}
-
-.predixCommunity {
- background-position: -47px -672px;
-}
-
-.solvedHardDiv1ProblemInSrm {
- background-position: 0 -624px;
-}
-
-.solvedHardDiv2ProblemInSrm {
- background-position: -48px -624px;
-}
-
-.srmEngagementHonor {
- background-position: -146px -671px;
-}
-
-.srmWinnerDiv1 {
- background-position: -144px -528px;
-}
-
-.srmWinnerDiv2 {
- background-position: -192px -528px;
-}
-
-.studioCupTopFive {
- background-position: 0 -240px;
-}
-
-.studioCupWinner {
- background-position: 0 -336px;
-}
-
-.studioMentor {
- background-position: -96px -336px;
-}
-
-.studioReviewer {
- background-position: -192px -288px;
-}
-
-.studioScreener {
- background-position: -144px -288px;
-}
-
-.studioSpecReviewer {
- background-position: -96px -288px;
-}
-
-.studioSpirit {
- background-position: -48px -336px;
-}
-
-.tco17Champion {
- background: url('assets/images/dashboard/badge-tco17-gold.svg');
- background-size: 48px 48px;
-}
-
-.tco17Finalist {
- background: url('assets/images/dashboard/badge-tco17-finalist.svg');
- background-size: 48px 48px;
-}
-
-.tco18Finalist {
- background: url('assets/images/dashboard/badge-tco18-finalist.svg');
- background-size: 48px 48px;
-}
-
-.topcoderReviewer {
- background-position: -192px -288px;
-}
-
-.xlBadgeHider {
- background: transparent;
- height: 48px;
- left: 76px;
- position: absolute;
- top: 76px;
- width: 48px;
-}
-
-.xlBadgeNode {
- align-items: center;
- background: white;
- border-radius: 50%;
- box-shadow: 0 0 10px 2px $tc-gray-40;
- display: flex;
- height: 200px;
- justify-content: center;
- left: -76px;
- position: absolute;
- top: -76px;
- width: 200px;
- z-index: 6;
-
- img {
- width: 100%;
- height: 100%;
- }
-}
-
-.xlBadgeImage {
- width: 100%;
-}
diff --git a/src/shared/components/Dashboard/Header/index.jsx b/src/shared/components/Dashboard/Header/index.jsx
deleted file mode 100644
index 673942d47c..0000000000
--- a/src/shared/components/Dashboard/Header/index.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Child component of Dashboard/Header renders summary of user achievements,
- * money earned and number of active challenges. Also renders special badges
- * based of acheivement data.
-*/
-import React from 'react';
-import PT from 'prop-types';
-
-import Badge, { MAP, XL_MAP } from './Badge';
-
-import './styles.scss';
-
-export default function Header(props) {
- const {
- achievements,
- showXlBadge,
- xlBadge,
- } = props;
- const badges = achievements.filter(x => MAP[x.description]);
- return (
-
-
- Dashboard
-
-
- {
- badges.map(({ description }) => (
- showXlBadge(show && description)}
- title={description}
- xlBadge={xlBadge === description && XL_MAP[xlBadge]}
- />
- ))
- }
-
-
- );
-}
-
-Header.defaultProps = {
- achievements: [],
-};
-
-Header.propTypes = {
- achievements: PT.arrayOf(PT.object),
- showXlBadge: PT.func.isRequired,
- xlBadge: PT.string.isRequired,
-};
diff --git a/src/shared/components/Dashboard/Header/styles.scss b/src/shared/components/Dashboard/Header/styles.scss
deleted file mode 100644
index c52f7f332e..0000000000
--- a/src/shared/components/Dashboard/Header/styles.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-@import "~styles/mixins";
-
-.badges {
- display: flex;
- flex-wrap: wrap;
- justify-content: flex-end;
-
- @include xs-to-sm {
- justify-content: center;
- }
-}
-
-.container {
- background: $tc-gray-neutral-light;
- display: flex;
- justify-content: space-between;
- padding: 20px 25px 20px 30px;
- position: relative;
-
- @include xs-to-sm {
- flex-direction: column;
- text-align: center;
- }
-}
-
-.title {
- @include tc-title;
-
- margin-right: 20px;
- max-width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/Earnings/Coin/index.jsx b/src/shared/components/Dashboard/MemberMetrics/Earnings/Coin/index.jsx
deleted file mode 100644
index bb89b10f39..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Earnings/Coin/index.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-import './style.scss';
-
-export default function Coin() {
- return (
-
- $
-
- );
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/Earnings/Coin/style.scss b/src/shared/components/Dashboard/MemberMetrics/Earnings/Coin/style.scss
deleted file mode 100644
index 3652eb8e4f..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Earnings/Coin/style.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-@import "~styles/mixins";
-
-.coin {
- background: $tc-gold;
- border-radius: 35px;
- color: $tc-gold-110;
- display: inline-block;
- font-size: 18px;
- height: 26px;
- line-height: 26px;
- margin: 10px;
- vertical-align: top;
- width: 26px;
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/Earnings/Dial/index.jsx b/src/shared/components/Dashboard/MemberMetrics/Earnings/Dial/index.jsx
deleted file mode 100644
index 69ce8713ec..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Earnings/Dial/index.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * The Dial component shows a single competition track statistic.
- */
-
-import _ from 'lodash';
-import PT from 'prop-types';
-import React from 'react';
-
-import Coin from '../Coin';
-
-import './style.scss';
-
-export default function Dial({
- amount,
- show,
- title,
- url,
-}) {
- return (
-
-
- {_.startCase(title)}
-
-
-
- {
- show ? Math.round(amount).toLocaleString() : (
-
- hidden
-
- )
- }
-
-
- );
-}
-
-Dial.propTypes = {
- amount: PT.number.isRequired,
- show: PT.bool.isRequired,
- title: PT.string.isRequired,
- url: PT.string.isRequired,
-};
diff --git a/src/shared/components/Dashboard/MemberMetrics/Earnings/Dial/style.scss b/src/shared/components/Dashboard/MemberMetrics/Earnings/Dial/style.scss
deleted file mode 100644
index af17c2aec3..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Earnings/Dial/style.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@import '~styles/mixins';
-
-.container {
- border-radius: 4px;
- display: inline-block;
- margin: 0 auto;
- padding: 20px 30px;
- text-align: center;
-}
-
-.content {
- @include tc-heading-xl;
-
- color: $tc-yellow-110;
- margin: 10px 20px 0;
- white-space: nowrap;
-}
-
-.title {
- @include tc-body-md;
-
- border-bottom: 1px solid $tc-gray-20;
- color: $tc-gray-40;
- overflow: hidden;
- padding-bottom: 10px;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.container:hover {
- background: $tc-gray-neutral-light;
- cursor: pointer;
-
- .title {
- border-color: $tc-black;
- color: $tc-black;
- }
-}
-
-.hidden {
- background: radial-gradient($tc-yellow, $tc-yellow-30 50%, $tc-yellow-10 70%, transparent 90%);
- border-radius: 100px;
- color: $tc-gold-110;
- display: inline-block;
- font-size: 20px;
- padding: 0 40px;
- vertical-align: top;
-}
-
-.value {
- filter: blur(8px);
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/Earnings/index.jsx b/src/shared/components/Dashboard/MemberMetrics/Earnings/index.jsx
deleted file mode 100644
index c68efb0fda..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Earnings/index.jsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import _ from 'lodash';
-import Carousel from 'components/XCarousel';
-import PT from 'prop-types';
-import React from 'react';
-
-import { config, Link } from 'topcoder-react-utils';
-
-import Dial from './Dial';
-
-import './style.scss';
-
-const PACTS_FULL_URL = `${config.URL.COMMUNITY}/PactsMemberServlet?module=PaymentHistory&full_list=true`;
-const PACTS_OWED_URL = `${config.URL.COMMUNITY}/PactsMemberServlet?module=PaymentHistory&full_list=false`;
-
-export default function Earnings({ finances, showEarnings }) {
- if (!showEarnings) return null;
-
- const map = {};
- finances.forEach((x) => { map[x.status] = x; });
-
- const owed = _.get(map.OWED, 'amount', 0);
- const paid = _.get(map.PAID, 'amount', 0);
- const total = owed + paid;
-
- if (!total) {
- return (
-
-
- Start competing today
-
-
- to gain experience and win prize money!
-
- );
- }
-
- if (owed && paid) {
- return (
-
-
-
-
-
-
-
- );
- }
-
- return (
-
-
-
- );
-}
-
-Earnings.propTypes = {
- finances: PT.arrayOf(PT.shape({
- amount: PT.number.isRequired,
- status: PT.string.isRequired,
- })).isRequired,
- showEarnings: PT.bool.isRequired,
-};
diff --git a/src/shared/components/Dashboard/MemberMetrics/Earnings/style.scss b/src/shared/components/Dashboard/MemberMetrics/Earnings/style.scss
deleted file mode 100644
index 3333c9efe6..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Earnings/style.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- @include tc-body-lg;
-
- padding: 10px 0;
-}
-
-.link,
-.link:active,
-.link:visited {
- color: $tc-dark-blue-110;
-}
-
-.link:hover {
- color: $tc-dark-blue-70;
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/Records/Dial/index.jsx b/src/shared/components/Dashboard/MemberMetrics/Records/Dial/index.jsx
deleted file mode 100644
index 294994a23e..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Records/Dial/index.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * The Dial component shows a single competition track statistic.
- */
-
-import _ from 'lodash';
-import PT from 'prop-types';
-import React from 'react';
-
-import { getRatingLevel } from 'utils/tc';
-
-import './style.scss';
-
-export default function Dial({
- handle,
- track,
- subTrack,
- metric,
- value,
-}) {
- const title = _.startCase(subTrack);
-
- let ratingType;
- if (metric === 'Rating') ratingType = `rating-${getRatingLevel(value)}`;
- else {
- switch (track) {
- case 'DATA_SCIENCE': ratingType = 'ratingInDataScience'; break;
- case 'DESIGN': ratingType = 'ratingInDesign'; break;
- case 'DEVELOP': ratingType = 'ratingInDevelopment'; break;
- default: ratingType = 'rating';
- }
- }
-
- return (
-
-
- {_.startCase(title)}
-
-
- {value.toLocaleString()}
-
-
- {metric}
-
-
- );
-}
-
-Dial.propTypes = {
- handle: PT.string.isRequired,
- track: PT.string.isRequired,
- subTrack: PT.string.isRequired,
- metric: PT.string.isRequired,
- value: PT.number.isRequired,
-};
diff --git a/src/shared/components/Dashboard/MemberMetrics/Records/Dial/style.scss b/src/shared/components/Dashboard/MemberMetrics/Records/Dial/style.scss
deleted file mode 100644
index d3f6e6a080..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Records/Dial/style.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@import '~styles/mixins';
-
-.container {
- border-radius: 4px;
- display: inline-block;
- padding: 20px 30px;
- text-align: center;
-}
-
-.label {
- @include tc-label-sm;
-
- color: $tc-gray-40;
-}
-
-@mixin rating($color) {
- @include tc-heading-xl;
-
- color: $color;
- margin: 15px 10px 5px;
-}
-
-.rating { @include rating($tc-black); }
-.rating-1 { @include rating($tc-level-1); }
-.rating-2 { @include rating($tc-level-2); }
-.rating-3 { @include rating($tc-level-3); }
-.rating-4 { @include rating($tc-level-4); }
-.rating-5 { @include rating($tc-level-5); }
-.ratingInDataScience { @include rating($tc-orange); }
-.ratingInDesign { @include rating($tc-light-blue); }
-.ratingInDevelopment { @include rating($tc-green); }
-
-.title {
- @include tc-label-sm;
-
- border-bottom: 1px solid $tc-gray-20;
- color: $tc-gray-40;
- overflow: hidden;
- padding: 0 10px 10px;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.container:hover {
- background: $tc-gray-neutral-light;
-
- .label,
- .title {
- border-color: $tc-black;
- color: $tc-black;
- }
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/Records/index.jsx b/src/shared/components/Dashboard/MemberMetrics/Records/index.jsx
deleted file mode 100644
index 9163c6719a..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Records/index.jsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import _ from 'lodash';
-import Carousel from 'components/XCarousel';
-import moment from 'moment';
-import PT from 'prop-types';
-import React from 'react';
-
-import Dial from './Dial';
-
-import './style.scss';
-
-/**
- * Transforms stats object to a more convenient array representation.
- * @param {Object} stats
- * @return {Array}
- */
-function transformStats(stats) {
- const res = [];
-
- const push = (track, subTrack, data) => {
- if (!_.isPlainObject(data)) return;
- if (data.rank && data.rank.rating) {
- res.push({
- track,
- subTrack,
- metric: 'Rating',
- mostRecentSubmission: data.mostRecentSubmission,
- value: data.rank.rating,
- });
- } else if (data.wins) {
- res.push({
- track,
- subTrack,
- metric: 'Victories',
- mostRecentSubmission: data.mostRecentSubmission,
- value: data.wins,
- });
- }
- };
-
- let s = stats.COPILOT;
- if (s) {
- s = (s.contests || 0) - (s.failures || 0);
- if (s) {
- res.push({
- track: 'COPILOT',
- subTrack: 'COPILOT',
- metric: 'Successful Challenges',
- value: s,
- });
- }
- }
-
- s = stats.DATA_SCIENCE;
- if (s) _.forIn(s, (x, key) => push('DATA_SCIENCE', key, x));
-
- s = _.get(stats.DESIGN, 'subTracks');
- if (s) s.forEach(x => push('DESIGN', x.name, x));
-
- s = _.get(stats.DEVELOP, 'subTracks');
- if (s) s.forEach(x => push('DEVELOP', x.name, x));
-
- return res.sort((a, b) => moment(b.mostRecentSubmission).diff(a.mostRecentSubmission));
-}
-
-export default function Records({ stats }) {
- return (
-
-
- {
- transformStats(stats || {}).map(item => (
-
- ))
- }
-
-
- );
-}
-
-Records.propTypes = {
- stats: PT.shape().isRequired,
-};
diff --git a/src/shared/components/Dashboard/MemberMetrics/Records/style.scss b/src/shared/components/Dashboard/MemberMetrics/Records/style.scss
deleted file mode 100644
index 67f6d2aa7c..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/Records/style.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.container {
- padding: 0 30px;
-}
diff --git a/src/shared/components/Dashboard/MemberMetrics/index.jsx b/src/shared/components/Dashboard/MemberMetrics/index.jsx
deleted file mode 100644
index 05bc59e5bc..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/index.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import LoadingIndicator from 'components/LoadingIndicator';
-import PT from 'prop-types';
-import React from 'react';
-import Switch from 'components/SwitchWithLabel';
-
-import Earnings from './Earnings';
-import Records from './Records';
-
-import style from './style.scss';
-
-export default function Finances({
- finances,
- financesLoading,
- showEarnings,
- stats,
- statsLoading,
- switchShowEarnings,
-}) {
- return (
-
-
- My Earnings and Stats
-
- {
- financesLoading || statsLoading ? (
-
-
-
- ) : (
-
-
-
-
- )
- }
-
-
- );
-}
-
-Finances.propTypes = {
- finances: PT.arrayOf(PT.object).isRequired,
- financesLoading: PT.bool.isRequired,
- showEarnings: PT.bool.isRequired,
- stats: PT.shape().isRequired,
- statsLoading: PT.bool.isRequired,
- switchShowEarnings: PT.func.isRequired,
-};
diff --git a/src/shared/components/Dashboard/MemberMetrics/style.scss b/src/shared/components/Dashboard/MemberMetrics/style.scss
deleted file mode 100644
index 0cbd581d20..0000000000
--- a/src/shared/components/Dashboard/MemberMetrics/style.scss
+++ /dev/null
@@ -1,57 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- border-bottom: 1px solid $tc-gray-neutral-dark;
- padding: 30px 0;
- position: relative;
- text-align: center;
-}
-
-.link,
-.link:active,
-.link:visited {
- color: $tc-dark-blue-110;
-}
-
-.link:hover {
- color: $tc-dark-blue-70;
-}
-
-.loading {
- margin: 30px 0;
-}
-
-.title {
- @include tc-heading-md;
-
- padding: 0 30px;
- text-transform: uppercase;
-}
-
-.totalNumber {
- @include tc-heading-xl;
-
- border-radius: 4px;
- cursor: pointer;
- display: inline-block;
- margin: 10px 0 0;
- padding: 20px 30px;
-
- &,
- &:active,
- &:visited {
- color: $tc-yellow-110;
- }
-
- &:hover {
- background: $tc-gray-neutral-light;
- }
-}
-
-.showEarningsSwitch {
- @include tc-label-sm;
-
- position: absolute;
- right: 25px;
- top: 10px;
-}
diff --git a/src/shared/components/Dashboard/Program/IosCard.jsx b/src/shared/components/Dashboard/Program/IosCard.jsx
deleted file mode 100644
index f86f1989f9..0000000000
--- a/src/shared/components/Dashboard/Program/IosCard.jsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import React from 'react';
-import PT from 'prop-types';
-
-import { stripUnderscore } from 'utils/tc';
-import { config } from 'topcoder-react-utils';
-
-import './IosCard.scss';
-
-const IosCard = (props) => {
- const { challenge } = props;
- const { legacy } = challenge;
- const { track, forumId } = legacy;
- return (
-
-
-
-
-
- {challenge.userCurrentPhase}
-
- {
- challenge.userCurrentPhaseEndTime
- && (
-
-
- Ends In
-
-
- {challenge.userCurrentPhaseEndTime[0]}
-
-
- {challenge.userCurrentPhaseEndTime[1]}
-
-
- )
- }
- {
- !challenge.userCurrentPhaseEndTime
- && (
-
- This challenge is currently paused.
-
- )
- }
- {
- challenge.reviewType === 'PEER'
- && (
-
- Peer Review Challenge
-
- )
- }
- {
- challenge.reviewType !== 'PEER'
- && (
-
- {`$${(challenge.totalPrize || 0).toLocaleString()}`}
-
- )
- }
-
- {challenge.tags}
-
-
-
- );
-};
-
-IosCard.propTypes = {
- challenge: PT.shape().isRequired,
-};
-
-export default IosCard;
diff --git a/src/shared/components/Dashboard/Program/IosCard.scss b/src/shared/components/Dashboard/Program/IosCard.scss
deleted file mode 100644
index fb9dd2d6b4..0000000000
--- a/src/shared/components/Dashboard/Program/IosCard.scss
+++ /dev/null
@@ -1,258 +0,0 @@
-@import '~styles/mixins';
-
-// Default Challenge Tile Stylings
-.challenge.tile-view {
- position: relative;
- width: 270px;
- height: 370px;
- border: 1px solid #dcdcdc;
- border-radius: 4px;
- background-color: $tc-white;
-
- .challenge-track {
- width: 5px;
- height: 91px;
- position: absolute;
- top: -1px;
- left: -1px;
- border-top-left-radius: 4px;
- }
-
- .challenge-calendar {
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 75px;
- height: 63px;
- margin-top: 16px;
- background-image: url(assets/images/dashboard/ico-calendar.svg);
-
- > p {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- }
-
- .starts-in {
- margin-top: 3px;
- font-size: 10px;
- line-height: 13px;
- text-transform: uppercase;
- color: #7f7f7f;
- }
-
- .time-remaining {
- margin-top: 5px;
- font-size: 24px;
- color: #3d3d3d;
- }
-
- .unit-of-time {
- margin-top: 1px;
- font-size: 10px;
- text-transform: lowercase;
- color: #7f7f7f;
- }
- }
-
- header {
- height: 91px;
- border-bottom: 1px solid #f0f0f0;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
-
- a.name {
- display: block;
- padding: 15px 20px 0;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 14px;
- line-height: 20px;
- color: #3d3d3d;
- text-decoration: none;
-
- @include ellipsis;
-
- text-transform: uppercase;
- }
-
- p.subtrack-color {
- padding: 0 20px;
- margin-top: 5px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- line-height: 14px;
- text-transform: uppercase;
- }
-
- .challenge-links {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- border-bottom-right-radius: 3px;
- border-bottom-left-radius: 3px;
- padding: 0 20px;
- margin-bottom: 10px;
-
- a {
- text-decoration: none;
-
- p {
- color: #a3a3ae;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- line-height: 14px;
- text-transform: uppercase;
- }
- }
-
- .stats,
- .registrants,
- .submissions,
- .forum {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
-
- .registrants,
- .submissions,
- .form {
- cursor: pointer;
- }
-
- .registrants {
- margin-right: 15px;
- }
- }
- }
-
- .challenge-details {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .currentPhase {
- margin-top: 40px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 300;
- font-size: 18px;
- line-height: 23px;
- text-transform: uppercase;
- color: #3d3d3d;
- }
- }
-
- .prize-money {
- margin-top: 32px;
- margin-bottom: 10px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 300;
- font-size: 20px;
- line-height: 24px;
- }
-
- .technologies {
- height: 36px;
- margin-bottom: 33px;
- padding: 0 20px;
- font-family: 'Merriweather Sans', Arial, Helvetica, sans-serif;
- font-size: 13px;
- line-height: 18px;
- }
-
- .stalled-challenge {
- margin-top: 31px;
- margin-bottom: 12px;
- height: 36px;
- padding: 0 20px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- line-height: 18px;
- text-transform: uppercase;
- text-align: center;
- color: #a3a3ae;
- }
-
- .phase-action {
- margin-bottom: 25px;
- }
-
- .roles {
- min-height: 36px;
- margin-bottom: 25px;
- color: #3d3d3d;
- font-family: 'Merriweather Sans', Arial, Helvetica, sans-serif;
- font-size: 13px;
- line-height: 18px;
- }
-
- div[class$="-icon"] {
- margin-right: 3px;
- }
-
- .registrants-icon {
- width: 15px;
- height: 16px;
- background-image: url(assets/images/dashboard/ico-users.svg);
- background-size: 15px 16px;
- }
-
- .submissions-icon {
- width: 22px;
- height: 14px;
- background-image: url(assets/images/dashboard/ico-submissions.svg);
- }
-
- .forum-icon {
- width: 20px;
- height: 17px;
- background-image: url(assets/images/dashboard/ico-posts.svg);
- }
-
- // Dynamic colors based on track
- &.DESIGN {
- .challenge-track {
- background-color: #21b2f1;
- }
-
- header p.subtrack-color {
- color: #21b2f1;
- }
- }
-
- &.DEVELOP {
- .challenge-track {
- background-color: #05c14f;
- }
-
- header p.subtrack-color {
- color: #05c14f;
- }
- }
-
- &.DATA_SCIENCE {
- .challenge-track {
- background-color: #fc9a00;
- }
-
- header p.subtrack-color {
- color: #fc9a00;
- }
- }
-
- &.COPILOT {
- .challenge-track {
- background-color: #7d939e;
- }
-
- header p.subtrack-color {
- color: #7d939e;
- }
- }
-}
diff --git a/src/shared/components/Dashboard/Program/index.jsx b/src/shared/components/Dashboard/Program/index.jsx
deleted file mode 100644
index 4466bca225..0000000000
--- a/src/shared/components/Dashboard/Program/index.jsx
+++ /dev/null
@@ -1,109 +0,0 @@
-/* eslint jsx-a11y/no-static-element-interactions:0 */
-
-import PT from 'prop-types';
-import React from 'react';
-import { config } from 'topcoder-react-utils';
-
-import IosCard from './IosCard';
-import MemberIcon from '../../../../assets/images/Member-06.svg';
-import './styles.scss';
-
-const Program = (props) => {
- const { challenges, iosRegistered, registerIos } = props;
- return (
-
- {
- iosRegistered
- && (
-
-
-
- iOS
-
- {' '}
- Community
-
-
- )
- }
-
- {
- !iosRegistered
- && (
-
-
-
- iOS Community
-
-
-
- Earn iOS topcoder badges and exclusive access to iOS challenges,
- prizes and special community-related events.
-
-
-
-
- )
- }
- {
- iosRegistered
- && (
-
-
-
- {
- challenges.map(challenge => (
-
-
-
- ))
- }
-
-
- )
- }
-
-
- );
-};
-
-Program.propTypes = {
- challenges: PT.arrayOf(PT.shape()).isRequired,
- iosRegistered: PT.bool.isRequired,
- registerIos: PT.func.isRequired,
-};
-
-export default Program;
diff --git a/src/shared/components/Dashboard/Program/styles.scss b/src/shared/components/Dashboard/Program/styles.scss
deleted file mode 100644
index ff93b91fc7..0000000000
--- a/src/shared/components/Dashboard/Program/styles.scss
+++ /dev/null
@@ -1,278 +0,0 @@
-@import '~styles/mixins';
-
-.empty-state-placeholder {
- display: flex;
- flex-direction: column;
- align-items: center;
- color: #3d3d3d;
- padding: 20px 10px;
-
- @media only screen and (min-width: 768px) {
- padding: 30px 20px;
- }
-
- .title {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 24px;
- line-height: 29px;
- text-align: center;
- text-transform: uppercase;
- }
-
- .content {
- margin-top: 20px;
-
- @media only screen and (min-width: 768px) {
- margin-top: 30px;
- }
- }
-
- .description {
- max-width: 650px;
- margin-top: 20px;
- font-family: 'Merriweather Sans', Arial, Helvetica, sans-serif;
- font-weight: 400;
- font-size: 15px;
- line-height: 24px;
- text-align: center;
-
- @media only screen and (min-width: 768px) {
- margin-top: 30px;
- }
-
- @media only screen and (min-width: 900px) {
- max-width: 856px;
- }
- }
-
- .help-links {
- text-align: center;
- display: flex;
- flex-direction: column;
- margin-top: 20px;
-
- @media only screen and (min-width: 768px) {
- margin-top: 30px;
- }
-
- .help-link {
- &:not(:first-child) {
- margin-top: 30px;
- }
- }
-
- a {
- text-transform: uppercase;
- display: block;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
-
- // used more generic css class (secondary-cta)
- // remove learn-more class once we are sure that it is not being used in any empty state
- &.secondary-cta,
- &.learn-more {
- font-size: 12px;
- line-height: 12px;
- color: #a3a3ae;
- }
- }
- }
-}
-
-.empty-state-placeholder.sky .title {
- color: $tc-white;
-}
-
-.programs {
- .section-loading {
- min-height: 200px;
- }
-
- h1.section-title {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 24px;
- line-height: 29px;
- color: #3d3d3d;
- text-align: center;
- text-transform: uppercase;
- padding-top: 30px;
- margin-bottom: 30px;
-
- span {
- text-transform: none;
- }
- }
-
- .unregistered {
- .empty-state-placeholder {
- .title {
- text-transform: none;
- }
- }
- }
-
- .registered {
- padding: 20px 15px 5px;
- border-top: 1px solid #f0f0f0;
- border-bottom: 1px solid #f0f0f0;
- background-color: #fcfcfc;
-
- .badge-and-challenges {
- white-space: nowrap;
- overflow-y: hidden;
- overflow-x: scroll;
-
- @media only screen and (min-width: 768px) {
- display: flex;
- flex-flow: row wrap;
- margin: 0 auto;
- overflow: visible;
- white-space: normal;
- width: 555px;
- }
-
- @media only screen and (min-width: 870px) {
- width: 840px;
- }
-
- @media only screen and (min-width: 1155px) {
- width: 1125px;
- }
- }
-
- .registered-badge {
- display: inline;
- vertical-align: top;
- height: 370px;
- min-width: 270px;
- border: 1px solid #dcdcdc;
- border-radius: 4px;
- background-color: $tc-white;
-
- @media only screen and (max-width: 767px) {
- display: inline-block;
- }
-
- .flex-wrapper {
- display: flex;
- flex-direction: column;
- align-items: center;
- }
-
- p {
- margin-top: 60px;
- margin-bottom: 37px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 300;
- font-size: 20px;
- color: #3d3d3d;
- }
-
- .badge-timeline {
- width: 120px;
- }
-
- a {
- display: block;
- margin-top: 37px;
- text-decoration: none;
- }
- }
-
- .ios-card {
- display: inline;
- margin-bottom: 15px;
-
- @media only screen and (max-width: 767px) {
- display: inline-block;
- margin-left: 15px;
-
- &:first-child {
- margin-left: 0;
- }
- }
-
- @media only screen and (min-width: 768px) {
- &:nth-child(2n) {
- margin-left: 15px;
- }
- }
-
- @media only screen and (min-width: 870px) {
- margin-left: 15px;
-
- &:nth-child(4n) {
- margin-left: 0;
- }
- }
-
- @media only screen and (min-width: 1155px) {
- &:nth-child(4n) {
- margin-left: 15px;
- }
- }
- }
- }
-}
-
-.empty-state-placeholder.sky {
- background-image: url(assets/images/pattern-ios-challenges.png);
- background-repeat: repeat;
-
- .description {
- color: $tc-white;
- }
-
- .help-links {
- .help-link {
- .learn-more {
- color: #f6f6f6;
- }
- }
- }
-}
-
-a.tc-btn {
- height: 40px;
- padding: 0 15px;
- line-height: 40px;
- border-radius: 4px;
- border: 1px solid #0096ff;
- background-color: #0096ff;
- background-image: none;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- font-style: normal;
- color: $tc-white;
- text-transform: uppercase;
- outline: none;
- text-shadow: none;
-
- &:focus {
- border: 1px solid #0096ff;
- background-color: #0096ff;
- color: $tc-white;
- }
-
- &:hover {
- background-color: #097dce;
- border-color: #097dce;
- color: $tc-white;
- }
-
- &:active {
- background-color: #097dce;
- background-image: none;
- border-color: #097dce;
- box-shadow: inset 0 1px 1px 0 rgba(0, 0, 0, 0.3);
- line-height: 30px;
- }
-
- &:disabled {
- opacity: 0.3;
- cursor: default;
- }
-}
diff --git a/src/shared/components/Dashboard/SRM/SRMTile.jsx b/src/shared/components/Dashboard/SRM/SRMTile.jsx
deleted file mode 100644
index f1b0afb2a8..0000000000
--- a/src/shared/components/Dashboard/SRM/SRMTile.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import React from 'react';
-import PT from 'prop-types';
-
-import { timeDiff, localTime } from 'utils/tc';
-import { config } from 'topcoder-react-utils';
-
-import './SRMTile.scss';
-
-const SRMTile = (props) => {
- const { srm } = props;
- return (
-
- {
- srm.status === 'FUTURE'
- && (
-
-
-
-
-
- Starts in
- {' '}
- {
-
- {`${timeDiff(srm.rounds[0].registrationStartAt, 'quantity')}
- ${timeDiff(srm.rounds[0].registrationStartAt, 'unit')}`}
-
- }
-
-
-
- {localTime(srm.rounds[0].registrationStartAt, 'DD')}
-
-
- {localTime(srm.rounds[0].registrationStartAt, 'MMM')}
-
-
- {localTime(srm.rounds[0].registrationStartAt, 'hh:mm a')}
-
-
- UTC{localTime(srm.rounds[0].registrationStartAt, 'Z')}
-
-
-
-
- {srm.userStatus === 'registered' && (
-
- Registered
-
- )}
- {
- srm.userStatus !== 'registered'
- && (
-
- )
- }
-
-
- )
- }
-
- );
-};
-
-SRMTile.propTypes = {
- srm: PT.shape().isRequired,
-};
-
-export default SRMTile;
diff --git a/src/shared/components/Dashboard/SRM/SRMTile.scss b/src/shared/components/Dashboard/SRM/SRMTile.scss
deleted file mode 100644
index 63feea7368..0000000000
--- a/src/shared/components/Dashboard/SRM/SRMTile.scss
+++ /dev/null
@@ -1,308 +0,0 @@
-@import '~styles/mixins';
-
-// common styles for both list and tile view
-.srm {
- .noclick {
- cursor: default;
- }
-
- .phase-status {
- .registered {
- position: relative;
- height: 30px;
- line-height: 30px;
- padding-left: 35px;
- padding-right: 20px;
- border: 1px solid #f0f0f0;
- border-radius: 4px;
- background-color: $tc-white;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 400;
- font-size: 12px;
- text-transform: uppercase;
- color: #3d3d3d;
-
- &::before {
- content: '';
- width: 15px;
- height: 15px;
- background: url(assets/images/dashboard/ico-checkmark.svg);
- background-size: 15px 15px;
- position: absolute;
- bottom: 6px;
- left: 14px;
- }
- }
-
- a {
- display: block;
- }
- }
-}
-
-.srm.tile-view {
- position: relative;
- width: 270px;
- height: 321px;
- border: 1px solid #dcdcdc;
- border-radius: 4px;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
- background-color: $tc-white;
-
- &:hover {
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- }
-
- .challenge-track {
- width: 5px;
- height: 52px;
- position: absolute;
- top: -1px;
- left: -1px;
- border-top-left-radius: 4px;
- background-color: #f39426;
- }
-
- header {
- padding: 0 20px;
- height: 52px;
- border-bottom: 1px solid #f0f0f0;
- display: flex;
- flex-flow: column wrap;
-
- a {
- display: block;
- width: 210px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 14px;
- line-height: 49px;
- color: #3d3d3d;
- text-decoration: none;
-
- @include ellipsis;
- }
-
- .ended-on {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- text-transform: uppercase;
- color: #a3a3ae;
- }
- }
-
- .past-srm {
- header {
- a {
- line-height: 29px;
- }
- }
- }
-
- .srm-calendar {
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 76px;
- height: 91px;
- background-image: url(assets/images/dashboard/ico-calendar-detailed.svg);
-
- span {
- display: block;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- color: #b7b7b7;
-
- &.day {
- margin-top: 2px;
- font-size: 24px;
- color: #3d3d3d;
- }
-
- &.month,
- &.time-zone {
- letter-spacing: 1.6px;
- font-size: 10px;
- }
-
- &.month {
- margin-top: 3px;
- margin-left: 3px;
- }
-
- &.time-zone {
- margin-top: 3px;
- margin-left: 2px;
- }
-
- &.hour {
- margin-top: 18px;
- margin-left: 3px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 400;
- font-size: 12px;
- color: #3d3d3d;
- }
- }
- }
-
- .srm-details {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .starts-in,
- .ended-on {
- margin-top: 40px;
- margin-bottom: 20px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 300;
- font-size: 20px;
- line-height: 24px;
-
- span {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- }
- }
- }
-
- .member-stats {
- display: flex;
- flex-flow: column wrap;
- align-items: center;
-
- .points {
- margin-top: 40px;
- margin-bottom: 20px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 300;
- font-size: 20px;
- line-height: 24px;
- display: flex;
- flex-flow: column wrap;
- align-items: center;
-
- span {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- height: 24px;// matches the line-height of the section
- }
- }
-
- .ranks {
- display: flex;
- flex-direction: row;
- justify-content: center;
- margin-bottom: 10px;
-
- .division {
- width: 84px;
- height: 84px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- text-align: center;
- background-color: #f6f6f6;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- color: #3d3d3d;
-
- p:last-child {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 9px;
- color: #a3a3ae;
- text-transform: uppercase;
- display: flex;
- flex-flow: column wrap;
- }
- }
-
- .room {
- width: 84px;
- height: 84px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- text-align: center;
- background-color: #f6f6f6;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- color: #3d3d3d;
- margin-left: 15px;
-
- p:last-child {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 9px;
- color: #a3a3ae;
- text-transform: uppercase;
- display: flex;
- flex-flow: column wrap;
- }
- }
- }
-
- .placement {
- color: #a3a3ae;
- text-transform: uppercase;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 400;
- font-size: 12px;
- margin-bottom: 60px;
- }
- }
-
- .phase-status {
- display: flex;
- flex-flow: row wrap;
- justify-content: center;
- margin-top: 22px;
- margin-bottom: 40px;
- }
-}
-
-a.tc-btn {
- height: 40px;
- padding: 0 15px;
- line-height: 40px;
- border-radius: 4px;
- border: 1px solid #0096ff;
- background-color: #0096ff;
- background-image: none;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- font-style: normal;
- color: $tc-white;
- text-transform: uppercase;
- outline: none;
- text-shadow: none;
-
- &:focus {
- border: 1px solid #0096ff;
- background-color: #0096ff;
- color: $tc-white;
- }
-
- &:hover {
- background-color: #097dce;
- border-color: #097dce;
- color: $tc-white;
- }
-
- &:active {
- background-color: #097dce;
- background-image: none;
- border-color: #097dce;
- box-shadow: inset 0 1px 1px 0 rgba(0, 0, 0, 0.3);
- line-height: 30px;
- }
-
- &:disabled {
- opacity: 0.3;
- cursor: default;
- }
-}
diff --git a/src/shared/components/Dashboard/SRM/index.jsx b/src/shared/components/Dashboard/SRM/index.jsx
deleted file mode 100644
index d3460230ef..0000000000
--- a/src/shared/components/Dashboard/SRM/index.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from 'react';
-import PT from 'prop-types';
-
-import { config } from 'topcoder-react-utils';
-
-import SRMTile from './SRMTile';
-import './styles.scss';
-
-const SRM = (props) => {
- const { srms } = props;
- return (
-
-
-
- Single Round Matches
-
-
-
-
- {
- srms.map(srm => (
-
-
-
- ))
- }
-
-
-
-
-
- );
-};
-
-SRM.propTypes = {
- srms: PT.arrayOf(PT.shape()),
-};
-
-SRM.defaultProps = {
- srms: [],
-};
-
-export default SRM;
diff --git a/src/shared/components/Dashboard/SRM/styles.scss b/src/shared/components/Dashboard/SRM/styles.scss
deleted file mode 100644
index 708f29e72c..0000000000
--- a/src/shared/components/Dashboard/SRM/styles.scss
+++ /dev/null
@@ -1,210 +0,0 @@
-@import '~styles/mixins';
-
-.srms {
- min-height: 50px;
- display: flex;
- flex-direction: column;
-
- > header {
- position: relative;
- margin-bottom: 30px;
-
- a.viewSRMs {
- position: absolute;
- top: 3px;
- right: 10px;
- text-decoration: underline;
- }
- }
-
- .section-title {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 24px;
- line-height: 29px;
- color: #3d3d3d;
- text-align: center;
- text-transform: uppercase;
- padding-top: 30px;
- }
-
- .srms-links {
- width: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: $tc-white;
- min-height: 72px;
- text-transform: uppercase;
-
- @media only screen and (min-width: 900px) {
- padding: 30px 0;
- }
-
- a {
- color: #0096ff;
- font-size: 12px;
- }
-
- a:not(:first-child) {
- margin-left: 30px;
- }
- }
-
- section {
- padding: 20px 15px 5px;
- border-top: 1px solid #f0f0f0;
- border-bottom: 1px solid #f0f0f0;
- background-color: #fcfcfc;
-
- .srm-tiles {
- white-space: nowrap;
- overflow-y: hidden;
- overflow-x: scroll;
-
- @media only screen and (min-width: 768px) {
- display: flex;
- flex-flow: row wrap;
- margin: 0 auto;
- overflow: visible;
- white-space: normal;
- width: 555px;
- }
-
- @media only screen and (min-width: 870px) {
- width: 840px;
- }
-
- @media only screen and (min-width: 1155px) {
- width: 1125px;
- }
-
- .srm-tile {
- display: inline;
- margin-bottom: 15px;
-
- @media only screen and (max-width: 767px) {
- display: inline-block;
- margin-left: 15px;
-
- &:first-child {
- margin-left: 0;
- }
- }
-
- @media only screen and (min-width: 768px) {
- &:nth-child(2n + 1) {
- margin-right: 15px;
- }
- }
-
- @media only screen and (min-width: 870px) {
- margin-right: 15px;
-
- &:nth-child(3n) {
- margin-right: 0;
- }
- }
-
- @media only screen and (min-width: 1155px) {
- &:nth-child(3n) {
- margin-right: 15px;
- }
-
- &:nth-child(4n) {
- margin-right: 0;
- }
- }
- }
-
- .srm-links-card {
- display: inline;
- width: 270px;
- height: 321px;
- vertical-align: top;
- margin-bottom: 15px;
- padding: 0 21px;
- border: 1px solid #dcdcdc;
- background-color: $tc-white;
- border-radius: 4px;
-
- @media only screen and (max-width: 767px) {
- display: inline-block;
- margin-left: 15px;
- }
-
- a {
- width: 196px;
- text-align: center;
- }
- }
-
- .flex-wrapper {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- h2 {
- margin-top: 60px;
- margin-bottom: 55px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 300;
- font-size: 24px;
- line-height: 1;
- text-align: center;
- color: #3d3d3d;
- text-transform: uppercase;
- white-space: normal;
- max-width: 100%;
- }
-
- a {
- margin-bottom: 21px;
- }
- }
- }
- }
-}
-
-a.tc-btn {
- height: 40px;
- padding: 0 15px;
- line-height: 40px;
- border-radius: 4px;
- border: 1px solid #0096ff;
- background-color: #0096ff;
- background-image: none;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 12px;
- font-style: normal;
- color: $tc-white;
- text-transform: uppercase;
- outline: none;
- text-shadow: none;
-
- &:focus {
- border: 1px solid #0096ff;
- background-color: #0096ff;
- color: $tc-white;
- }
-
- &:hover {
- background-color: #097dce;
- border-color: #097dce;
- color: $tc-white;
- }
-
- &:active {
- background-color: #097dce;
- background-image: none;
- border-color: #097dce;
- box-shadow: inset 0 1px 1px 0 rgba(0, 0, 0, 0.3);
- line-height: 30px;
- }
-
- &:disabled {
- opacity: 0.3;
- cursor: default;
- }
-}
diff --git a/src/shared/components/Dashboard/TCOLeaderboards/index.jsx b/src/shared/components/Dashboard/TCOLeaderboards/index.jsx
new file mode 100644
index 0000000000..3367c55141
--- /dev/null
+++ b/src/shared/components/Dashboard/TCOLeaderboards/index.jsx
@@ -0,0 +1,109 @@
+/**
+ * TCOLeaderboards component
+ */
+
+import _ from 'lodash';
+import LoadingIndicator from 'components/LoadingIndicator';
+import PT from 'prop-types';
+import React from 'react';
+import ChevronDown from 'assets/images/chevron-down-white.svg';
+import DefaultAvatar from 'assets/images/default-avatar-photo-blue.svg';
+import { themr } from 'react-css-super-themr';
+import { Avatar } from 'topcoder-react-utils';
+import Select from 'components/Select';
+import styles from './styles.scss';
+
+export default class TCOLeaderboards extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ selectedIndex: 0,
+ };
+ this.handleTrackChange = this.handleTrackChange.bind(this);
+ }
+
+ handleTrackChange({ value }) {
+ this.setState({ selectedIndex: value });
+ }
+
+ render() {
+ const {
+ leaderboards,
+ leaderboardsLoading,
+ itemCount,
+ } = this.props;
+ const { selectedIndex } = this.state;
+ const AvatarComponent = themr('Avatar', styles)(Avatar);
+ const options = leaderboards && leaderboards
+ .map((track, index) => ({
+ value: index,
+ label: track.selectText,
+ selected: index === selectedIndex,
+ }));
+ const data = _.get(leaderboards, [selectedIndex]);
+ const items = (data && !leaderboardsLoading) ? data.leaderboard
+ .slice(0, itemCount).map((row, index) => (
+ // We limit the items to itemCount - no scrolling
+ // eslint-disable-next-line react/no-array-index-key
+
+
{index + 1}
+
+
+
{row['tco_leaderboard.tco_points'].toLocaleString('en-US', { maximumFractionDigits: 2 })}
+
+ )) :
;
+
+ return (
+
+
+
+ {
+ !leaderboardsLoading && (
+
+ )
+ }
+
+ {
+ !leaderboardsLoading
+ && (
+
+
+
+
+ )
+ }
+
+
+ {items}
+
+
+
+ );
+ }
+}
+
+
+TCOLeaderboards.defaultProps = {
+ leaderboards: [],
+ leaderboardsLoading: true,
+};
+
+TCOLeaderboards.propTypes = {
+ leaderboards: PT.arrayOf(PT.object),
+ leaderboardsLoading: PT.bool,
+ itemCount: PT.number.isRequired,
+};
diff --git a/src/shared/components/Dashboard/TCOLeaderboards/styles.scss b/src/shared/components/Dashboard/TCOLeaderboards/styles.scss
new file mode 100644
index 0000000000..396a4cc938
--- /dev/null
+++ b/src/shared/components/Dashboard/TCOLeaderboards/styles.scss
@@ -0,0 +1,199 @@
+@import '~styles/mixins';
+
+.container {
+ background-image: linear-gradient(225deg, #31a0ca 0%, #16679a 100%);
+ border-radius: 8px;
+ width: auto;
+ padding: 12px 0 0;
+ margin-bottom: 20px;
+}
+
+.header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 8px 10px 14px;
+}
+
+.titles {
+ margin-right: 8px;
+ min-height: 33px;
+}
+
+.stageText {
+ @include barlow-semi-bold;
+
+ display: block;
+ font-size: 16px;
+ color: $tc-white;
+ text-transform: uppercase;
+}
+
+.titleText {
+ @include barlow;
+
+ font-size: 12px;
+ margin-top: 8px;
+ line-height: 16px;
+ color: $tc-white;
+ text-decoration: underline;
+
+ &:visited,
+ &:hover {
+ color: $tc-white;
+ }
+
+ &:hover {
+ text-decoration: none;
+ }
+}
+
+.dropdown-wrapper {
+ position: relative;
+ min-width: 108px;
+ width: auto;
+ height: 26px;
+
+ .chevronDown {
+ position: absolute;
+ top: 50%;
+ right: 8px;
+ margin-top: -4px;
+ pointer-events: none;
+ width: 9px;
+ height: 9px;
+ }
+
+ .dropdown {
+ width: 100%;
+ height: 26px;
+
+ :global {
+ .Select-control {
+ background: rgba(0, 0, 0, 0.1) !important;
+ border-radius: 13px !important;
+ border: none !important;
+ height: 26px;
+ width: 100%;
+
+ .Select-value {
+ line-height: 26px;
+ padding-right: 15px;
+ }
+
+ .Select-value-label {
+ @include roboto-regular;
+
+ font-size: 13px;
+ color: $tc-white !important;
+ }
+
+ .Select-arrow {
+ display: none;
+ }
+ }
+
+ .Select-input {
+ height: 26px;
+ background: none !important;
+ }
+
+ .Select-menu-outer,
+ .Select-menu {
+ border-radius: 8px;
+ border: none;
+ width: auto;
+ overflow-x: hidden;
+ min-width: 180px;
+ }
+
+ .Select-menu-outer {
+ @media screen and (max-width: 1786px) {
+ margin-left: -75px;
+ }
+ }
+
+ .Select-menu {
+ padding: 4px 0;
+ }
+
+ .Select-option {
+ @include roboto-regular;
+
+ font-size: 13px;
+ line-height: 26px;
+ margin: 0;
+ padding: 0 15px;
+ color: $tc-gray-90;
+ white-space: nowrap;
+
+ &.is-focused {
+ background-color: #229174;
+ color: $tc-white;
+ }
+
+ &.is-selected:not(.is-focused) {
+ @include roboto-bold;
+
+ background-color: transparent;
+ }
+ }
+ }
+ }
+}
+
+.leaderboard {
+ width: 100%;
+ margin: 0;
+}
+
+.leaderboardRow {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ margin: 12px 0;
+ padding: 0 8px;
+ width: 100%;
+
+ span {
+ @include roboto-regular;
+
+ font-size: 14px;
+ color: $tc-white;
+ margin: 0 6px;
+ text-overflow: ellipsis;
+ }
+
+ > .index {
+ text-align: left;
+ width: 15px;
+ margin-right: 0;
+ }
+
+ > .handle {
+ flex: 1;
+ margin-left: 0;
+
+ a {
+ text-decoration: underline;
+ color: $tc-white;
+ font-family: Roboto, sans-serif;
+ font-size: 14px;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+
+ > .tcoPoints {
+ text-align: right;
+ }
+}
+
+.avatar {
+ height: 30px;
+ width: 30px;
+ border-radius: 15px;
+}
diff --git a/src/shared/components/Dashboard/TCTime/dark.scss b/src/shared/components/Dashboard/TCTime/dark.scss
new file mode 100644
index 0000000000..ed3eedfe4b
--- /dev/null
+++ b/src/shared/components/Dashboard/TCTime/dark.scss
@@ -0,0 +1,32 @@
+@import "~styles/mixins";
+
+.container {
+ display: flex;
+ flex-direction: column;
+ background-color: #363636;
+ border-radius: 8px;
+ padding: 12px;
+ margin-bottom: 20px;
+
+ @media screen and (max-width: 425px) {
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .title {
+ color: #aaa;
+ font-size: 11px;
+ font-family: Roboto, sans-serif;
+ font-weight: bold;
+ text-transform: uppercase;
+ line-height: 16px;
+ }
+
+ .time {
+ color: #fff;
+ font-size: 14px;
+ font-family: Roboto, sans-serif;
+ line-height: 22px;
+ }
+}
diff --git a/src/shared/components/Dashboard/TCTime/index.jsx b/src/shared/components/Dashboard/TCTime/index.jsx
new file mode 100644
index 0000000000..adb3f8bd60
--- /dev/null
+++ b/src/shared/components/Dashboard/TCTime/index.jsx
@@ -0,0 +1,34 @@
+/**
+ * Topcoder Time Component
+ */
+import React, { useState, useEffect } from 'react';
+import moment from 'moment-timezone';
+import darkTheme from './dark.scss';
+
+const THEMES = {
+ dark: darkTheme,
+};
+
+function TopcoderTime() {
+ const theme = THEMES.dark; // for v1 only dark theme
+ let FORMAT = 'MMM Do, HH:mm UTC';
+ const TIMEZONE = 'America/New_York';
+ const now = moment.tz(new Date(), TIMEZONE);
+ FORMAT += now.utcOffset() / 60;
+ const [tcTime, setTCTime] = useState(`${now.format(FORMAT)}`);
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setTCTime(moment.tz(new Date(), TIMEZONE).format(FORMAT));
+ }, 60000);
+ return () => clearInterval(interval);
+ }, []);
+
+ return (
+
+ Topcoder Time
+ {tcTime}
+
+ );
+}
+
+export default TopcoderTime;
diff --git a/src/shared/components/Dashboard/ThriveArticlesFeed/index.jsx b/src/shared/components/Dashboard/ThriveArticlesFeed/index.jsx
new file mode 100644
index 0000000000..89bff6afaf
--- /dev/null
+++ b/src/shared/components/Dashboard/ThriveArticlesFeed/index.jsx
@@ -0,0 +1,57 @@
+/**
+ * Thrive Articles Feed component
+ */
+
+import LoadingIndicator from 'components/LoadingIndicator';
+import PT from 'prop-types';
+import React from 'react';
+import './styles.scss';
+import ThriveArticlesIcon from 'assets/images/thrive-articles.svg';
+
+export default function ThriveArticlesFeed({
+ articles,
+ loading,
+ theme,
+}) {
+ return (
+
+
+
+ {loading ?
+ : articles.map(article => (
+
+ ))}
+
+
+ );
+}
+
+ThriveArticlesFeed.defaultProps = {
+ articles: [],
+ theme: 'light',
+};
+
+ThriveArticlesFeed.propTypes = {
+ articles: PT.arrayOf(PT.shape()),
+ loading: PT.bool.isRequired,
+ theme: PT.oneOf(['dark', 'light']),
+};
diff --git a/src/shared/components/Dashboard/ThriveArticlesFeed/styles.scss b/src/shared/components/Dashboard/ThriveArticlesFeed/styles.scss
new file mode 100644
index 0000000000..7ee6fb6593
--- /dev/null
+++ b/src/shared/components/Dashboard/ThriveArticlesFeed/styles.scss
@@ -0,0 +1,105 @@
+@import "~styles/mixins";
+
+.loading {
+ height: 195px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include xs-to-sm {
+ height: 285px;
+ }
+}
+
+.container {
+ @include roboto-regular;
+
+ color: $tc-gray-90;
+ padding: 13px 12px 3px 12px;
+ border-radius: 8px;
+ margin-bottom: 20px;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-bottom: 10px;
+
+ .title {
+ @include barlow-semi-bold;
+
+ display: flex;
+ font-size: 16px;
+ align-items: center;
+
+ .icon {
+ fill: $tc-gray-90;
+ width: 17px;
+ height: 17px;
+ margin-right: 9px;
+ margin-bottom: 3px;
+ }
+ }
+
+ .allLink {
+ color: $dashboard-teal;
+ font-size: 13px;
+ line-height: 20px;
+
+ &:hover {
+ text-decoration: none !important;
+ }
+
+ span {
+ @include xs-to-sm {
+ display: none;
+ }
+ }
+ }
+}
+
+.articles {
+ width: 100%;
+
+ .row {
+ display: flex;
+ font-size: 14px;
+ line-height: 18px;
+ align-items: flex-start;
+ justify-content: space-between;
+ padding: 10px 0;
+ border-top: 1px solid $tc-gray-05;
+
+ &:first-child {
+ border: none;
+ }
+ }
+}
+
+.light {
+ background-color: $tc-white;
+}
+
+.container.dark {
+ color: $tc-white;
+ background: $dashboard-dark-card-bg;
+
+ .header {
+ .icon {
+ fill: $tc-white;
+ }
+
+ .allLink {
+ color: $dashboard-dark-link;
+ text-decoration: underline;
+ }
+ }
+
+ .articles {
+ .row:not(:first-child) {
+ border-top: 1px solid $dashboard-dark-bg;
+ }
+ }
+}
diff --git a/src/shared/components/Dashboard/index.jsx b/src/shared/components/Dashboard/index.jsx
deleted file mode 100644
index 49d5ce8da1..0000000000
--- a/src/shared/components/Dashboard/index.jsx
+++ /dev/null
@@ -1,136 +0,0 @@
-import Announcement from 'containers/Dashboard/Announcement';
-import PT from 'prop-types';
-import React from 'react';
-
-// import CommunityBlog from './CommunityBlog';
-// import MemberMetrics from './MemberMetrics';
-import CurrentActivity from './CurrentActivity';
-import Header from './Header';
-
-import './style.scss';
-
-export default function Dashboard({
- achievements,
- achievementsLoading,
- announcementPreviewId,
- challengeFilter,
- challenges,
- challengesLoading,
- communities,
- communitiesLoading,
- communityStats,
- // finances,
- // financesLoading,
- selectChallengeDetailsTab,
- setChallengeListingFilter,
- showChallengeFilter,
- // showEarnings,
- showXlBadge,
- srms,
- srmsLoading,
- // stats,
- // statsLoading,
- switchChallengeFilter,
- switchShowChallengeFilter,
- // switchShowEarnings,
- switchTab,
- tab,
- // tcBlogLoading,
- // tcBlogPosts,
- unregisterFromChallenge,
- userGroups,
- xlBadge,
- // errorLoadingRss,
- userResources,
- challengeTypesMap,
-}) {
- return (
-
-
-
-
- {/*
*/}
-
- {/* {!errorLoadingRss && (
-
- )} */}
-
-
- );
-}
-
-Dashboard.defaultProps = {
- announcementPreviewId: '',
- userResources: [],
-};
-
-Dashboard.propTypes = {
- achievements: PT.arrayOf(PT.object).isRequired,
- achievementsLoading: PT.bool.isRequired,
- announcementPreviewId: PT.string,
- challengeFilter: PT.string.isRequired,
- challenges: PT.arrayOf(PT.object).isRequired,
- challengesLoading: PT.bool.isRequired,
- communities: PT.arrayOf(PT.object).isRequired,
- communitiesLoading: PT.bool.isRequired,
- communityStats: PT.shape().isRequired,
- // finances: PT.arrayOf(PT.object).isRequired,
- // financesLoading: PT.bool.isRequired,
- selectChallengeDetailsTab: PT.func.isRequired,
- setChallengeListingFilter: PT.func.isRequired,
- showChallengeFilter: PT.bool.isRequired,
- // showEarnings: PT.bool.isRequired,
- showXlBadge: PT.func.isRequired,
- srms: PT.arrayOf(PT.object).isRequired,
- srmsLoading: PT.bool.isRequired,
- // stats: PT.shape().isRequired,
- // statsLoading: PT.bool.isRequired,
- switchChallengeFilter: PT.func.isRequired,
- switchShowChallengeFilter: PT.func.isRequired,
- // switchShowEarnings: PT.func.isRequired,
- switchTab: PT.func.isRequired,
- tab: PT.string.isRequired,
- // tcBlogLoading: PT.bool.isRequired,
- // tcBlogPosts: PT.arrayOf(PT.object).isRequired,
- unregisterFromChallenge: PT.func.isRequired,
- userGroups: PT.arrayOf(PT.string).isRequired,
- xlBadge: PT.string.isRequired,
- // errorLoadingRss: PT.bool.isRequired,
- userResources: PT.arrayOf(PT.shape()),
- challengeTypesMap: PT.shape().isRequired,
-};
diff --git a/src/shared/components/Dashboard/style.scss b/src/shared/components/Dashboard/style.scss
deleted file mode 100644
index 09b651437d..0000000000
--- a/src/shared/components/Dashboard/style.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- background: $tc-gray-neutral-dark;
- flex: 1;
- padding: 20px 30px;
- width: 100%;
-
- @include xs-to-sm {
- padding: 0;
- }
-}
-
-.page {
- background: $tc-white;
- border-radius: 6px;
- margin: 0 auto;
- max-width: $screen-lg;
-}
-
-.subTitle {
- @include tc-heading-md;
-
- margin: 30px;
- text-align: center;
-}
diff --git a/src/shared/components/Gigs/ReferralCode/index.jsx b/src/shared/components/Gigs/ReferralCode/index.jsx
index 2cd9d593c3..1ed5597794 100644
--- a/src/shared/components/Gigs/ReferralCode/index.jsx
+++ b/src/shared/components/Gigs/ReferralCode/index.jsx
@@ -49,7 +49,7 @@ function ReferralCode(props) {
button: buttonThemes.tc['primary-borderless-sm'],
}}
>
- REFFER A FRIEND
+ REFER A FRIEND
{
loginModalOpen
@@ -98,7 +98,7 @@ function ReferralCode(props) {
)
}
{
- growSurfState.error &&
Ops, we couldn't load your profile. Please try again later or contact support .
+ growSurfState.error &&
Oops, we couldn't load your profile. Please try again later or contact support .
}
)
diff --git a/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/AlmostDone/index.jsx b/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/AlmostDone/index.jsx
index c3324d7262..052b48644f 100644
--- a/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/AlmostDone/index.jsx
+++ b/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/AlmostDone/index.jsx
@@ -39,7 +39,7 @@ const AlmostDone = ({ location }) => {
Back to My Dashboard
diff --git a/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Failed/index.jsx b/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Failed/index.jsx
index b0fa012bcf..027cb52616 100644
--- a/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Failed/index.jsx
+++ b/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Failed/index.jsx
@@ -42,7 +42,7 @@ const Failed = () => (
Back to My Dashboard
diff --git a/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Success/index.jsx b/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Success/index.jsx
index 2e4392bb6c..8afaabdc56 100644
--- a/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Success/index.jsx
+++ b/src/shared/components/Settings/Account/MyAccount/EmailVerifiResult/Success/index.jsx
@@ -26,7 +26,7 @@ const Success = () => (
Back to My Dashboard
diff --git a/src/shared/components/TopcoderHeader/index.jsx b/src/shared/components/TopcoderHeader/index.jsx
index df7b073947..dd8e688283 100644
--- a/src/shared/components/TopcoderHeader/index.jsx
+++ b/src/shared/components/TopcoderHeader/index.jsx
@@ -274,8 +274,8 @@ export default class TopcoderHeader extends React.Component {
items: [{
enforceA: true,
icon:
,
- link: `${BASE_URL}/my-dashboard`,
- title: 'Dashboard',
+ link: `${BASE_URL}/home`,
+ title: 'Home',
}, {
enforceA: true,
icon:
,
diff --git a/src/shared/components/buttons/themed/tc.scss b/src/shared/components/buttons/themed/tc.scss
index 1c0e006caf..55df1ef13f 100644
--- a/src/shared/components/buttons/themed/tc.scss
+++ b/src/shared/components/buttons/themed/tc.scss
@@ -99,6 +99,7 @@
border-radius: 25px !important;
min-height: auto !important;
padding: 0 30px !important;
+ letter-spacing: 0.8px !important;
}
@mixin secondary {
diff --git a/src/shared/components/examples/Announcement/index.jsx b/src/shared/components/examples/Announcement/index.jsx
deleted file mode 100644
index c055d54b84..0000000000
--- a/src/shared/components/examples/Announcement/index.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import Announcement from 'containers/Dashboard/Announcement';
-import PT from 'prop-types';
-import React from 'react';
-
-import './style.scss';
-
-export default function AnnouncementExample({ match }) {
- return (
-
-
- Announcement Preview
-
-
-
- );
-}
-
-AnnouncementExample.propTypes = {
- match: PT.shape({
- params: PT.shape({
- id: PT.string.isRequired,
- }).isRequired,
- }).isRequired,
-};
diff --git a/src/shared/components/examples/Announcement/style.scss b/src/shared/components/examples/Announcement/style.scss
deleted file mode 100644
index 2e9181f6c4..0000000000
--- a/src/shared/components/examples/Announcement/style.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-@import "~styles/mixins";
-
-.container {
- background: white;
- margin: 0 auto;
- max-width: $screen-lg;
- padding: 25px 50px;
-}
diff --git a/src/shared/components/examples/BlogFeed/index.jsx b/src/shared/components/examples/BlogFeed/index.jsx
new file mode 100644
index 0000000000..1aa90714c3
--- /dev/null
+++ b/src/shared/components/examples/BlogFeed/index.jsx
@@ -0,0 +1,35 @@
+import BlogFeedContainer from 'containers/Dashboard/BlogFeed';
+import React from 'react';
+
+import './styles.scss';
+import { PrimaryButton } from 'topcoder-react-ui-kit/src/shared/components/buttons';
+
+export default class BlogFeedExample extends React.Component {
+ constructor() {
+ super();
+ this.state = { theme: 'light' };
+ this.toggleTheme = this.toggleTheme.bind(this);
+ }
+
+ toggleTheme() {
+ const { theme } = this.state;
+ this.setState({ theme: theme === 'light' ? 'dark' : 'light' });
+ }
+
+ render() {
+ const { theme } = this.state;
+
+ return (
+
+
+
+ Blog Feed Preview
+
+
Theme: {theme}
+
Toggle Theme
+
+
+
+ );
+ }
+}
diff --git a/src/shared/components/examples/BlogFeed/styles.scss b/src/shared/components/examples/BlogFeed/styles.scss
new file mode 100644
index 0000000000..a221cbaa79
--- /dev/null
+++ b/src/shared/components/examples/BlogFeed/styles.scss
@@ -0,0 +1,25 @@
+@import "~styles/mixins";
+
+.page {
+ width: 100%;
+ min-height: 100vh;
+}
+
+.container {
+ margin: 0 auto;
+ padding: 58px 50px;
+ max-width: 375px;
+
+ @include xs-to-sm {
+ padding: 25px 15px;
+ }
+}
+
+.light {
+ background: $tc-white;
+}
+
+.dark {
+ background: $dashboard-dark-bg;
+ color: $tc-white;
+}
diff --git a/src/shared/components/examples/ChallengesFeed/index.jsx b/src/shared/components/examples/ChallengesFeed/index.jsx
new file mode 100644
index 0000000000..77171427cb
--- /dev/null
+++ b/src/shared/components/examples/ChallengesFeed/index.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import ChallengesFeed from 'containers/Dashboard/ChallengesFeed';
+import './styles.scss';
+
+import { PrimaryButton } from 'topcoder-react-ui-kit/src/shared/components/buttons';
+
+export default class ChallengesFeedExample extends React.Component {
+ constructor() {
+ super();
+ this.state = { theme: 'light' };
+ this.toggleTheme = this.toggleTheme.bind(this);
+ }
+
+ toggleTheme() {
+ const { theme } = this.state;
+
+ this.setState({ theme: theme === 'light' ? 'dark' : 'light' });
+ }
+
+ render() {
+ const { theme } = this.state;
+
+ return (
+
+
+
Challenges Feed Preview
+
Theme: {theme}
+
Toggle Theme
+
+
+
+ );
+ }
+}
diff --git a/src/shared/components/examples/ChallengesFeed/styles.scss b/src/shared/components/examples/ChallengesFeed/styles.scss
new file mode 100644
index 0000000000..9e57309c8a
--- /dev/null
+++ b/src/shared/components/examples/ChallengesFeed/styles.scss
@@ -0,0 +1,25 @@
+@import '~styles/mixins';
+
+.page {
+ width: 100%;
+ min-height: 100vh;
+}
+
+.container {
+ margin: 0 auto;
+ padding: 25px 50px;
+ max-width: 670px;
+
+ @include xs-to-sm {
+ padding: 25px 15px;
+ }
+}
+
+.light {
+ background: #f4f4ff;
+}
+
+.dark {
+ background: #2a2a2a;
+ color: $tc-white;
+}
diff --git a/src/shared/components/examples/GigsFeed/index.jsx b/src/shared/components/examples/GigsFeed/index.jsx
new file mode 100644
index 0000000000..b54fe8200d
--- /dev/null
+++ b/src/shared/components/examples/GigsFeed/index.jsx
@@ -0,0 +1,35 @@
+import GigsFeed from 'containers/Dashboard/GigsFeed';
+import React from 'react';
+
+import './style.scss';
+import { PrimaryButton } from 'topcoder-react-ui-kit/src/shared/components/buttons';
+
+export default class GigsFeedExample extends React.Component {
+ constructor() {
+ super();
+ this.state = { theme: 'light' };
+ this.toggleTheme = this.toggleTheme.bind(this);
+ }
+
+ toggleTheme() {
+ const { theme } = this.state;
+ this.setState({ theme: theme === 'light' ? 'dark' : 'light' });
+ }
+
+ render() {
+ const { theme } = this.state;
+
+ return (
+
+
+
+ Gigs Feed Preview
+
+
Theme: {theme}
+
Toggle Theme
+
+
+
+ );
+ }
+}
diff --git a/src/shared/components/examples/GigsFeed/style.scss b/src/shared/components/examples/GigsFeed/style.scss
new file mode 100644
index 0000000000..211374bab5
--- /dev/null
+++ b/src/shared/components/examples/GigsFeed/style.scss
@@ -0,0 +1,25 @@
+@import "~styles/mixins";
+
+.page {
+ width: 100%;
+ min-height: 100vh;
+}
+
+.container {
+ margin: 0 auto;
+ padding: 25px 50px;
+ max-width: 670px;
+
+ @include xs-to-sm {
+ padding: 25px 15px;
+ }
+}
+
+.light {
+ background: #f4f4f4;
+}
+
+.dark {
+ background: $dashboard-dark-bg;
+ color: $tc-white;
+}
diff --git a/src/shared/components/examples/TCOLeaderboards/index.jsx b/src/shared/components/examples/TCOLeaderboards/index.jsx
new file mode 100644
index 0000000000..8a798e37f8
--- /dev/null
+++ b/src/shared/components/examples/TCOLeaderboards/index.jsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import TCOLeaderboardsContainer from 'containers/Dashboard/TCOLeaderboards';
+import trackConfig from 'assets/mock-data/leaderboards-config-tco-2021.json';
+import './style.scss';
+
+export default function TCOLeaderboardsExample() {
+ return (
+
+
+
+ TCO Leaderboards Preview
+
+
+
+
+ );
+}
diff --git a/src/shared/components/examples/TCOLeaderboards/style.scss b/src/shared/components/examples/TCOLeaderboards/style.scss
new file mode 100644
index 0000000000..144957b44c
--- /dev/null
+++ b/src/shared/components/examples/TCOLeaderboards/style.scss
@@ -0,0 +1,31 @@
+@import "~styles/mixins";
+
+.container {
+ background: $tc-white;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ padding: 50px;
+ align-items: flex-start;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ max-width: 765px;
+ width: 100%;
+ align-items: center;
+ margin-bottom: 50px;
+}
+
+@include xs-to-sm {
+ .container {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .header {
+ flex-direction: column;
+ justify-content: center;
+ }
+}
diff --git a/src/shared/components/examples/ThriveArticlesFeed/index.jsx b/src/shared/components/examples/ThriveArticlesFeed/index.jsx
new file mode 100644
index 0000000000..c6b9739c14
--- /dev/null
+++ b/src/shared/components/examples/ThriveArticlesFeed/index.jsx
@@ -0,0 +1,35 @@
+import ThriveArticlesFeedContainer from 'containers/Dashboard/ThriveArticlesFeed';
+import React from 'react';
+
+import './style.scss';
+import { PrimaryButton } from 'topcoder-react-ui-kit/src/shared/components/buttons';
+
+export default class ThriveArticlesFeedExample extends React.Component {
+ constructor() {
+ super();
+ this.state = { theme: 'light' };
+ this.toggleTheme = this.toggleTheme.bind(this);
+ }
+
+ toggleTheme() {
+ const { theme } = this.state;
+ this.setState({ theme: theme === 'light' ? 'dark' : 'light' });
+ }
+
+ render() {
+ const { theme } = this.state;
+
+ return (
+
+
+
+ Thrive Articles Feed Preview
+
+
Theme: {theme}
+
Toggle Theme
+
+
+
+ );
+ }
+}
diff --git a/src/shared/components/examples/ThriveArticlesFeed/style.scss b/src/shared/components/examples/ThriveArticlesFeed/style.scss
new file mode 100644
index 0000000000..211374bab5
--- /dev/null
+++ b/src/shared/components/examples/ThriveArticlesFeed/style.scss
@@ -0,0 +1,25 @@
+@import "~styles/mixins";
+
+.page {
+ width: 100%;
+ min-height: 100vh;
+}
+
+.container {
+ margin: 0 auto;
+ padding: 25px 50px;
+ max-width: 670px;
+
+ @include xs-to-sm {
+ padding: 25px 15px;
+ }
+}
+
+.light {
+ background: #f4f4f4;
+}
+
+.dark {
+ background: $dashboard-dark-bg;
+ color: $tc-white;
+}
diff --git a/src/shared/containers/Dashboard/Announcement.jsx b/src/shared/containers/Dashboard/Announcement.jsx
deleted file mode 100644
index a15a7ba6cb..0000000000
--- a/src/shared/containers/Dashboard/Announcement.jsx
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * Container for dashboard announcement.
- */
-
-import _ from 'lodash';
-import Announcement from 'components/Dashboard/Announcement';
-import ContentfulLoader from 'containers/ContentfulLoader';
-import cookies from 'browser-cookies';
-import LoadingIndicator from 'components/LoadingIndicator';
-import moment from 'moment';
-import PT from 'prop-types';
-import React from 'react';
-import uiActions from 'actions/page/dashboard';
-import { connect } from 'react-redux';
-import { isomorphy } from 'topcoder-react-utils';
-
-const COOKIE = 'lastSeenDashboardAnnouncement';
-
-class AnnouncementContainer extends React.Component {
- constructor(props) {
- super(props);
- this.rendered = false;
- this.now = moment().format();
- }
-
- render() {
- const {
- hidePreviewMetaData,
- previewId,
- show,
- switchShowAnnouncement,
- } = this.props;
-
- return (
-
{
- let announcement = previewId || data.entries.matches[0].items[0];
- if (!announcement) return null;
- announcement = data.entries.items[announcement];
- const backgroundAssetId = _.get(announcement.fields.backgroundImage, 'sys.id');
-
- const lastSeen = cookies.get(COOKIE);
- const thisId = announcement.sys.id;
- if (show && !this.rendered && isomorphy.isClientSide()
- && lastSeen === thisId) {
- this.rendered = true;
- switchShowAnnouncement(false);
- }
-
- return (
- (
- {
- if (on) cookies.erase(COOKIE);
- else cookies.set(COOKIE, thisId, { expires: 365 });
- switchShowAnnouncement(on);
- }}
- />
- )}
- renderPlaceholder={LoadingIndicator}
- />
- );
- }}
- renderPlaceholder={LoadingIndicator}
- />
- );
- }
-}
-
-AnnouncementContainer.defaultProps = {
- hidePreviewMetaData: false,
- previewId: '',
-};
-
-AnnouncementContainer.propTypes = {
- hidePreviewMetaData: PT.bool,
- previewId: PT.string,
- show: PT.bool.isRequired,
- switchShowAnnouncement: PT.func.isRequired,
-};
-
-function mapStateToProps(state, props) {
- return {
- hidePreviewMetaData: props.hidePreviewMetaData,
- previewId: props.previewId,
- show: state.page.dashboard.showAnnouncement,
- };
-}
-
-function mapDispatchToProps(dispatch) {
- return {
- switchShowAnnouncement: show => dispatch(uiActions.page.dashboard.showAnnouncement(show)),
- };
-}
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps,
-)(AnnouncementContainer);
diff --git a/src/shared/containers/Dashboard/BlogFeed.jsx b/src/shared/containers/Dashboard/BlogFeed.jsx
new file mode 100644
index 0000000000..62ce6d2dc0
--- /dev/null
+++ b/src/shared/containers/Dashboard/BlogFeed.jsx
@@ -0,0 +1,77 @@
+/**
+ * Container for blog feed.
+ */
+
+import BlogFeed from 'components/Dashboard/BlogFeed';
+import PT from 'prop-types';
+import React from 'react';
+import actions from 'actions/blog';
+import { connect } from 'react-redux';
+
+
+class BlogFeedContainer extends React.Component {
+ componentDidMount() {
+ const {
+ getBlogs,
+ blogs,
+ itemCount,
+ } = this.props;
+
+ // This gets articles.
+ if (!blogs || blogs.length === 0) {
+ getBlogs({
+ limit: itemCount,
+ });
+ }
+ }
+
+ render() {
+ const {
+ blogs,
+ theme,
+ loading,
+ } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+BlogFeedContainer.defaultProps = {
+ itemCount: 5,
+ blogs: [],
+ loading: true,
+ theme: 'light',
+};
+
+BlogFeedContainer.propTypes = {
+ blogs: PT.oneOfType([PT.arrayOf(PT.shape()), PT.shape]),
+ itemCount: PT.number,
+ getBlogs: PT.func.isRequired,
+ loading: PT.bool,
+ theme: PT.oneOf(['dark', 'light']),
+};
+
+function mapStateToProps(state) {
+ const data = state.blog;
+ return {
+ blogs: data ? data.communityStories : [],
+ loading: data ? data.communityStoriesLoading : true,
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ const a = actions.blog;
+ return {
+ getBlogs: (ownProps) => {
+ dispatch(a.getCommunityStoriesInit());
+ dispatch(a.getCommunityStoriesDone(ownProps));
+ },
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(BlogFeedContainer);
diff --git a/src/shared/containers/Dashboard/ChallengesFeed.jsx b/src/shared/containers/Dashboard/ChallengesFeed.jsx
new file mode 100644
index 0000000000..5dbbfd7e56
--- /dev/null
+++ b/src/shared/containers/Dashboard/ChallengesFeed.jsx
@@ -0,0 +1,70 @@
+/**
+ * ChallengesFeed component
+ */
+import React from 'react';
+import PT from 'prop-types';
+import ChallengesFeed from 'components/Dashboard/Challenges';
+import { connect } from 'react-redux';
+import actions from '../../actions/dashboard';
+
+class ChallengesFeedContainer extends React.Component {
+ componentDidMount() {
+ const { getChallenges, challenges, itemCount } = this.props;
+
+ if (!challenges || challenges.length === 0) {
+ getChallenges({
+ page: 1,
+ perPage: itemCount,
+ types: ['CH', 'F2F', 'MM'],
+ tracks: ['DES', 'DEV', 'DEV', 'DS', 'QA'],
+ status: 'Active',
+ sortBy: 'updated',
+ sortOrder: 'desc',
+ isLightweight: true,
+ currentPhaseName: 'Registration',
+ });
+ }
+ }
+
+ render() {
+ const { challenges, theme, loading } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+ChallengesFeedContainer.defaultProps = {
+ itemCount: 5,
+ challenges: [],
+ loading: true,
+ theme: 'light',
+};
+
+ChallengesFeedContainer.propTypes = {
+ challenges: PT.arrayOf(PT.shape()),
+ itemCount: PT.number,
+ getChallenges: PT.func.isRequired,
+ loading: PT.bool,
+ theme: PT.oneOf(['dark', 'light']),
+};
+
+const mapStateToProps = state => ({
+ challenges: state.dashboard.challenges,
+ loading: state.dashboard.loading,
+});
+
+const mapDispatchToProps = dispatch => ({
+ getChallenges: (query) => {
+ const a = actions.dashboard;
+
+ dispatch(a.fetchChallengesInit());
+ dispatch(a.fetchChallengesDone(query));
+ },
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ChallengesFeedContainer);
diff --git a/src/shared/containers/Dashboard/GigsFeed.jsx b/src/shared/containers/Dashboard/GigsFeed.jsx
new file mode 100644
index 0000000000..5ad294e568
--- /dev/null
+++ b/src/shared/containers/Dashboard/GigsFeed.jsx
@@ -0,0 +1,81 @@
+/**
+ * Container for dashboard announcement.
+ */
+
+import GigsFeed from 'components/Dashboard/GigsFeed';
+import PT from 'prop-types';
+import React from 'react';
+import actions from 'actions/recruitCRM';
+import { connect } from 'react-redux';
+
+
+class GigsFeedContainer extends React.Component {
+ componentDidMount() {
+ const {
+ getGigs,
+ gigs,
+ itemCount,
+ } = this.props;
+
+ // This gets all jobs.
+ if (!gigs || gigs.length === 0) {
+ getGigs({
+ perPage: itemCount,
+ sortBy: 'createdAt',
+ sortOrder: 'desc',
+ status: 'sourcing',
+ isApplicationPageActive: true,
+ });
+ }
+ }
+
+ render() {
+ const {
+ gigs,
+ theme,
+ loading,
+ } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+GigsFeedContainer.defaultProps = {
+ itemCount: 10,
+ gigs: [],
+ loading: true,
+ theme: 'light',
+};
+
+GigsFeedContainer.propTypes = {
+ gigs: PT.arrayOf(PT.shape()),
+ itemCount: PT.number,
+ getGigs: PT.func.isRequired,
+ loading: PT.bool,
+ theme: PT.oneOf(['dark', 'light']),
+};
+
+function mapStateToProps(state) {
+ const data = state.recruitCRM;
+ return {
+ gigs: data ? data.gigs : [],
+ loading: data ? data.gigsLoading : true,
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ const a = actions.recruit;
+ return {
+ getGigs: (ownProps) => {
+ dispatch(a.getGigsInit(ownProps));
+ dispatch(a.getGigsDone(ownProps));
+ },
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(GigsFeedContainer);
diff --git a/src/shared/containers/Dashboard/NewsFeed/index.jsx b/src/shared/containers/Dashboard/NewsFeed/index.jsx
new file mode 100644
index 0000000000..70250d8059
--- /dev/null
+++ b/src/shared/containers/Dashboard/NewsFeed/index.jsx
@@ -0,0 +1,88 @@
+/**
+ * SlashTC NewsFeed container component
+ */
+import React, { useState, useEffect } from 'react';
+import LoadingIndicator from 'components/LoadingIndicator';
+import Masonry from 'react-masonry-css';
+import { config } from 'topcoder-react-utils';
+import moment from 'moment';
+import darkTheme from './themes/dark.scss';
+
+const htmlToText = require('html-to-text');
+
+const THEMES = {
+ dark: darkTheme,
+};
+const MAX_NEWS_TEXT_LEN = 85;
+
+function NewsFeedContainer() {
+ const [newsData, setNewsData] = useState();
+ const theme = THEMES.dark; // for v1 only dark theme
+
+ useEffect(() => {
+ async function fetchData() {
+ const isProd = config.URL.FORUMS_VANILLA === 'https://discussions.topcoder.com';
+ const result = await fetch(`/api/cdn/public/forums/discussions?categoryID=${isProd ? 1441 : 2716}`);
+ const data = await result.json();
+ setNewsData(
+ data.sort((a, b) => new Date(b.dateInserted) - new Date(a.dateInserted)).slice(0, 6),
+ );
+ }
+ fetchData();
+ }, []);
+
+ return !newsData ? : (
+
+
+
+ {
+ newsData.map((item) => {
+ const opt = {
+ ignoreHref: true,
+ ignoreImage: true,
+ singleNewLineParagraphs: true,
+ uppercaseHeadings: false,
+ };
+ const cont = htmlToText.fromString(
+ item.body,
+ opt,
+ );
+ const title = htmlToText.fromString(
+ item.name,
+ opt,
+ );
+ return (
+
+
{moment(item.dateInserted).format('MMM D, YYYY')}
+
{title}
+
+ {
+ cont.length > MAX_NEWS_TEXT_LEN ? (
+
+ {`${cont.substring(0, MAX_NEWS_TEXT_LEN)}... `}
+ Read more
+
+ ) : cont
+ }
+
+
+ );
+ })
+ }
+
+
+ );
+}
+
+export default NewsFeedContainer;
diff --git a/src/shared/containers/Dashboard/NewsFeed/themes/dark.scss b/src/shared/containers/Dashboard/NewsFeed/themes/dark.scss
new file mode 100644
index 0000000000..b0fc0d411b
--- /dev/null
+++ b/src/shared/containers/Dashboard/NewsFeed/themes/dark.scss
@@ -0,0 +1,90 @@
+@import "~styles/mixins";
+$gutterSize: 15px;
+
+.container {
+ width: 100%;
+ background-color: #2a2a2a;
+
+ .head {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+
+ h6 {
+ color: #fff;
+ font-family: Barlow, sans-serif;
+ font-size: 16px;
+ font-weight: 600;
+ line-height: 20px;
+ text-transform: uppercase;
+ }
+
+ a {
+ color: #5fb7ee;
+ font-family: Roboto, sans-serif;
+ font-size: 13px;
+ line-height: 20px;
+ text-decoration: underline;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+
+ :global {
+ .newsfeed-masonry-grid {
+ display: flex;
+ margin-left: -$gutterSize;
+ width: auto;
+
+ .newsfeed-masonry-grid_column {
+ padding-left: $gutterSize;
+ background-clip: padding-box;
+
+ .newsItem {
+ margin-bottom: $gutterSize;
+ background-color: #363636;
+ padding: 15px;
+ color: #fff;
+ font-family: Roboto, sans-serif;
+ border-radius: 8px;
+ overflow: hidden;
+
+ .date {
+ color: #d4d4d4;
+ font-size: 12px;
+ line-height: 18px;
+ margin-bottom: 2px;
+ }
+
+ .title {
+ color: #fff;
+ font-size: 14px;
+ line-height: 18px;
+ font-weight: 500;
+ margin-bottom: 4px;
+ display: inline-block;
+ }
+
+ .cont {
+ color: #d4d4d4;
+ font-family: Roboto, sans-serif;
+ font-size: 14px;
+ line-height: 20px;
+
+ .readmore {
+ color: #5fb7ee;
+ text-decoration: underline;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/shared/containers/Dashboard/TCOLeaderboards.jsx b/src/shared/containers/Dashboard/TCOLeaderboards.jsx
new file mode 100644
index 0000000000..a9df64e8d0
--- /dev/null
+++ b/src/shared/containers/Dashboard/TCOLeaderboards.jsx
@@ -0,0 +1,82 @@
+/**
+ * Container for TCOLeaderboards.
+ */
+
+import TCOLeaderboards from 'components/Dashboard/TCOLeaderboards';
+import PT from 'prop-types';
+import React from 'react';
+import actions from 'actions/tco/leaderboards';
+import { connect } from 'react-redux';
+
+
+class TCOLeaderboardsContainer extends React.Component {
+ componentDidMount() {
+ const {
+ fetchLeaderboards,
+ leaderboardsLoading,
+ leaderboards,
+ trackConfig,
+ auth,
+ } = this.props;
+ if (!leaderboards || (leaderboards.length === 0 && !leaderboardsLoading)) {
+ fetchLeaderboards(auth, trackConfig);
+ }
+ }
+
+ render() {
+ const {
+ leaderboardsLoading,
+ leaderboards,
+ itemCount,
+ } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+
+TCOLeaderboardsContainer.defaultProps = {
+ leaderboardsLoading: true,
+ leaderboards: null,
+ itemCount: 5,
+ auth: null,
+};
+
+TCOLeaderboardsContainer.propTypes = {
+ leaderboards: PT.arrayOf(PT.object),
+ leaderboardsLoading: PT.bool,
+ fetchLeaderboards: PT.func.isRequired,
+ trackConfig: PT.object.isRequired,
+ itemCount: PT.number,
+ auth: PT.object,
+};
+
+
+function mapStateToProps(state) {
+ return {
+ leaderboards: state.tcoLeaderboards.leaderboards,
+ leaderboardsLoading: state.tcoLeaderboards.loading,
+ auth: state.auth,
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ const a = actions.tcoLeaderboards;
+ return {
+ fetchLeaderboards: async (auth, trackConfig) => {
+ dispatch(a.fetchLeaderboardsInit(trackConfig));
+ dispatch(a.fetchLeaderboardsDone(auth, trackConfig));
+ },
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(TCOLeaderboardsContainer);
diff --git a/src/shared/containers/Dashboard/ThriveArticlesFeed.jsx b/src/shared/containers/Dashboard/ThriveArticlesFeed.jsx
new file mode 100644
index 0000000000..b4c0682753
--- /dev/null
+++ b/src/shared/containers/Dashboard/ThriveArticlesFeed.jsx
@@ -0,0 +1,77 @@
+/**
+ * Container for thrive articles feed.
+ */
+
+import ThriveArticlesFeed from 'components/Dashboard/ThriveArticlesFeed';
+import PT from 'prop-types';
+import React from 'react';
+import actions from 'actions/contentful';
+import { connect } from 'react-redux';
+
+
+class ThriveArticlesFeedContainer extends React.Component {
+ componentDidMount() {
+ const {
+ getArticles,
+ articles,
+ itemCount,
+ } = this.props;
+
+ // This gets articles.
+ if (!articles || articles.length === 0) {
+ getArticles({
+ limit: itemCount,
+ });
+ }
+ }
+
+ render() {
+ const {
+ articles,
+ theme,
+ loading,
+ } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+ThriveArticlesFeedContainer.defaultProps = {
+ itemCount: 5,
+ articles: [],
+ loading: true,
+ theme: 'light',
+};
+
+ThriveArticlesFeedContainer.propTypes = {
+ articles: PT.oneOfType([PT.arrayOf(PT.shape()), PT.shape]),
+ itemCount: PT.number,
+ getArticles: PT.func.isRequired,
+ loading: PT.bool,
+ theme: PT.oneOf(['dark', 'light']),
+};
+
+function mapStateToProps(state) {
+ const data = state.thrive;
+ return {
+ articles: data ? data.articles : [],
+ loading: data ? data.loading : true,
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ const a = actions.contentful;
+ return {
+ getArticles: (ownProps) => {
+ dispatch(a.getThriveArticlesInit());
+ dispatch(a.getThriveArticlesDone(ownProps));
+ },
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ThriveArticlesFeedContainer);
diff --git a/src/shared/containers/Dashboard/index.jsx b/src/shared/containers/Dashboard/index.jsx
index 2f5e42a29e..79e9e94ba4 100644
--- a/src/shared/containers/Dashboard/index.jsx
+++ b/src/shared/containers/Dashboard/index.jsx
@@ -1,475 +1,143 @@
+/* eslint-disable react/no-unused-prop-types */
+/* eslint-disable no-unused-vars */
+/* eslint-disable no-restricted-globals */
+/* eslint-disable react/destructuring-assignment */
/**
- * Container for the dashboard page.
+ * SlashTC index container
*/
-/* global location */
-/* eslint-disable no-restricted-globals */
-
-import _ from 'lodash';
-import challengeDetailsActions from 'actions/page/challenge-details';
-import cookies from 'browser-cookies';
-import Dashboard from 'components/Dashboard';
-import dashActions from 'actions/page/dashboard';
-import challengeListingSidebarActions from 'actions/challenge-listing/sidebar';
-import LoadingIndicator from 'components/LoadingIndicator';
-import { actions } from 'topcoder-react-lib';
+import React, { useEffect } from 'react';
import PT from 'prop-types';
-import qs from 'qs';
-import React from 'react';
-import rssActions from 'actions/rss';
-import shortId from 'shortid';
-
import { connect } from 'react-redux';
-import { BUCKETS } from 'utils/challenge-listing/buckets';
-import { updateChallengeType } from 'utils/challenge';
-
-import challengeListingActions from 'actions/challenge-listing';
-import communityActions from 'actions/tc-communities';
-
-import { isTokenExpired, decodeToken } from '@topcoder-platform/tc-auth-lib';
-import { config, isomorphy } from 'topcoder-react-utils';
-
-import './styles.scss';
-
-/* When mounted, this container triggers (re-)loading of various data it needs.
- * It will not reload any data already present in the Redux store, if they were
- * fetched more recently than this max age [ms]. */
-const CACHE_MAX_AGE = 10 * 60 * 1000;
-
-/* Constants for loading Topcoder blog. */
-const TOPCODER_BLOG_ID = 'TOPCODER_BLOG';
-const TOPOCDER_BLOG_URL = `/community-app-assets/api/proxy-get?url=${
- encodeURIComponent(config.URL.BLOG_FEED)}`;
-
-function updateCommunityStats(props) {
- const {
- activeChallenges,
- communities,
- communityStats,
- getCommunityStats,
- tokenV3,
- } = props;
- const now = Date.now();
- communities.forEach((community) => {
- const stats = communityStats[community.communityId];
- if (stats && (stats.loadingUuid
- || now - (stats.timestamp || 0) < CACHE_MAX_AGE)) return;
- getCommunityStats(community, activeChallenges, tokenV3);
- });
-}
-
-export class DashboardPageContainer extends React.Component {
- componentDidMount() {
- const {
- challengeFilter,
- switchChallengeFilter,
- getMemberResources,
- tokenV3,
- } = this.props;
-
- this.updateData(this.props);
-
- if (challengeFilter) switchChallengeFilter('');
-
- if (this.authCheck(tokenV3)) {
- const user = decodeToken(tokenV3);
- getMemberResources(user.userId, tokenV3);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- this.updateData(nextProps);
- }
+import { useMediaQuery } from 'react-responsive';
+import { isTokenExpired } from '@topcoder-platform/tc-auth-lib';
+import { config } from 'topcoder-react-utils';
+import Viewport from 'components/Contentful/Viewport';
+import TopcoderTime from 'components/Dashboard/TCTime';
+import ThriveArticlesFeedContainer from 'containers/Dashboard/ThriveArticlesFeed';
+import GigsFeed from 'containers/Dashboard/GigsFeed';
+import TCOLeaderboardsContainer from 'containers/Dashboard/TCOLeaderboards';
+import ContentfulLoader from 'containers/ContentfulLoader';
+import LoadingIndicator from 'components/LoadingIndicator';
+import ChallengesFeed from 'containers/Dashboard/ChallengesFeed';
+import BlogFeedContainer from 'containers/Dashboard/BlogFeed';
+import MetaTags from 'components/MetaTags';
+import NewsFeed from './NewsFeed';
+import darkTheme from './themes/dark.scss';
+
+const THEMES = {
+ dark: darkTheme,
+};
- /**
- * Does nothing if a valid TC API v3 token is passed in; otherwise redirects
- * user to the TC auth page, with proper return URL.
- * @param {String} tokenV3
- * @return {Boolean} `true` if the user is authenticated; `false` otherwise.
- */
- authCheck(tokenV3) {
- if (tokenV3 && !isTokenExpired(tokenV3)) return true;
+function SlashTCContainer(props) {
+ const theme = THEMES.dark; // for v1 only dark theme
+ const isTabletOrMobile = useMediaQuery({ maxWidth: 768 });
+ const title = 'Home | Topcoder';
- /* This implements front-end redirection. Once the server-side rendering of
- * the Dashboard is in place, this should be updated to work for the server
- * side rendering as well. */
+ useEffect(() => {
+ if (props.tokenV3 && !isTokenExpired(props.tokenV3)) return;
let url = `retUrl=${encodeURIComponent(location.href)}`;
- url = `${config.URL.AUTH}/member?${url}&utm_source=community-app-main`;
+ url = `${config.URL.AUTH}/member?${url}&utm_source=community-app-home-page`;
location.href = url;
+ }, [props.tokenV3]);
- _.noop(this);
- return false;
- }
-
- updateData({
- achievementsLoading,
- achievementsTimestamp,
- activeChallengesLoading,
- activeChallengesTimestamp,
- authenticating,
- communitiesLoading,
- communitiesTimestamp,
- financesLoading,
- financesTimestamp,
- getAllActiveChallenges,
- getCommunityList,
- getMemberAchievements,
- getMemberFinances,
- getMemberStats,
- getSrms,
- // getTopcoderBlogFeed,
- handle,
- profile,
- srmsLoading,
- srmsTimestamp,
- statsLoading,
- statsTimestamp,
- // tcBlogLoading,
- // tcBlogTimestamp,
- tokenV3,
- }) {
- if (authenticating || !this.authCheck(tokenV3)) return;
-
- const now = Date.now();
-
- if (now - achievementsTimestamp > CACHE_MAX_AGE
- && !achievementsLoading) getMemberAchievements(handle);
-
- if (now - activeChallengesTimestamp > CACHE_MAX_AGE
- && !activeChallengesLoading) getAllActiveChallenges(tokenV3);
-
- if (now - communitiesTimestamp > CACHE_MAX_AGE
- && !communitiesLoading) getCommunityList({ profile, tokenV3 });
-
- if (now - financesTimestamp > CACHE_MAX_AGE
- && !financesLoading) getMemberFinances(handle, tokenV3);
-
- if (now - srmsTimestamp > CACHE_MAX_AGE
- && !srmsLoading) getSrms(handle, tokenV3);
-
- if (now - statsTimestamp > CACHE_MAX_AGE
- && !statsLoading) getMemberStats(handle, tokenV3);
-
- // if (now - tcBlogTimestamp > CACHE_MAX_AGE
- // && !tcBlogLoading) getTopcoderBlogFeed();
-
- if (now - communitiesTimestamp < CACHE_MAX_AGE) {
- updateCommunityStats(this.props);
- }
- }
-
- render() {
- const {
- achievements,
- achievementsLoading,
- activeChallenges,
- activeChallengesLoading,
- authenticating,
- challengeFilter,
- communities,
- communitiesLoading,
- communityStats,
- finances,
- financesLoading,
- selectChallengeDetailsTab,
- setChallengeListingFilter,
- showChallengeFilter,
- showEarnings,
- showXlBadge,
- srms,
- srmsLoading,
- stats,
- statsLoading,
- switchChallengeFilter,
- switchShowChallengeFilter,
- switchShowEarnings,
- switchTab,
- tab,
- tcBlogLoading,
- tcBlogPosts,
- tokenV2,
- tokenV3,
- unregisterFromChallenge,
- urlQuery,
- userGroups,
- xlBadge,
- errorLoadingRss,
- userResources,
- challengeTypesMap,
- getTypes,
- } = this.props;
-
- // console.log('r', userResources);
-
- if (authenticating) return ;
-
- let announcementPreviewId;
- if (urlQuery) {
- ({ announcementPreviewId } = qs.parse(urlQuery));
- }
-
- if (_.isEmpty(challengeTypesMap)) {
- getTypes();
- }
-
- return (
- unregisterFromChallenge({ tokenV2, tokenV3 }, id)}
- userGroups={userGroups.map(x => x.id)}
- xlBadge={xlBadge}
- errorLoadingRss={errorLoadingRss}
- userResources={userResources ? userResources.resources : []}
- challengeTypesMap={challengeTypesMap}
+ return (
+
+
- );
- }
+ {
+ // Render different stacking of components for tables&mobile devices
+ isTabletOrMobile ? (
+
+
+
+
+
+
+
+ {
+ const confTCO = data.entries.items['5HmoppBlc79RfxOwb8JAls'];
+ if (confTCO) {
+ return (
+
+ );
+ }
+ return null;
+ }}
+ renderPlaceholder={LoadingIndicator}
+ />
+
+
+
+
+
+
+ ) : (
+
+ {/* Left column */}
+
+
+
+
+
+
+ {/* Center column */}
+
+
+
+
+
+
+ {/* Right column */}
+
+ {
+ const confTCO = data.entries.items['5HmoppBlc79RfxOwb8JAls'];
+ if (confTCO) {
+ return (
+
+ );
+ }
+ return null;
+ }}
+ renderPlaceholder={LoadingIndicator}
+ />
+
+
+
+ )
+ }
+
+ );
}
-DashboardPageContainer.defaultProps = {
- achievements: [],
- achievementsTimestamp: 0,
- finances: [],
- financesTimestamp: 0,
+SlashTCContainer.defaultProps = {
profile: null,
- showEarnings:
- isomorphy.isClientSide() ? cookies.get('showEarningsInDashboard') !== 'false' : true,
- stats: {},
- statsTimestamp: 0,
- tcBlogPosts: [],
- tcBlogTimestamp: 0,
- tokenV2: null,
tokenV3: null,
- errorLoadingRss: false,
- userResources: {},
};
-DashboardPageContainer.propTypes = {
- achievements: PT.arrayOf(PT.object),
- achievementsLoading: PT.bool.isRequired,
- achievementsTimestamp: PT.number, // eslint-disable-line react/no-unused-prop-types
- activeChallenges: PT.arrayOf(PT.object).isRequired,
- activeChallengesLoading: PT.bool.isRequired,
- activeChallengesTimestamp: PT.number.isRequired, // eslint-disable-line react/no-unused-prop-types
- authenticating: PT.bool.isRequired, // eslint-disable-line react/no-unused-prop-types
- challengeFilter: PT.string.isRequired,
- communities: PT.arrayOf(PT.object).isRequired,
- communitiesLoading: PT.bool.isRequired,
- communityStats: PT.shape().isRequired,
- communitiesTimestamp: PT.number.isRequired, // eslint-disable-line react/no-unused-prop-types
- finances: PT.arrayOf(PT.object),
- financesLoading: PT.bool.isRequired,
- financesTimestamp: PT.number, // eslint-disable-line react/no-unused-prop-types
- getAllActiveChallenges: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getCommunityList: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getCommunityStats: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getMemberAchievements: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getMemberFinances: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getMemberStats: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getSrms: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- getTopcoderBlogFeed: PT.func.isRequired, // eslint-disable-line react/no-unused-prop-types
- profile: PT.shape(), // eslint-disable-line react/no-unused-prop-types
- selectChallengeDetailsTab: PT.func.isRequired,
- setChallengeListingFilter: PT.func.isRequired,
- showChallengeFilter: PT.bool.isRequired,
- showEarnings: PT.bool,
- showXlBadge: PT.func.isRequired,
- srms: PT.arrayOf(PT.object).isRequired,
- srmsLoading: PT.bool.isRequired,
- srmsTimestamp: PT.number.isRequired, // eslint-disable-line react/no-unused-prop-types
- stats: PT.shape(),
- statsLoading: PT.bool.isRequired,
- statsTimestamp: PT.number, // eslint-disable-line react/no-unused-prop-types
- switchChallengeFilter: PT.func.isRequired,
- switchShowChallengeFilter: PT.func.isRequired,
- switchShowEarnings: PT.func.isRequired,
- switchTab: PT.func.isRequired,
- tab: PT.string.isRequired,
- tcBlogLoading: PT.bool.isRequired,
- tcBlogPosts: PT.arrayOf(PT.object),
- tcBlogTimestamp: PT.number, // eslint-disable-line react/no-unused-prop-types
- tokenV2: PT.string,
+SlashTCContainer.propTypes = {
+ profile: PT.shape(),
tokenV3: PT.string,
- unregisterFromChallenge: PT.func.isRequired,
- urlQuery: PT.string.isRequired,
- userGroups: PT.arrayOf(PT.object).isRequired,
- xlBadge: PT.string.isRequired,
- errorLoadingRss: PT.bool,
- getMemberResources: PT.func.isRequired,
- userResources: PT.shape(),
- challengeTypesMap: PT.shape().isRequired,
- getTypes: PT.func.isRequired,
};
-function mapStateToProps(state, props) {
- const communities = state.tcCommunities.list;
-
- const userHandle = _.get(state.auth, 'user.handle');
- const member = state.members[userHandle] || {};
- const achievements = member.achievements || {};
- const finances = member.finances || {};
- const stats = member.stats || {};
-
- const dash = state.page.dashboard;
-
- const tcBlog = state.rss ? (state.rss[TOPCODER_BLOG_ID] || {}) : {};
- updateChallengeType(
- state.challengeListing.challenges, state.challengeListing.challengeTypesMap,
- );
+function mapStateToProps(state) {
+ const profile = state.auth && state.auth.profile ? { ...state.auth.profile } : {};
return {
- achievements: achievements.data,
- achievementsLoading: Boolean(achievements.loadingUuid),
- achievementsTimestamp: achievements.timestamp,
- activeChallenges: state.challengeListing.challenges,
- activeChallengesLoading:
- Boolean(state.challengeListing.loadingActiveChallengesUUID),
- activeChallengesTimestamp:
- state.challengeListing.lastUpdateOfActiveChallenges,
- authenticating: state.auth.authenticating,
- challengeFilter: dash.challengeFilter,
- communities: communities.data,
- communitiesLoading: Boolean(communities.loadingUuid),
- communitiesTimestamp: communities.timestamp,
- communityStats: state.stats.communities,
- finances: finances.data,
- financesLoading: Boolean(finances.loadingUuid),
- financesTimestamp: finances.timestamp,
- handle: userHandle,
- profile: state.auth.profile,
- showChallengeFilter: dash.showChallengeFilter,
- showEarnings: dash.showEarnings,
- srms: state.challengeListing.srms.data,
- srmsLoading: Boolean(state.challengeListing.srms.loadingUuid),
- srmsTimestamp: state.challengeListing.srms.timestamp,
- stats: stats.data,
- statsLoading: Boolean(stats.loadingUuid),
- statsTimestamp: stats.timestamp,
- tab: dash.tab,
- tcBlogLoading: Boolean(tcBlog.loadingUuid),
- tcBlogPosts: _.get(tcBlog, 'data.item'),
- tcBlogTimestamp: tcBlog.timestamp,
- tokenV2: state.auth.tokenV2,
+ profile,
tokenV3: state.auth.tokenV3,
- urlQuery: props.location.search.slice(1),
- userGroups: _.get(state.auth.profile, 'groups', []),
- xlBadge: dash.xlBadge,
- errorLoadingRss: state.rss.errorLoadingRss,
- userResources: state.members.userResources,
- challengeTypesMap: state.challengeListing.challengeTypesMap,
};
}
-function mapDispatchToProps(dispatch) {
- const dash = dashActions.page.dashboard;
- const { members } = actions;
- return {
- getAllActiveChallenges: (tokenV3) => {
- const uuid = shortId();
- dispatch(challengeListingActions.challengeListing.getAllUserChallengesInit(uuid));
- dispatch(challengeListingActions.challengeListing.getAllUserChallengesDone(uuid, tokenV3));
- },
- getCommunityList: (auth) => {
- const uuid = shortId();
- dispatch(communityActions.tcCommunity.getListInit(uuid));
- dispatch(communityActions.tcCommunity.getListDone(uuid, auth));
- },
- getCommunityStats: (community, challenges, token) => {
- const uuid = shortId();
- const a = actions.stats;
- dispatch(a.getCommunityStatsInit(community, uuid));
- dispatch(a.getCommunityStatsDone(community, uuid, challenges, token));
- },
- getMemberAchievements: (handle) => {
- const uuid = shortId();
- dispatch(members.getAchievementsInit(handle, uuid));
- dispatch(members.getAchievementsV3Done(handle, uuid));
- },
- getMemberFinances: (handle, tokenV3) => {
- const uuid = shortId();
- dispatch(members.getFinancesInit(handle, uuid));
- dispatch(members.getFinancesDone(handle, uuid, tokenV3));
- },
- getMemberStats: (handle, tokenV3) => {
- const uuid = shortId();
- dispatch(members.getStatsInit(handle, uuid));
- dispatch(members.getStatsDone(handle, uuid, tokenV3));
- },
- getMemberResources: (memberId, tokenV3) => {
- const uuid = shortId();
- dispatch(members.getUserResourcesInit(memberId, uuid));
- dispatch(members.getUserResourcesDone(memberId, tokenV3, uuid));
- },
- getSrms: (handle, tokenV3) => {
- const uuid = shortId();
- const a = challengeListingActions.challengeListing;
- dispatch(a.getSrmsInit(uuid));
- dispatch(a.getSrmsDone(uuid, handle, {
- filter: 'status=future',
- orderBy: 'registrationStartAt',
- limit: 3,
- }, tokenV3));
- },
- getTopcoderBlogFeed: () => {
- const uuid = shortId();
- const a = rssActions.rss;
- dispatch(a.getInit(TOPCODER_BLOG_ID, uuid));
- dispatch(a.getDone(TOPCODER_BLOG_ID, uuid, TOPOCDER_BLOG_URL));
- },
- selectChallengeDetailsTab:
- tab => dispatch(challengeDetailsActions.page.challengeDetails.selectTab(tab)),
- setChallengeListingFilter: (filter) => {
- const cl = challengeListingActions.challengeListing;
- const cls = challengeListingSidebarActions.challengeListing.sidebar;
- dispatch(cl.setFilter(filter));
- dispatch(cls.selectBucket(BUCKETS.OPEN_FOR_REGISTRATION));
- },
- showXlBadge: name => dispatch(dash.showXlBadge(name)),
- switchChallengeFilter: filter => dispatch(dash.switchChallengeFilter(filter)),
- switchShowChallengeFilter:
- show => dispatch(dash.showChallengeFilter(show)),
- switchShowEarnings: show => dispatch(dash.showEarnings(show)),
- switchTab: tab => dispatch(dash.switchTab(tab)),
- unregisterFromChallenge: (auth, challengeId) => {
- const a = actions.challenge;
- dispatch(a.unregisterInit());
- dispatch(a.unregisterDone(auth, challengeId));
- },
- getTypes: () => {
- const cl = challengeListingActions.challengeListing;
- dispatch(cl.getChallengeTypesInit());
- dispatch(cl.getChallengeTypesDone());
- },
- };
-}
-
-const DashboardContainer = connect(
+export default connect(
mapStateToProps,
- mapDispatchToProps,
-)(DashboardPageContainer);
-
-export default DashboardContainer;
+)(SlashTCContainer);
diff --git a/src/shared/containers/Dashboard/styles.scss b/src/shared/containers/Dashboard/styles.scss
deleted file mode 100644
index 00d0e796bf..0000000000
--- a/src/shared/containers/Dashboard/styles.scss
+++ /dev/null
@@ -1,334 +0,0 @@
-@import '~styles/mixins';
-
-.dashboard-container {
- background-color: #f6f6f6;
- flex: 1;
-}
-
-.page-container {
- padding: 10px;
-}
-
-@media screen and (min-width: 768px) {
- .page-container {
- padding: 30px 10px;
- }
-}
-
-.tc-banner-placeholder {
- display: flex;
- flex-direction: column;
- align-items: center;
- color: #3d3d3d;
- padding: 20px 10px;
-
- @media only screen and (min-width: 768px) {
- padding: 30px 20px;
- }
-
- .image {
- margin-bottom: 15px;
-
- img {
- width: 186px;
- }
- }
-
- .title {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 24px;
- line-height: 29px;
- text-align: center;
- text-transform: uppercase;
- }
-
- .content {
- margin-top: 20px;
-
- @media only screen and (min-width: 768px) {
- margin-top: 30px;
- }
- }
-
- .description {
- max-width: 650px;
- margin-top: 20px;
- font-family: 'Merriweather Sans', Arial, Helvetica, sans-serif;
- font-weight: 400;
- font-size: 15px;
- line-height: 24px;
- text-align: center;
-
- @media only screen and (min-width: 768px) {
- margin-top: 30px;
- }
-
- @media only screen and (min-width: 900px) {
- max-width: 856px;
- }
- }
-
- .ctas {
- text-align: center;
- display: flex;
- flex-direction: column;
- margin-top: 20px;
-
- @media only screen and (min-width: 768px) {
- margin-top: 30px;
- }
-
- .cta {
- &:not(:first-child) {
- margin-top: 30px;
- }
- }
-
- a {
- text-transform: uppercase;
- display: block;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
-
- &.secondary-cta {
- font-size: 12px;
- line-height: 12px;
- color: #a3a3ae;
- }
-
- &.tco-cta {
- border: 1px solid $tc-orange;
- background-color: $tc-orange;
-
- &:focus {
- border: 1px solid $tc-orange;
- background-color: $tc-orange;
- }
-
- &:hover {
- background-color: $tc-orange-110;
- border-color: $tc-orange-110;
- }
-
- &:active {
- background-color: $tc-orange-110;
- border-color: $tc-orange-110;
- }
- }
- }
- }
-}
-
-.my-dashboard-container {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- .section-title {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 500;
- font-size: 24px;
- line-height: 29px;
- color: #3d3d3d;
- text-align: center;
- text-transform: uppercase;
- }
-
- .subtrack-stats {
- width: 100%;
- max-width: 1242px;
- background-color: $tc-white;
- }
-
- .challenges,
- .srms,
- .programs,
- .tco,
- .ttl,
- .community-updates {
- background-color: $tc-white;
- max-width: 1242px;
- margin-left: 10px;
- margin-right: 10px;
- padding-top: 0;
- margin-top: 1px;
- width: 100%;
-
- @media only screen and (min-width: 900px) {
- padding-top: 30px;
- }
-
- header {
- .section-title {
- padding-top: 30px;
- }
- }
- }
-
- .ttl {
- .tc-banner-placeholder {
- background: url(assets/images/dashboard/team-live-bg.png) repeat;
-
- .image {
- img {
- width: auto;
- }
- }
- }
- }
-}
-
-// from topcoder-app/assets/css/directives/tc-banner.scss
-.tco17 {
- margin-bottom: 10px;
-}
-
-.tco18 {
- margin-bottom: 10px;
-}
-
-// themes
-
-// black
-.tc-banner-placeholder.black {
- margin-bottom: 10px;
- background-color: #222;
-
- .title {
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- color: $tc-white;
- }
-
- .subtitle {
- @extend .title;
-
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 400;
- }
-
- .description {
- color: $tc-white;
- }
-
- .ctas {
- .cta {
- .learn-more {
- color: #f6f6f6;
- }
- }
- }
-
- .tc-btn-white {
- background-color: white;
- color: #0096ff;
- padding: 10px 20px;
- margin-top: 5px;
- }
-
- .tc-btn-radius {
- border-radius: 26px;
- }
-}
-
-.tc-banner-placeholder.bg-image {
- background-image: url(assets/images/dashboard/home-hero.png);
- background-size: 100%;
- height: 352px;
- background-position: center 25%;
- background-repeat: no-repeat;
- flex-direction: row;
- padding-top: 50px;
-
- .container {
- width: 50%;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- margin-left: 37px;
- height: 95%;
- justify-content: space-between;
-
- .title {
- margin-top: 10px;
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: bold;
- color: $tc-white;
- }
-
- .subtitle {
- margin-top: 20px;
- width: 450px;
- font-size: 20px;
-
- @extend .title;
-
- font-family: 'Sofia Pro', Arial, Helvetica, sans-serif;
- font-weight: 400;
- text-align: left;
- }
-
- .description {
- margin-top: 20px;
- margin-bottom: 5px;
- }
-
- .cta {
- margin-top: 20px;
- }
- }
-}
-
-.tc-banner-placeholder.cognitive {
- background-image: url(assets/images/dashboard/cognitive-home-hero.jpg);
- background-size: cover;
- height: 352px;
- background-position: center 100%;
- background-repeat: no-repeat;
- background-color: #222;
- z-index: 1;
- flex-direction: row;
-
- .container {
- width: 50%;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- margin-left: 37px;
- height: 95%;
- justify-content: space-between;
-
- .img {
- background-image: url(assets/images/dashboard/cognitive-home-hero-title.png);
- background-size: 100%;
- background-repeat: no-repeat;
- background-position: center 100%;
- height: 57px;
- width: 75%;
- }
-
- .description {
- color: $tc-white;
- margin: 0 30px 0 0;
- font-size: 24px;
- line-height: 29px;
- text-align: left;
- font-weight: 300;
- }
-
- .cta {
- margin-top: 20px;
- }
-
- .tc-btn-white {
- background-color: white;
- color: #0096ff;
- padding: 10px 20px;
- }
-
- .tc-btn-radius {
- border-radius: 26px;
- }
- }
-}
diff --git a/src/shared/containers/Dashboard/themes/dark.scss b/src/shared/containers/Dashboard/themes/dark.scss
new file mode 100644
index 0000000000..b6afff2f7f
--- /dev/null
+++ b/src/shared/containers/Dashboard/themes/dark.scss
@@ -0,0 +1,35 @@
+@import "~styles/mixins";
+
+.container {
+ background-color: #2a2a2a;
+ padding-top: 32px;
+ padding-bottom: 100px;
+ width: 100%;
+ border-bottom: 1px solid #555;
+
+ @media screen and (max-width: 1280px) {
+ padding-left: 15px;
+ padding-right: 15px;
+ }
+
+ @media screen and (max-width: 768px) {
+ padding-bottom: 30px;
+ }
+
+ .layoutWrapper {
+ display: grid;
+ gap: 44px;
+ grid-template-columns: 260px 1fr 260px;
+ margin: auto;
+ max-width: 1280px;
+
+ @media screen and (max-width: 1024px) {
+ grid-template-columns: 1fr;
+ }
+
+ .column {
+ display: flex;
+ flex-direction: column;
+ }
+ }
+}
diff --git a/src/shared/containers/Gigs/RecruitCRMJobApply.jsx b/src/shared/containers/Gigs/RecruitCRMJobApply.jsx
index d74b876ec7..4c086cf6ab 100644
--- a/src/shared/containers/Gigs/RecruitCRMJobApply.jsx
+++ b/src/shared/containers/Gigs/RecruitCRMJobApply.jsx
@@ -123,12 +123,14 @@ class RecruitCRMJobApplyContainer extends React.Component {
}
onApplyClick() {
- const { applyForJob, job, optimizely } = this.props;
+ const {
+ applyForJob, job, optimizely, auth,
+ } = this.props;
const { formData } = this.state;
this.validateForm();
this.setState((state) => {
if (_.isEmpty(state.formErrors)) {
- applyForJob(job, formData);
+ applyForJob(job, formData, auth.tokenV3);
optimizely.track('Submit Application Form');
const isFeatured = _.find(job.custom_fields, ['field_name', 'Featured']);
const jobTags = _.find(job.custom_fields, ['field_name', 'Job Tag']);
@@ -279,6 +281,7 @@ RecruitCRMJobApplyContainer.defaultProps = {
applying: false,
application: null,
recruitProfile: null,
+ auth: {},
};
RecruitCRMJobApplyContainer.propTypes = {
@@ -290,6 +293,7 @@ RecruitCRMJobApplyContainer.propTypes = {
searchCandidates: PT.func.isRequired,
recruitProfile: PT.shape(),
optimizely: PT.shape().isRequired,
+ auth: PT.object,
};
function mapStateToProps(state, ownProps) {
@@ -313,15 +317,18 @@ function mapStateToProps(state, ownProps) {
? state.recruitCRM[job.slug].application : null,
recruitProfile: state.recruitCRM && profile && state.recruitCRM[profile.email]
? state.recruitCRM[profile.email].profile : null,
+ auth: {
+ ...state.auth,
+ },
};
}
function mapDispatchToActions(dispatch) {
const a = actions.recruit;
return {
- applyForJob: (job, payload) => {
+ applyForJob: (job, payload, tokenV3) => {
dispatch(a.applyForJobInit(job, payload));
- dispatch(a.applyForJobDone(job, payload));
+ dispatch(a.applyForJobDone(job, payload, tokenV3));
},
searchCandidates: (email) => {
dispatch(a.searchCandidatesInit(email));
diff --git a/src/shared/containers/Gigs/RecruitCRMJobs.jsx b/src/shared/containers/Gigs/RecruitCRMJobs.jsx
index 8bc3af8bd4..af5c0bc377 100644
--- a/src/shared/containers/Gigs/RecruitCRMJobs.jsx
+++ b/src/shared/containers/Gigs/RecruitCRMJobs.jsx
@@ -179,9 +179,9 @@ class RecruitCRMJobsContainer extends React.Component {
// build current locations dropdown based on all data
// and filter by selected location
jobsToDisplay = _.filter(jobs, (job) => {
- const country = !job.country || job.country === 'Anywhere' || job.country === 'Any' ? 'All' : job.country;
+ const country = _.trim(!job.country || job.country === 'Anywhere' || job.country === 'Any' ? 'All' : job.country);
// build dropdown
- const found = _.findIndex(locations, { label: country });
+ const found = _.findIndex(locations, l => l.label.toLowerCase() === country.toLowerCase());
if (found === -1) {
locations.push({
label: country, selected: location.toLowerCase() === country.toLowerCase(),
diff --git a/src/shared/reducers/blog.js b/src/shared/reducers/blog.js
new file mode 100644
index 0000000000..4549fc9f10
--- /dev/null
+++ b/src/shared/reducers/blog.js
@@ -0,0 +1,46 @@
+/**
+ * Reducer for the community leaderboard page
+ */
+
+import actions from 'actions/blog';
+import { redux } from 'topcoder-react-utils';
+
+/**
+ * Handles contentful.getThriveArticlesInit action.
+ * @param {Object} state Previous state.
+ */
+function onGetCommunityStoriesInit(state) {
+ return {
+ ...state,
+ communityStoriesLoading: true,
+ communityStories: [],
+ };
+}
+
+/**
+ * Handles contentful.getThriveArticlesDone action.
+ * @param {Object} state Previous state.
+ * @param {Object} action The action.
+ */
+function onGetCommunityStoriesDone(state, action) {
+ return {
+ ...state,
+ communityStoriesLoading: false,
+ communityStories: action.payload,
+ };
+}
+
+/**
+ * Creates a new blog reducer with the specified initial state.
+ * @param {Object} initialState Optional. Initial state.
+ * @return Function reducer.
+ */
+function create(initialState = {}) {
+ return redux.handleActions({
+ [actions.blog.getCommunityStoriesInit]: onGetCommunityStoriesInit,
+ [actions.blog.getCommunityStoriesDone]: onGetCommunityStoriesDone,
+ }, initialState);
+}
+
+/* Default reducer with empty initial state. */
+export default create();
diff --git a/src/shared/reducers/contentful/index.js b/src/shared/reducers/contentful/index.js
index 248880817e..9c0a27b45d 100644
--- a/src/shared/reducers/contentful/index.js
+++ b/src/shared/reducers/contentful/index.js
@@ -57,8 +57,8 @@ function create(init) {
const { error, payload } = action;
const newState = _.clone(state);
- const spaceName = payload.spaceName || config.CONTENTFUL.DEFAULT_SPACE_NAME;
- const environment = payload.environment || config.CONTENTFUL.DEFAULT_ENVIRONMENT;
+ const spaceName = _.get(payload, 'spaceName') || config.CONTENTFUL.DEFAULT_SPACE_NAME;
+ const environment = _.get(payload, 'environment') || config.CONTENTFUL.DEFAULT_ENVIRONMENT;
const res = _.get(newState, `${spaceName}.${environment}`);
if (error || !res) {
logger.log('CMS-related error');
diff --git a/src/shared/reducers/contentful/thrive.js b/src/shared/reducers/contentful/thrive.js
new file mode 100644
index 0000000000..17f4011cb3
--- /dev/null
+++ b/src/shared/reducers/contentful/thrive.js
@@ -0,0 +1,47 @@
+/**
+ * Reducer for state.challengesBlock
+ */
+
+import actions from 'actions/contentful';
+import { handleActions } from 'redux-actions';
+
+/**
+ * Handles contentful.getThriveArticlesInit action.
+ * @param {Object} state Previous state.
+ */
+function onGetThriveArticlesInit(state) {
+ return {
+ ...state,
+ loading: true,
+ articles: [],
+ };
+}
+
+/**
+ * Handles contentful.getThriveArticlesDone action.
+ * @param {Object} state Previous state.
+ * @param {Object} action The action.
+ */
+function onGetThriveArticlesDone(state, action) {
+ return {
+ ...state,
+ loading: false,
+ articles: action.payload,
+ };
+}
+
+/**
+ * Creates thrive reducer with the specified initial state.
+ * @param {Object} state Optional. If not given, the default one is
+ * generated automatically.
+ * @return {Function} Reducer.
+ */
+function create(state = {}) {
+ return handleActions({
+ [actions.contentful.getThriveArticlesInit]: onGetThriveArticlesInit,
+ [actions.contentful.getThriveArticlesDone]: onGetThriveArticlesDone,
+ }, state);
+}
+
+/* Reducer with the default initial state. */
+export default create();
diff --git a/src/shared/reducers/dashboard.js b/src/shared/reducers/dashboard.js
new file mode 100644
index 0000000000..c0fe816909
--- /dev/null
+++ b/src/shared/reducers/dashboard.js
@@ -0,0 +1,57 @@
+/**
+ * Reducer for dashboard
+ */
+
+import actions from 'actions/dashboard';
+import { redux } from 'topcoder-react-utils';
+
+/**
+ * Handles done actions.
+ * @param {Object} state Previous state.
+ * @param {Object} action Action.
+ */
+function onDone(state, action) {
+ return {
+ ...state,
+ challenges: action.error ? null : action.payload,
+ failed: action.error,
+ loading: false,
+ };
+}
+
+/**
+ * Creates a new challenges reducer with the specified initial state.
+ * @param {Object} initialState Optional. Initial state.
+ * @return challenges reducer.
+ */
+function create(initialState) {
+ return redux.handleActions({
+ [actions.dashboard.fetchChallengesInit](state) {
+ return {
+ ...state,
+ details: null,
+ failed: false,
+ loading: true,
+ };
+ },
+ [actions.dashboard.fetchChallengesDone]: onDone,
+ }, initialState || {});
+}
+
+/**
+ * Factory which creates a new reducer with its initial state tailored to the
+ * ExpressJS HTTP request, if specified (for efficient server-side rendering).
+ * If HTTP request is not specified, it creates just the default reducer.
+ * @param {Object} req Optional. ExpressJS HTTP request.
+ * @return Promise which resolves to the new reducer.
+ */
+export function factory(req) {
+ if (req && req.url.endsWith('/home')) {
+ return redux.resolveAction(actions.dashboard.fetchChallengesDone())
+ .then(res => create(onDone({}, res)));
+ }
+ return Promise.resolve(create());
+}
+
+/* Default reducer with empty initial state. */
+export default create();
diff --git a/src/shared/reducers/index.js b/src/shared/reducers/index.js
index 3d41dc0f95..ab8dc25019 100644
--- a/src/shared/reducers/index.js
+++ b/src/shared/reducers/index.js
@@ -38,9 +38,13 @@ import { factory as scoreboardFactory } from './tco/scoreboard';
import { factory as termsFactory } from './terms';
import newsletterPreferences from './newsletterPreferences';
import mmLeaderboard from './mmLeaderboard';
+import tcoLeaderboards from './tco/leaderboards';
import recruitCRM from './recruitCRM';
import gSheet from './gSheet';
import growSurf from './growSurf';
+import thrive from './contentful/thrive';
+import dashboard from './dashboard';
+import blog from './blog';
/**
* Given HTTP request, generates options for SSR by topcoder-react-lib's reducer
@@ -170,6 +174,10 @@ export function factory(req) {
mmLeaderboard,
gSheet,
growSurf,
+ thrive,
+ tcoLeaderboards,
+ dashboard,
+ blog,
}));
}
diff --git a/src/shared/reducers/recruitCRM.js b/src/shared/reducers/recruitCRM.js
index 9942cb2602..888b46dfb5 100644
--- a/src/shared/reducers/recruitCRM.js
+++ b/src/shared/reducers/recruitCRM.js
@@ -126,6 +126,31 @@ function onGetJobApplicationsDone(state, { payload }) {
};
}
+/**
+ * Handles recruit.getGigsInit action.
+ * @param {Object} state Previous state.
+ */
+function onGigsInit(state) {
+ return {
+ ...state,
+ gigs: [],
+ gigsLoading: true,
+ };
+}
+
+/**
+ * Handles recruit.getGigsDone action.
+ * @param {Object} state Previous state.
+ * @param {Object} action The action.
+ */
+function onGigsDone(state, { payload }) {
+ return {
+ ...state,
+ gigsLoading: false,
+ gigs: payload.data,
+ };
+}
+
/**
* Creates recruitCRM reducer with the specified initial state.
* @param {Object} state Optional. If not given, the default one is
@@ -144,6 +169,8 @@ function create(state = {}) {
[actions.recruit.searchCandidatesDone]: onSearchCandidatesDone,
[actions.recruit.getJobApplicationsInit]: onGetJobApplicationsInit,
[actions.recruit.getJobApplicationsDone]: onGetJobApplicationsDone,
+ [actions.recruit.getGigsInit]: onGigsInit,
+ [actions.recruit.getGigsDone]: onGigsDone,
}, state);
}
diff --git a/src/shared/reducers/tco/leaderboards/index.js b/src/shared/reducers/tco/leaderboards/index.js
new file mode 100644
index 0000000000..9609390929
--- /dev/null
+++ b/src/shared/reducers/tco/leaderboards/index.js
@@ -0,0 +1,41 @@
+/**
+ * Reducer for the TCO leaderboards component
+ */
+
+import actions from 'actions/tco/leaderboards';
+import { redux } from 'topcoder-react-utils';
+
+/**
+ * Handles tcoLeaderboards.fetchLeaderboards action.
+ * @param {Object} state Previous state.
+ * @param {Object} action Action.
+ */
+function onDone(state, action) {
+ return {
+ ...state,
+ loading: false,
+ leaderboards: action.payload,
+ };
+}
+
+
+/**
+ * Creates a new TCOLeaderboards reducer with the specified initial state.
+ * @param {Object} initialState Optional. Initial state.
+ * @return TCOLeaderboards reducer.
+ */
+function create(initialState) {
+ return redux.handleActions({
+ [actions.tcoLeaderboards.fetchLeaderboardsInit](state, action) {
+ return {
+ ...state,
+ leaderboards: action.payload,
+ loading: true,
+ };
+ },
+ [actions.tcoLeaderboards.fetchLeaderboardsDone]: onDone,
+ }, initialState || {});
+}
+
+/* Default reducer with empty initial state. */
+export default create();
diff --git a/src/shared/routes/Examples/Examples.jsx b/src/shared/routes/Examples/Examples.jsx
index a266bfe53a..4c23246982 100644
--- a/src/shared/routes/Examples/Examples.jsx
+++ b/src/shared/routes/Examples/Examples.jsx
@@ -3,7 +3,6 @@
* available in this App code.
*/
-import Announcement from 'components/examples/Announcement';
import Buttons from 'components/examples/Buttons';
import Carousel from 'components/examples/Carousel';
import ColorMixins from 'components/examples/ColorMixins';
@@ -31,7 +30,12 @@ import SearchBarExample from 'components/examples/SearchBar';
import TracksTreeExample from 'components/examples/TracksTree';
import TracksFilterExample from 'components/examples/TracksFilter';
import SearchPageFilterExample from 'components/examples/SearchPageFilter';
+import BlogFeedExample from 'components/examples/BlogFeed';
import GUIKit from 'components/examples/GUIKit';
+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 {
Switch,
@@ -49,7 +53,6 @@ export default function Examples({
}) {
return (
-
@@ -98,6 +101,11 @@ export default function Examples({
+
+
+
+
+
);
diff --git a/src/shared/routes/Topcoder/Dashboard.jsx b/src/shared/routes/Topcoder/Dashboard.jsx
index 2a7bc672cd..bd6a50c453 100644
--- a/src/shared/routes/Topcoder/Dashboard.jsx
+++ b/src/shared/routes/Topcoder/Dashboard.jsx
@@ -7,7 +7,7 @@ export default function DashboardRoute(props) {
import(/* webpackChunkName: "dashboard/chunk" */'containers/Dashboard')
.then(({ default: Dashboard }) => (
diff --git a/src/shared/routes/Topcoder/Routes.jsx b/src/shared/routes/Topcoder/Routes.jsx
index b230077d0d..87b988cf1e 100644
--- a/src/shared/routes/Topcoder/Routes.jsx
+++ b/src/shared/routes/Topcoder/Routes.jsx
@@ -16,7 +16,7 @@ import React from 'react';
import ReviewOpportunityDetails from 'routes/ReviewOpportunityDetails';
import Submission from 'routes/Submission';
import SubmissionManagement from 'routes/SubmissionManagement';
-import { Route, Switch } from 'react-router-dom';
+import { Route, Switch, Redirect } from 'react-router-dom';
import { config, isomorphy } from 'topcoder-react-utils';
import ContentfulLoader from 'containers/ContentfulLoader';
import LoadingIndicator from 'components/LoadingIndicator';
@@ -63,7 +63,12 @@ export default function Topcoder() {
/>
-
+
+
(res.ok ? res.json() : new Error(res.statusText)));
+ }
+}
+
+/**
+ * Returns a new or existing challenges service.
+ * @param {String} tokenV5 Optional. Auth token for Topcoder API v5.
+ * @return {DashboardService} Dashboard service object
+ */
+let lastInstance = null;
+export function getService(tokenV5) {
+ if (!lastInstance || tokenV5 !== lastInstance.private.tokenV5) {
+ lastInstance = new DashboardService(tokenV5);
+ }
+ return lastInstance;
+}
+
+/* Using default export would be confusing in this case. */
+export default undefined;
diff --git a/src/shared/services/recruitCRM.js b/src/shared/services/recruitCRM.js
index 10e54a7898..b98284ba2c 100644
--- a/src/shared/services/recruitCRM.js
+++ b/src/shared/services/recruitCRM.js
@@ -75,8 +75,9 @@ export default class Service {
* applyForJob for candidate
* @param {string} id The job id to apply to
* @param {object} payload The apply payload
+ * @param {string} tokenV3 User token
*/
- async applyForJob(id, payload) {
+ async applyForJob(id, payload, tokenV3) {
const { resume } = payload;
const data = new FormData();
data.append('resume', resume);
@@ -84,6 +85,9 @@ export default class Service {
const res = await fetch(`${this.baseUrl}/jobs/${id}/apply`, {
method: 'POST',
body: data,
+ headers: new Headers({
+ Authorization: `Bearer ${tokenV3}`,
+ }),
});
if (!res.ok) {
const error = new Error('Failed to apply for job');
@@ -104,4 +108,17 @@ export default class Service {
}
return res.json();
}
+
+ /**
+ * Get TAAS jobs
+ * @param {object} query The request query
+ */
+ async getTaasJobs(query) {
+ const res = await fetch(`${this.baseUrl}/taasjobs?${qs.stringify(query)}`);
+ if (!res.ok) {
+ const error = new Error('Failed to get taas jobs');
+ logger.error(error, res);
+ }
+ return res.json();
+ }
}
diff --git a/src/styles/_mixins/_variables.scss b/src/styles/_mixins/_variables.scss
index 37b2b33efa..c5bec7709c 100644
--- a/src/styles/_mixins/_variables.scss
+++ b/src/styles/_mixins/_variables.scss
@@ -13,3 +13,11 @@ $member-red: #ea1900;
/* recommendation tag color */
$recommendation-bg: #cef0af;
+
+/* dashboard colors */
+$dashboard-teal: #219174;
+$dashboard-dark-card-bg: #363636;
+$dashboard-dark-link: #5fb7ee;
+$dashboard-dark-bg: #2a2a2a;
+$dashboard-light-link: #0d61bf;
+$dashboard-light-card-bg: #fbfbfb;
diff --git a/src/test/jmeter/path.csv b/src/test/jmeter/path.csv
index ab5247b543..1a045c243e 100644
--- a/src/test/jmeter/path.csv
+++ b/src/test/jmeter/path.csv
@@ -1,5 +1,5 @@
path
-/my-dashboard
+/home
/members/codejam
/challenges
/community/arena