From abc831ee7ccadf2e8db4fdf92f959de0186c16af Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 27 Apr 2020 19:23:17 +0300 Subject: [PATCH 01/12] Fix for #4286 --- .../challenge-detail/Specification/index.jsx | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/shared/components/challenge-detail/Specification/index.jsx b/src/shared/components/challenge-detail/Specification/index.jsx index 3f425545c8..5c9af5d04a 100644 --- a/src/shared/components/challenge-detail/Specification/index.jsx +++ b/src/shared/components/challenge-detail/Specification/index.jsx @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /* Component renders challenge details and specifications */ @@ -468,41 +469,21 @@ export default function ChallengeDetailsView(props) { ) : (

- Topcoder will compensate members in accordance with our - standard payment policies, unless otherwise specified in this - challenge. For information on payment policies, setting up your - profile to receive payments, and general payment questions, - please refer to + Topcoder will compensate members in accordance with our standard payment policies, unless + otherwise specified in this challenge. For information on payment policies, setting up your profile to + receive payments, and general payment questions, please refer to ‌ - https://www.topcoder.com/thrive/articles/Payment%20Policies%20and%20Instructions - + Payment Policies and Instructions + .

) } -
-

- Reliability Rating and Bonus -

-

- For challenges that have a reliability bonus, the bonus depends - on the reliability rating at the moment of registration for that - project. A participant with no previous projects is considered to - have no reliability rating, and therefore gets no bonus. - Reliability bonus does not apply to Digital Run winnings. Since - reliability rating is based on the past 15 projects, it can only - have 15 discrete values. -
- - Read more. - -

