- { !isDataScience && (!challenge.isPrivate
+ { !challenge.isPrivate
? (
{ challenge.name }
- ) : (
+ )
+ : (
{ challenge.name }
- )) }
-
- { isDataScience
- && (
-
- { challenge.name }
-
- ) }
+ )}
{underscoreReplace(type)}
diff --git a/src/shared/components/Contentful/Article/Article.jsx b/src/shared/components/Contentful/Article/Article.jsx
index 150d213290..c9078713ea 100644
--- a/src/shared/components/Contentful/Article/Article.jsx
+++ b/src/shared/components/Contentful/Article/Article.jsx
@@ -15,7 +15,10 @@ import LoadingIndicator from 'components/LoadingIndicator';
import YouTubeVideo from 'components/YouTubeVideo';
import moment from 'moment';
import localStorage from 'localStorage';
-import { config, Link, isomorphy } from 'topcoder-react-utils';
+import { Helmet } from 'react-helmet';
+import {
+ config, Link, isomorphy,
+} from 'topcoder-react-utils';
import qs from 'qs';
// SVGs and assets
import GestureIcon from 'assets/images/icon-gesture.svg';
@@ -110,9 +113,27 @@ export default class Article extends React.Component {
if (isomorphy.isClientSide()) {
shareUrl = encodeURIComponent(window.location.href);
}
+ const description = htmlToText.fromString(
+ ReactDOMServer.renderToString(markdown(fields.content)),
+ {
+ ignoreHref: true,
+ ignoreImage: true,
+ singleNewLineParagraphs: true,
+ uppercaseHeadings: false,
+ },
+ ).substring(0, CONTENT_PREVIEW_LENGTH);
return (
+
+ {fields.title}
+
+
+
+
+
+
+
{/* Banner */}
{
fields.featuredImage ? (
diff --git a/src/shared/components/Contentful/SearchBar/SearchBar.jsx b/src/shared/components/Contentful/SearchBar/SearchBar.jsx
index 9444c56777..d23301d0ed 100644
--- a/src/shared/components/Contentful/SearchBar/SearchBar.jsx
+++ b/src/shared/components/Contentful/SearchBar/SearchBar.jsx
@@ -56,6 +56,7 @@ export class SearchBarInner extends Component {
this.getSuggestionList = this.getSuggestionList.bind(this);
this.handleSearchChange = this.handleSearchChange.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
// using debounce to avoid processing or requesting too much
this.updateSuggestionListWithNewSearch = _.debounce(
this.updateSuggestionListWithNewSearch.bind(this), 400,
@@ -71,6 +72,29 @@ export class SearchBarInner extends Component {
document.removeEventListener('mousedown', this.handleClickOutside);
}
+ onKeyDown(e) {
+ const { inputlVal, selectedFilter } = this.state;
+ if (inputlVal && e.which === 13) {
+ const searchQuery = {};
+ if (this.searchFieldRef && this.searchFieldRef.value) {
+ if (selectedFilter.name === 'Tags') {
+ searchQuery.tags = [this.searchFieldRef.value];
+ }
+ if (selectedFilter.name === 'All') {
+ searchQuery.phrase = this.searchFieldRef.value;
+ }
+ if (selectedFilter.name === 'Title') {
+ searchQuery.title = this.searchFieldRef.value;
+ }
+ }
+ if (selectedFilter.name !== 'Author') {
+ window.location.href = `${config.TC_EDU_BASE_PATH}${config.TC_EDU_SEARCH_PATH}?${qs.stringify(searchQuery)}`;
+ } else {
+ window.location.href = `${config.TC_EDU_BASE_PATH}${config.TC_EDU_SEARCH_PATH}?author=${inputlVal}`;
+ }
+ }
+ }
+
/**
* Set the search field ref
*/
@@ -136,6 +160,7 @@ export class SearchBarInner extends Component {
isShowSuggestion,
suggestionList,
selectedFilter,
+ noResults,
} = this.state;
const searchQuery = {};
@@ -151,7 +176,8 @@ export class SearchBarInner extends Component {
}
}
- return (suggestionList && !_.isEmpty(suggestionList) && isShowSuggestion && (
+ // eslint-disable-next-line no-nested-ternary
+ return suggestionList && !_.isEmpty(suggestionList) && isShowSuggestion ? (
- ));
+ ) : noResults ? (
+
+ No Results
+
+ ) : null;
}
handleClickOutside(e) {
@@ -379,7 +412,10 @@ export class SearchBarInner extends Component {
.then((results) => {
// Nothing found?
if (!results.total) {
- this.setState({ suggestionList: {} });
+ this.setState({
+ suggestionList: {},
+ noResults: true,
+ });
return;
}
// Author query?
@@ -387,7 +423,7 @@ export class SearchBarInner extends Component {
const suggestionList = {
Author: _.map(results.items, item => ({ ...item.fields })),
};
- this.setState({ suggestionList });
+ this.setState({ suggestionList, noResults: false });
return;
}
// ALL && Tags
@@ -395,7 +431,7 @@ export class SearchBarInner extends Component {
this.setState({ suggestionList });
});
} else {
- this.setState({ suggestionList: {} });
+ this.setState({ suggestionList: {}, noResults: false });
}
}
@@ -419,6 +455,7 @@ export class SearchBarInner extends Component {
contentAuthor: contentAuthor.fields,
externalArticle: fields.externalArticle,
contentUrl: fields.contentUrl,
+ slug: fields.slug,
};
}),
'type',
@@ -471,6 +508,7 @@ export class SearchBarInner extends Component {
document.addEventListener('mousedown', this.handleClickOutside);
}}
onChange={this.handleSearchChange}
+ onKeyDown={this.onKeyDown}
/>
diff --git a/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx b/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx
index 2ed2b5684d..4866ffdd4e 100644
--- a/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx
+++ b/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx
@@ -6,9 +6,7 @@ import PT from 'prop-types';
import SwitchWithLabel from 'components/SwitchWithLabel';
import { challenge as challengeUtils } from 'topcoder-react-lib';
import { COMPETITION_TRACKS as TRACKS } from 'utils/tc';
-import _ from 'lodash';
-import localStorage from 'localStorage';
import ChallengeSearchBar from './ChallengeSearchBar';
import EditTrackPanel from './EditTrackPanel';
import FiltersIcon from './FiltersSwitch/filters-icon.svg';
@@ -54,8 +52,6 @@ export default function ChallengeFilters({
const switchTrack = (track, on) => {
const act = on ? Filter.addTrack : Filter.removeTrack;
const filterObj = act(filterState, track);
- const newFilterObj = _.pick(filterObj, 'tracks');
- localStorage.setItem('trackStatus', JSON.stringify(newFilterObj));
setFilterState(filterObj);
};
diff --git a/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx b/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx
index a3320e0735..a5f71d344b 100644
--- a/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx
+++ b/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx
@@ -349,7 +349,6 @@ export default function FiltersPanel({
setFilterState({});
selectCommunity(defaultCommunityId);
setSearchText('');
- localStorage.setItem('trackStatus', JSON.stringify({}));
}}
size="sm"
theme={{ button: style.button }}
diff --git a/src/shared/components/challenge-listing/Sidebar/BucketSelector/index.jsx b/src/shared/components/challenge-listing/Sidebar/BucketSelector/index.jsx
index 3f267d87c3..66d7ea6988 100644
--- a/src/shared/components/challenge-listing/Sidebar/BucketSelector/index.jsx
+++ b/src/shared/components/challenge-listing/Sidebar/BucketSelector/index.jsx
@@ -15,7 +15,8 @@ import './style.scss';
const Filter = challengeUtils.filter;
-const RSS_LINK = 'http://feeds.topcoder.com/challenges/feed?list=active&contestType=all';
+// DISABLED: Until feeds.topcoder.com domain fixed community-app#4606
+// const RSS_LINK = 'http://feeds.topcoder.com/challenges/feed?list=active&contestType=all';
export default function BucketSelector({
activeBucket,
@@ -109,11 +110,14 @@ export default function BucketSelector({
) : ''
}
+ {/* DISABLED: Until feeds.topcoder.com domain fixed community-app#4606 */}
+ {/*
+ */}
);
}
diff --git a/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx b/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx
index 432afcde8b..65aac9c5f9 100644
--- a/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx
+++ b/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx
@@ -97,17 +97,17 @@ function Tip(props) {
const allPhases = c.phases || [];
const endPhaseDate = Math.max(...allPhases.map(d => phaseEndDate(d)));
- const registrationPhase = allPhases.find(phase => phase.name === 'Registration');
- const submissionPhase = allPhases.find(phase => phase.name === 'Submission');
- const checkpointPhase = allPhases.find(phase => phase.name === 'Checkpoint Submission');
+ const registrationPhase = allPhases.find(phase => phase.name === 'Registration') || {};
+ const submissionPhase = allPhases.find(phase => phase.name === 'Submission') || {};
+ const checkpointPhase = allPhases.find(phase => phase.name === 'Checkpoint Submission') || {};
- if (registrationPhase) {
+ if (!_.isEmpty(registrationPhase)) {
steps.push({
date: phaseStartDate(registrationPhase),
name: 'Start',
});
}
- if (checkpointPhase) {
+ if (!_.isEmpty(checkpointPhase)) {
steps.push({
date: phaseEndDate(checkpointPhase),
name: 'Checkpoint',
@@ -119,7 +119,7 @@ function Tip(props) {
date: phaseEndDate(iterativeReviewPhase),
name: 'Iterative Review',
});
- } else if (submissionPhase) {
+ } else if (!_.isEmpty(submissionPhase)) {
steps.push({
date: phaseEndDate(submissionPhase),
name: 'Submission',
diff --git a/src/shared/containers/EDU/Home.jsx b/src/shared/containers/EDU/Home.jsx
index 275efaab6c..30c8455a26 100644
--- a/src/shared/containers/EDU/Home.jsx
+++ b/src/shared/containers/EDU/Home.jsx
@@ -3,6 +3,7 @@
*/
import React from 'react';
import { config } from 'topcoder-react-utils';
+import { Helmet } from 'react-helmet';
import Viewport from 'components/Contentful/Viewport';
import SearchBar from 'components/Contentful/SearchBar/SearchBar';
import { getService } from 'services/contentful';
@@ -45,6 +46,13 @@ export default class EDUHome extends React.Component {
const { taxonomy } = this.state;
return (
+
+ THRIVE - Grow with us. Tutorials and workshops that matter.
+
+
+
+
+
{/* Banner */}
diff --git a/src/shared/containers/EDU/Search.jsx b/src/shared/containers/EDU/Search.jsx
index b49128c77a..6b311aa68e 100644
--- a/src/shared/containers/EDU/Search.jsx
+++ b/src/shared/containers/EDU/Search.jsx
@@ -13,6 +13,7 @@ import { updateQuery } from 'utils/url';
import qs from 'qs';
import LoadingIndicator from 'components/LoadingIndicator';
import SearchPageFilter from 'components/Contentful/SearchPageFilter/SearchPageFilter';
+import { Helmet } from 'react-helmet';
// Partials
import ResultTabs from './partials/ResultTabs';
// CSS
@@ -91,6 +92,13 @@ export default class EDUSearch extends React.Component {
if (!taxonomy) return
;
return (
+
+ THRIVE - Search {`${query.title}`}
+
+
+
+
+
{/* Banner */}
diff --git a/src/shared/containers/EDU/Tracks.jsx b/src/shared/containers/EDU/Tracks.jsx
index 0a1b4c8398..d98099191e 100644
--- a/src/shared/containers/EDU/Tracks.jsx
+++ b/src/shared/containers/EDU/Tracks.jsx
@@ -14,6 +14,7 @@ import qs from 'qs';
import TracksTree from 'components/Contentful/TracksTree/TracksTree';
import LoadingIndicator from 'components/LoadingIndicator';
import TracksFilter from 'components/Contentful/TracksFilter/TracksFilter';
+import { Helmet } from 'react-helmet';
// SVGs & Assets
import Dev from 'assets/images/img-development.png';
import Design from 'assets/images/img_design.png';
@@ -167,6 +168,13 @@ export default class EDUTracks extends React.Component {
if (!taxonomy) return
;
return (
+
+ THRIVE - {`${query.track}`}
+
+
+
+
+
{/* Banner */}
))
- ) : (
Nothing Found
)
+ ) : (
No results found
)
}
{
articles.total > articles.items.length ? (
@@ -193,7 +193,7 @@ export default class ResultTabs extends React.Component {
))
}
- ) : (
Nothing Found
)
+ ) : (
No results found
)
}
{
videos.total > videos.items.length ? (
@@ -225,7 +225,7 @@ export default class ResultTabs extends React.Component {
/>
);
})
- ) : (
Nothing Found
)
+ ) : (
No results found
)
}
{
posts.total > posts.items.length ? (
diff --git a/src/shared/containers/challenge-listing/FilterPanel.jsx b/src/shared/containers/challenge-listing/FilterPanel.jsx
index 7fe064ce44..df1a09565d 100644
--- a/src/shared/containers/challenge-listing/FilterPanel.jsx
+++ b/src/shared/containers/challenge-listing/FilterPanel.jsx
@@ -10,7 +10,6 @@ import shortId from 'shortid';
import FilterPanel from 'components/challenge-listing/Filters/ChallengeFilters';
import PT from 'prop-types';
import React from 'react';
-import localStorage from 'localStorage';
import sidebarActions from 'actions/challenge-listing/sidebar';
import { BUCKETS, isReviewOpportunitiesBucket } from 'utils/challenge-listing/buckets';
import { bindActionCreators } from 'redux';
@@ -64,12 +63,6 @@ export class Container extends React.Component {
const query = qs.parse(window.location.search.slice(1));
if (query.filter && !filterState.track) {
setFilterState(query.filter);
- } else {
- const trackStatus = localStorage.getItem('trackStatus');
- const filterObj = trackStatus ? JSON.parse(trackStatus) : null;
- if (filterObj) {
- setFilterState(filterObj);
- }
}
}
diff --git a/src/shared/routes/index.jsx b/src/shared/routes/index.jsx
index be51690503..0f1b026e2b 100644
--- a/src/shared/routes/index.jsx
+++ b/src/shared/routes/index.jsx
@@ -95,6 +95,10 @@ function Routes({ communityId }) {
component={() =>
}
path="/community/(competitive-programming|data-science|design|development|qa)/how-to-compete"
/>
+
{
// extract the phases from `challenge.phases`,
// as `challenge.registrationStartDate` returned from API is not reliable
- const registrationPhase = find(challenge.phases, p => p.name === 'Registration');
- const submissionPhase = find(challenge.phases, p => p.name === 'Submission');
+ const registrationPhase = find(challenge.phases, p => p.name === 'Registration') || {};
+ const submissionPhase = find(challenge.phases, p => p.name === 'Submission') || {};
// registration phase exists
- if (registrationPhase) {
+ if (!isEmpty(registrationPhase)) {
return moment(phaseStartDate(registrationPhase));
}
// registration phase doesnt exist, This is possibly a F2F or TSK. Take submission phase