-
Date: Tue, 28 Apr 2020 10:55:09 +0300 Subject: [PATCH 02/12] Fixes for #3972 --- src/shared/routes/Topcoder/Routes.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/routes/Topcoder/Routes.jsx b/src/shared/routes/Topcoder/Routes.jsx index eca52ee957..3df86d9ebb 100644 --- a/src/shared/routes/Topcoder/Routes.jsx +++ b/src/shared/routes/Topcoder/Routes.jsx @@ -117,7 +117,7 @@ export default function Topcoder() { { From bdd4fd5d0c25db683239eb36459b5c856121d100 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Tue, 28 Apr 2020 12:19:43 +0300 Subject: [PATCH 03/12] Fix addon for #3972 --- src/shared/routes/Topcoder/Routes.jsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/shared/routes/Topcoder/Routes.jsx b/src/shared/routes/Topcoder/Routes.jsx index 3df86d9ebb..0fb17a5b1a 100644 --- a/src/shared/routes/Topcoder/Routes.jsx +++ b/src/shared/routes/Topcoder/Routes.jsx @@ -117,12 +117,21 @@ export default function Topcoder() { { if (_.isEmpty(data.entries.items)) return ; - const id = data.entries.matches[0].items[0]; + let id = data.entries.matches[0].items[0]; + if (data.entries.matches[0].total !== 1) { + // more than 1 match. we need to try find best + const mId = _.findKey( + data.entries.items, + // eslint-disable-next-line max-len + o => o.fields.title.toLocaleLowerCase() === articleTitle.toLocaleLowerCase(), + ); + id = mId || id; + } const { externalArticle, contentUrl } = data.entries.items[id].fields; if (externalArticle && contentUrl && isomorphy.isClientSide()) { window.location.href = contentUrl; From 13cc53817045b7c1bc15d76e3dff60e00e272e02 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Tue, 28 Apr 2020 13:36:11 +0300 Subject: [PATCH 04/12] Fix for #3960 --- .../Contentful/SearchBar/SearchBar.jsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/shared/components/Contentful/SearchBar/SearchBar.jsx b/src/shared/components/Contentful/SearchBar/SearchBar.jsx index 3e3c53b58a..371118f87b 100644 --- a/src/shared/components/Contentful/SearchBar/SearchBar.jsx +++ b/src/shared/components/Contentful/SearchBar/SearchBar.jsx @@ -55,6 +55,7 @@ export class SearchBarInner extends Component { this.getDropdownPopup = this.getDropdownPopup.bind(this); this.getSuggestionList = this.getSuggestionList.bind(this); this.handleSearchChange = this.handleSearchChange.bind(this); + this.handleClickOutside = this.handleClickOutside.bind(this); // using debounce to avoid processing or requesting too much this.updateSuggestionListWithNewSearch = _.debounce( this.updateSuggestionListWithNewSearch.bind(this), 400, @@ -66,6 +67,10 @@ export class SearchBarInner extends Component { this.apiService = getService({ spaceName: 'EDU' }); } + componentWillUnmount() { + document.removeEventListener('mousedown', this.handleClickOutside); + } + /** * Set the search field ref */ @@ -317,6 +322,13 @@ export class SearchBarInner extends Component { )); } + handleClickOutside(e) { + if (this.popupSearchResultRef && !this.popupSearchResultRef.contains(e.target)) { + this.setState({ isShowSuggestion: false }); + document.removeEventListener('mousedown', this.handleClickOutside); + } + } + /** * Update size of popup search result */ @@ -453,14 +465,10 @@ export class SearchBarInner extends Component { ref={this.setSearchFieldRef} type="text" placeholder="Search..." - onBlur={() => { - _.delay(() => { - this.setState({ isShowSuggestion: false }); - }, 100); - }} onFocus={(e) => { this.updateSuggestionListWithNewSearch(e.target.value); this.setState({ isShowSuggestion: true, isShowFilterPopup: false }); + document.addEventListener('mousedown', this.handleClickOutside); }} onChange={this.handleSearchChange} /> From 4b8016abee5c3ecaeb8ad937e8c2d6e1110f81ff Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 29 Apr 2020 17:25:05 +0300 Subject: [PATCH 05/12] Implement #4050 --- .../ChallengeHistoryModal/index.jsx | 192 ++++++++++------ .../ChallengeHistoryModal/styles.scss | 207 +++++++++++------- 2 files changed, 261 insertions(+), 138 deletions(-) diff --git a/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx b/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx index fd8f7deb99..7fea82efdb 100644 --- a/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx +++ b/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx @@ -1,77 +1,147 @@ -import React from 'react'; -import { Modal, PrimaryButton } from 'topcoder-react-ui-kit'; +import React, { Component } from 'react'; +import { Modal } from 'topcoder-react-ui-kit'; import PT from 'prop-types'; import LoadingIndicator from 'components/LoadingIndicator'; import { config } from 'topcoder-react-utils'; +import cn from 'classnames'; +import _ from 'lodash'; import theme from './styles.scss'; import PodiumSpot from '../PodiumSpot'; +class ChallengeHistoryModal extends Component { + constructor(props) { + super(props); -function ChallengeHistoryModal({ - challenges, - competitor, - onCancel, - loading, - isCopilot, - isAlgo, -}) { - return ( - -

- Completed Challenges History -

-
- -
-
-
- Challenge Name -
- { - !isCopilot ? ( -
Placement
- ) : null - } -
- TCO Points + this.state = { + sortParam: { + order: '', + field: '', + }, + }; + } + + render() { + const { + challenges, + competitor, + onCancel, + loading, + isCopilot, + isAlgo, + } = this.props; + const { sortParam } = this.state; + const challengesOrdered = _.orderBy(challenges, [sortParam.field], [sortParam.order]); + + return ( + +

+ Completed Challenges History +

+
+
-
-
- { - challenges.map(challenge => ( -
- + + + + { !isCopilot ? ( -
{challenge.place}
+ ) : null } -
- {challenge.points} -
- - )) + + + + + { + challengesOrdered.map(challenge => ( + + + { + !isCopilot ? ( + + ) : null + } + + + )) + } + +
Challenge Name +
+ Placement + +
+
+
+ TCO Points + +
+
+ + {challenge.challenge_name || challenge.challenge_id} + + {challenge.place} + {challenge.points} +
+ { + loading ? : null } -
- { - loading ? : null - } -
- - Close - -
- - ); +
+ +
+ + ); + } } const CHALLENGES_TYPE = PT.arrayOf(PT.shape({ diff --git a/src/shared/components/Leaderboard/ChallengeHistoryModal/styles.scss b/src/shared/components/Leaderboard/ChallengeHistoryModal/styles.scss index 6ef72af9aa..a0eb6e36df 100644 --- a/src/shared/components/Leaderboard/ChallengeHistoryModal/styles.scss +++ b/src/shared/components/Leaderboard/ChallengeHistoryModal/styles.scss @@ -1,75 +1,60 @@ @import "~styles/mixins"; -.col-1 { - padding: 0 30px; - width: 55%; - - .challenge-name { - color: $tc-gray-90; - font-weight: 700; - } - - @media (max-width: 768px) { - padding-left: 2 * $base-unit; - } -} - -.col-2 { - width: 22.5%; - text-align: center; -} - -.col-3 { - color: $tc-gray-40; - width: 22.5%; - text-align: center; -} - -.body { - border: $tc-gray-10 1px solid; -} - -.buttons { - margin-top: 10px; - display: flex; - flex-direction: row; - justify-content: center; -} +$light-gray: #d4d4d4; .container { @include roboto-regular; color: $tc-gray-50; background: $tc-white; - border-radius: 3 * $corner-radius 3 * $corner-radius; + border-radius: 10px; top: 50%; width: 70%; max-height: 90%; overflow-y: auto; + padding: 80px 78px; @media (max-width: 768px) { width: 80%; } - h1 { - color: $tc-gray-80; + h3 { + color: #1e94a3; + font-family: BarlowCondensed, sans-serif; + font-size: 34px; + font-weight: 500; + line-height: 38px; text-align: center; - font-size: 22px; - font-weight: 400; - margin-bottom: 10px; + margin-bottom: 60px; + text-transform: uppercase; @media (max-width: 768px) { - font-size: 4vw; + font-size: 31px !important; + font-weight: 500 !important; + line-height: 33px !important; + margin-bottom: 30px; } } } +.overlay { + background-color: #2a2a2a; + opacity: 0.95; + border: none; + height: 100%; + left: 0; + outline: none; + position: fixed; + top: 0; + width: 100%; + z-index: 998; +} + .podium-spot-wrapper { text-align: center; display: flex; justify-content: center; - margin-top: 15px; - margin-bottom: 1em; + margin-bottom: 50px; > div > span { height: 128px; @@ -86,51 +71,119 @@ } } -.head { - background: $tc-gray-neutral-light; - border: $tc-gray-10 1px solid; - border-bottom: none; - color: $tc-gray-40; - font-weight: 400; - font-size: 13px; - line-height: 15px; - padding: 12.5px 0; +.history-table { + width: 100%; + margin-bottom: 62px; + + thead { + th { + color: #2a2a2a; + font-family: Roboto, sans-serif; + font-size: 14px; + font-weight: 500; + letter-spacing: 0.5px; + line-height: 18px; + text-align: left; + text-transform: uppercase; + border-bottom: 1px solid #d4d4d4; + padding-bottom: 15px; + } + } + + .row { + border-bottom: 1px solid #d4d4d4; + + .name { + .link { + color: #0d61bf; + font-size: 14px; + font-weight: 400; + line-height: 51px; + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } + } + + .placement { + color: #2a2a2a; + font-size: 14px; + font-weight: 500; + line-height: 51px; + } + + .points { + color: #2a2a2a; + font-size: 14px; + font-weight: 400; + line-height: 51px; + } + } +} + +.buttons { + margin-top: 10px; display: flex; + flex-direction: row; + justify-content: center; - @media (max-width: 768px) { - font-size: 3vw; - padding: 2 * $base-unit 0; + .close-btn { + color: #fafafb; + font-family: Roboto, sans-serif; + font-size: 14px; + font-weight: 700; + letter-spacing: 0.8px; + line-height: 40px; + background-color: #137d60; + border-radius: 20px; + border: none; + text-transform: uppercase; + padding: 0 20px; + + &:hover { + background-color: #0ab88a !important; + box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2); + } } } -.row { +.header-table-content { display: flex; align-items: center; - line-height: 40px; - color: $tc-gray-70; - font-size: 15px; - border-bottom: 1px solid $tc-gray-10; +} - @media (max-width: 768px) { - font-size: 3vw; - } +.sort-container { + display: flex; + flex-direction: column; + margin-left: 5px; + padding: 0; + border: none; + outline: none; + background: transparent; } -.title { - color: $tc-gray-50; - font-size: 13px; - line-height: 15px; - font-weight: 500; +.sort-container > div { + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; +} + +.sort-up { + border-bottom: 4px solid $light-gray; + margin-bottom: 2px; + + &.active { + border-bottom: 4px solid $tc-black; + } } -.link { - color: #0681ff; - display: block; - line-height: 1.5; +.sort-down { + border-top: 4px solid $light-gray; - &:visited, - &:hover, - &:active { - color: #0681ff; + &.active { + border-top: 4px solid $tc-black; } } From 2fd6475d02ca264a79ce97da8c77ea034a1b0518 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 30 Apr 2020 11:50:40 +0300 Subject: [PATCH 06/12] Implements #4058 --- .../components/NewsletterArchive/index.jsx | 126 +++++++++++++++--- .../components/NewsletterArchive/style.scss | 92 +++++++++++++ 2 files changed, 196 insertions(+), 22 deletions(-) diff --git a/src/shared/components/NewsletterArchive/index.jsx b/src/shared/components/NewsletterArchive/index.jsx index f38a745761..7e4c9cc089 100644 --- a/src/shared/components/NewsletterArchive/index.jsx +++ b/src/shared/components/NewsletterArchive/index.jsx @@ -1,36 +1,118 @@ import _ from 'lodash'; import moment from 'moment'; -import React, { Fragment } from 'react'; +import React, { Component } from 'react'; import PT from 'prop-types'; import { themr } from 'react-css-super-themr'; +import cn from 'classnames'; import defaultStyle from './style.scss'; /* Date/time format to use in the link. */ -const FORMAT = 'MMM DD, HH:mm'; +const FORMAT = 'MMM DD, YYYY'; -function NewsletterArchive({ - archive, -}) { -// console.log(archive) - return _.map( - archive.campaigns, - (link, indx) => ( - - - {link.settings.title} - - Sent: {moment(link.send_time).format(FORMAT)} - - ), - ); -} +class NewsletterArchive extends Component { + constructor(props) { + super(props); -NewsletterArchive.defaultProps = { - token: null, -}; + this.state = { + sortParam: { + order: '', + field: '', + }, + }; + } + + render() { + const { + archive, + } = this.props; + const { sortParam } = this.state; + const archiveOrdered = _.orderBy(archive.campaigns, [sortParam.field], [sortParam.order]); + + return ( + + + + + + + + + + { + archiveOrdered.map((archiveItem, indx) => ( + + + + + + )) + } + +
Item +
+ NEWSLETTER + +
+
+
+ SEND DATE + +
+
{indx + 1} + + {archiveItem.settings.title} + + + {moment(archiveItem.send_time).format(FORMAT)} +
+ ); + } +} NewsletterArchive.propTypes = { - archive: PT.shape().isRequired, + archive: PT.arrayOf().isRequired, }; export default themr('NewsletterArchive', defaultStyle)(NewsletterArchive); diff --git a/src/shared/components/NewsletterArchive/style.scss b/src/shared/components/NewsletterArchive/style.scss index e55b8bff9d..6c0292052c 100644 --- a/src/shared/components/NewsletterArchive/style.scss +++ b/src/shared/components/NewsletterArchive/style.scss @@ -1,5 +1,7 @@ @import "~styles/mixins"; +$light-gray: #d4d4d4; + .archive-link { display: block; font-size: 15px; @@ -20,3 +22,93 @@ margin-bottom: 10px; display: block; } + +.history-table { + width: 100%; + margin-bottom: 62px; + + thead { + th { + color: #2a2a2a; + font-family: Roboto, sans-serif; + font-size: 14px; + font-weight: 500; + letter-spacing: 0.5px; + line-height: 18px; + text-align: left; + text-transform: uppercase; + border-bottom: 1px solid #d4d4d4; + padding-bottom: 15px; + } + } + + .row { + border-bottom: 1px solid #d4d4d4; + + td { + padding: 0; + border-bottom: 1px solid #d4d4d4; + } + + .name { + .archive-link { + color: #0d61bf; + font-size: 14px; + font-weight: 500; + line-height: 51px; + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } + } + + .index, + .sent-date { + color: #2a2a2a; + font-size: 14px; + font-weight: 400; + line-height: 51px; + } + } +} + +.header-table-content { + display: flex; + align-items: center; +} + +.sort-container { + display: flex; + flex-direction: column; + margin-left: 5px; + padding: 0; + border: none; + outline: none; + background: transparent; +} + +.sort-container > div { + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; +} + +.sort-up { + border-bottom: 4px solid $light-gray; + margin-bottom: 2px; + + &.active { + border-bottom: 4px solid $tc-black; + } +} + +.sort-down { + border-top: 4px solid $light-gray; + + &.active { + border-top: 4px solid $tc-black; + } +} From 6dce16fcb253f621a9b2a2f2b06215e63da54f40 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 30 Apr 2020 15:07:45 +0300 Subject: [PATCH 07/12] Fix TCO Points to Points --- .../components/Leaderboard/ChallengeHistoryModal/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx b/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx index 7fea82efdb..883ebf67ad 100644 --- a/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx +++ b/src/shared/components/Leaderboard/ChallengeHistoryModal/index.jsx @@ -82,7 +82,7 @@ class ChallengeHistoryModal extends Component { }
- TCO Points + Points
About Community Changelog Talk to Sales + Terms
From 2e2a0edc8290ee8486ac4423f4a89fa00f4f4c78 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 7 May 2020 15:23:22 +0300 Subject: [PATCH 11/12] Implement #4165 --- .../Settings/Preferences/Email/index.jsx | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/shared/components/Settings/Preferences/Email/index.jsx b/src/shared/components/Settings/Preferences/Email/index.jsx index b8eae9fa7b..cbe54d4f12 100644 --- a/src/shared/components/Settings/Preferences/Email/index.jsx +++ b/src/shared/components/Settings/Preferences/Email/index.jsx @@ -1,15 +1,23 @@ /** * Email Preferences component. */ -import { debounce, isEqual } from 'lodash'; +import { debounce, isEqual, map } from 'lodash'; import React from 'react'; import PT from 'prop-types'; import ConsentComponent from 'components/Settings/ConsentComponent'; +import ToggleableItem from 'components/Settings/ToggleableItem'; import './styles.scss'; const SAVE_DELAY = 1000; +const newsletters = [ + { + id: 'TOPCODER_NL_GEN', + name: 'General Newsletter', + desc: 'News summary from Topcoder', + }, +]; export default class EmailPreferences extends ConsentComponent { saveEmailPreferences = debounce(() => { @@ -77,11 +85,33 @@ export default class EmailPreferences extends ConsentComponent { } render() { + const { profileState: { emailPreferences } } = this.props; return (

E-Mail Preferences

+
+ { + this.shouldRenderConsent() && this.renderConsent() + } + { + map(newsletters, (newsletter) => { + const checked = emailPreferences ? emailPreferences[newsletter.id] : false; + return ( + this.onHandleChange(newsletter.id, e.target.checked)} + /> + ); + }) + } +
); } From 7cf1c6d135ed2413870a296b7dbbe27dba45edff Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 7 May 2020 17:17:54 +0300 Subject: [PATCH 12/12] Implements #4354 --- .../components/Contentful/Article/Article.jsx | 4 +- .../Contentful/ArticleCard/ArticleCard.jsx | 2 +- .../Contentful/SearchBar/SearchBar.jsx | 10 ++-- .../challenge-detail/ThriveArticles/index.jsx | 2 +- src/shared/routes/Topcoder/Routes.jsx | 52 +++++++++++++++---- 5 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/shared/components/Contentful/Article/Article.jsx b/src/shared/components/Contentful/Article/Article.jsx index c0ef0eae71..809f7c909c 100644 --- a/src/shared/components/Contentful/Article/Article.jsx +++ b/src/shared/components/Contentful/Article/Article.jsx @@ -250,7 +250,7 @@ export default class Article extends React.Component { {subData.entries.items[rec.sys.id].fields.title} ) : ( - + {subData.entries.items[rec.sys.id].fields.title} ) @@ -278,7 +278,7 @@ export default class Article extends React.Component { Read More ) : ( - + Read More ) diff --git a/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx b/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx index 59a6e794bd..d50230a139 100644 --- a/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx +++ b/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx @@ -67,7 +67,7 @@ class ArticleCard extends React.Component { // determine if article cards will redirect to external link or article details page const articlePageUrl = article.externalArticle && article.contentUrl ? article.contentUrl - : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${article.title}`; + : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${article.slug || article.title}`; const articlePageTarget = article.externalArticle && article.contentUrl ? '_blank' : '_self'; diff --git a/src/shared/components/Contentful/SearchBar/SearchBar.jsx b/src/shared/components/Contentful/SearchBar/SearchBar.jsx index 371118f87b..9444c56777 100644 --- a/src/shared/components/Contentful/SearchBar/SearchBar.jsx +++ b/src/shared/components/Contentful/SearchBar/SearchBar.jsx @@ -169,13 +169,13 @@ export class SearchBarInner extends Component { className={theme['group-cell']} onClick={() => { window.location.href = (item.externalArticle && item.contentUrl) - ? item.contentUrl : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${item.title}`; + ? item.contentUrl : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${item.slug || item.title}`; }} onKeyPress={_.noop} > { e.nativeEvent.stopImmediatePropagation(); @@ -220,13 +220,13 @@ export class SearchBarInner extends Component { className={theme['group-cell']} onClick={() => { window.location.href = (item.externalArticle && item.contentUrl) - ? item.contentUrl : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${item.title}`; + ? item.contentUrl : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${item.slug || item.title}`; }} onKeyPress={_.noop} > { e.nativeEvent.stopImmediatePropagation(); @@ -272,7 +272,7 @@ export class SearchBarInner extends Component { > diff --git a/src/shared/components/challenge-detail/ThriveArticles/index.jsx b/src/shared/components/challenge-detail/ThriveArticles/index.jsx index e2f3013320..05407eab2d 100644 --- a/src/shared/components/challenge-detail/ThriveArticles/index.jsx +++ b/src/shared/components/challenge-detail/ThriveArticles/index.jsx @@ -20,7 +20,7 @@ export default function ThriveArticles({ articles }) { const getPageUrl = article => (article.externalArticle && article.contentUrl ? article.contentUrl - : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${article.title}`); + : `${config.TC_EDU_BASE_PATH}${config.TC_EDU_ARTICLES_PATH}/${article.slug || article.title}`); const items = map(articles, (a, idx) => (
diff --git a/src/shared/routes/Topcoder/Routes.jsx b/src/shared/routes/Topcoder/Routes.jsx index 0fb17a5b1a..46fa2b6331 100644 --- a/src/shared/routes/Topcoder/Routes.jsx +++ b/src/shared/routes/Topcoder/Routes.jsx @@ -117,21 +117,53 @@ export default function Topcoder() { { - if (_.isEmpty(data.entries.items)) return ; - let id = data.entries.matches[0].items[0]; - if (data.entries.matches[0].total !== 1) { - // more than 1 match. we need to try find best - const mId = _.findKey( - data.entries.items, - // eslint-disable-next-line max-len - o => o.fields.title.toLocaleLowerCase() === articleTitle.toLocaleLowerCase(), + if (_.isEmpty(data.entries.items)) { + // try search by title match + // this legacy support should be deprecated when all + // Thrive links switched to hypens, someday + return ( + { + if (_.isEmpty(dataTitle.entries.items)) return ; + let id = dataTitle.entries.matches[0].items[0]; + if (dataTitle.entries.matches[0].total !== 1) { + // more than 1 match. we need to try find best + const mId = _.findKey( + dataTitle.entries.items, + // eslint-disable-next-line max-len + o => o.fields.title.toLocaleLowerCase() === articleTitle.toLocaleLowerCase(), + ); + id = mId || id; + } + const { + externalArticle, + contentUrl, + } = dataTitle.entries.items[id].fields; + if (externalArticle && contentUrl && isomorphy.isClientSide()) { + window.location.href = contentUrl; + return null; + } + return ( +
+ ); + }} + renderPlaceholder={LoadingIndicator} + /> ); - id = mId || id; } + const id = data.entries.matches[0].items[0]; const { externalArticle, contentUrl } = data.entries.items[id].fields; if (externalArticle && contentUrl && isomorphy.isClientSide()) { window.location.href = contentUrl;