From 917eb7190508c41a229b4d8d57bb545b04d51d31 Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Fri, 28 Jul 2017 15:49:26 +0200 Subject: [PATCH 01/13] Update of dependencies It contains a bunch of updates around the codebase, as it seems that the new version of ESLint and AirBnB ESLint config use somewhat different rules for code indendation in some cases. --- README.md | 2 +- __tests__/server/server.js | 28 ++++---- __tests__/server/tc-communities.js | 18 ++--- .../challenge-listing/Listing/Bucket.jsx | 2 +- __tests__/shared/services/api.js | 52 +++++++------- package.json | 18 ++--- src/shared/actions/auth.js | 8 +-- src/shared/actions/challenge-listing/index.js | 20 +++--- .../actions/challenge-listing/sidebar.js | 6 +- src/shared/actions/dashboard.js | 8 +-- src/shared/actions/leaderboard.js | 14 ++-- src/shared/actions/stats.js | 8 +-- src/shared/actions/tc-communities/index.js | 2 +- src/shared/actions/tc-communities/news.js | 4 +- .../components/Dashboard/Header/index.jsx | 2 +- .../Dashboard/SubtrackStats/index.jsx | 60 ++++++++-------- .../components/LeaderboardAvatar/index.jsx | 1 - .../TopcoderHeader/mobile/Menu/index.jsx | 4 +- .../ChallengeCard/Status/index.jsx | 10 +-- .../Filters/ChallengeFilters.jsx | 68 +++++++++---------- .../Filters/DateRangePicker.jsx | 3 +- .../Tooltips/ProgressBarTooltip/index.jsx | 2 +- .../components/challenge-listing/index.jsx | 2 +- .../components/examples/SvgLoading/index.jsx | 2 +- .../components/examples/Themr/index.jsx | 1 - .../tc-communities/ArticleCard/index.jsx | 2 +- .../tc-communities/CommunityStats/index.jsx | 2 +- src/shared/containers/Dashboard/index.jsx | 3 +- src/shared/containers/Leaderboard/index.jsx | 1 - .../containers/SubmissionManagement/index.jsx | 3 +- .../challenge-listing/FilterPanel.jsx | 1 - .../challenge-listing/Listing/index.jsx | 1 - .../containers/challenge-listing/Sidebar.jsx | 1 - .../tc-communities/CommunityStats.jsx | 1 - .../containers/tc-communities/Page/index.jsx | 1 - .../reducers/challenge-listing/index.js | 6 +- src/shared/reducers/challenge.js | 4 +- src/shared/services/api.js | 3 +- src/shared/services/challenges.js | 63 +++++++++-------- src/shared/services/dashboard.js | 13 ++-- src/shared/services/groups.js | 23 +++---- src/shared/services/memberCert.js | 1 - src/shared/services/srm.js | 25 ++++--- src/shared/services/user-settings.js | 25 ++++--- src/shared/store-factory.js | 2 +- src/shared/utils/challenge-listing/filter.js | 2 +- src/shared/utils/tc.js | 4 +- 47 files changed, 258 insertions(+), 274 deletions(-) diff --git a/README.md b/README.md index a1572b123c..f9a916194d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ New version of Topcoder Community website. *Disclaimer:* Current instructions are biased towards Ubuntu 16.04. Hovewer, similar recipies should work for other OS. Should you encounter and overcome any tricky issues on other OS, you are welcome to add notes/hints into this file. -1. You should have NodeJS 6.10.0 (other recent versions should also work fine); +1. You should have NodeJS 6.10.2 (other recent versions should also work fine); 2. Install dependencies with one of the following commands: - `$ npm install` Installs all dependencies. Recommended for local development; diff --git a/__tests__/server/server.js b/__tests__/server/server.js index b6d3da43cc..b86ad9457f 100644 --- a/__tests__/server/server.js +++ b/__tests__/server/server.js @@ -48,23 +48,23 @@ describe('Api test', () => { server = require(MODULE).default; }); test('post to /api/logger', () => request(server).post('/api/logger') - .send({ data: 'data' }) - .then((response) => { - expect(response.statusCode).toBe(200); - })); + .send({ data: 'data' }) + .then((response) => { + expect(response.statusCode).toBe(200); + })); test('post to /api/xml2json', () => request(server).post('/api/xml2json') - .send({ xml: '' }) - .then((response) => { - expect(response.text).toBe('{"xml":{}}'); - })); + .send({ xml: '' }) + .then((response) => { + expect(response.text).toBe('{"xml":{}}'); + })); test('status 404', () => request(server).get('/ELB-HealthChecker/2.0') - .then((response) => { - expect(response.statusCode).toBe(404); - })); + .then((response) => { + expect(response.statusCode).toBe(404); + })); test('status 500', () => request(server).post('/api/logger') - .then((response) => { - expect(response.statusCode).toBe(500); - })); + .then((response) => { + expect(response.statusCode).toBe(500); + })); test('status 500 Internal Error', () => { process.env.NODE_ENV = 'development'; jest.resetModules(); diff --git a/__tests__/server/tc-communities.js b/__tests__/server/tc-communities.js index 4b9229295a..fc4525489d 100644 --- a/__tests__/server/tc-communities.js +++ b/__tests__/server/tc-communities.js @@ -9,15 +9,15 @@ describe('tc-communities api test', () => { server = require(MODULE).default; }); test('get community filter', () => request(server).get('/api/tc-communities') - .then((response) => { - expect(response.statusCode).toBe(200); - })); + .then((response) => { + expect(response.statusCode).toBe(200); + })); test('get community meta', () => request(server).get('/api/tc-communities/wipro/meta') - .then((response) => { - expect(response.statusCode).toBe(200); - })); + .then((response) => { + expect(response.statusCode).toBe(200); + })); test('get non-exist community meta', () => request(server).get('/api/tc-communities/noop/meta') - .then((response) => { - expect(response.statusCode).toBe(404); - })); + .then((response) => { + expect(response.statusCode).toBe(404); + })); }); diff --git a/__tests__/shared/components/challenge-listing/Listing/Bucket.jsx b/__tests__/shared/components/challenge-listing/Listing/Bucket.jsx index 5cc402f702..432e13f917 100644 --- a/__tests__/shared/components/challenge-listing/Listing/Bucket.jsx +++ b/__tests__/shared/components/challenge-listing/Listing/Bucket.jsx @@ -19,7 +19,7 @@ const mockDatas = [{ expanded: true, expand, challenges: [ - { id: '1', status: 'b' }, + { id: '1', status: 'b' }, { id: '2', status: 'a', diff --git a/__tests__/shared/services/api.js b/__tests__/shared/services/api.js index b8ba92b83e..7afc852e82 100644 --- a/__tests__/shared/services/api.js +++ b/__tests__/shared/services/api.js @@ -29,31 +29,33 @@ function testRes(res, base, token, method, body, mockOps) { function testApi(api, base, token) { return api.fetch(ENDPOINT, MOCK_OPS) - .then((res) => { - testRes(res, base, token, undefined, undefined, true); - return api.delete(ENDPOINT); - }).then((res) => { - testRes(res, base, token, 'DELETE'); - return api.get(ENDPOINT); - }).then((res) => { - testRes(res, base, token); - return api.post(ENDPOINT, 'BODY'); - }) - .then((res) => { - testRes(res, base, token, 'POST', 'BODY'); - return api.postJson(ENDPOINT, { BODY: 'BODY' }); - }) - .then((res) => { - testRes(res, base, token, 'POST', JSON.stringify({ BODY: 'BODY' })); - return api.put(ENDPOINT, 'BODY'); - }) - .then((res) => { - testRes(res, base, token, 'PUT', 'BODY'); - return api.putJson(ENDPOINT, { BODY: 'BODY' }); - }) - .then((res) => { - testRes(res, base, token, 'PUT', JSON.stringify({ BODY: 'BODY' })); - }); + .then((res) => { + testRes(res, base, token, undefined, undefined, true); + return api.delete(ENDPOINT); + }) + .then((res) => { + testRes(res, base, token, 'DELETE'); + return api.get(ENDPOINT); + }) + .then((res) => { + testRes(res, base, token); + return api.post(ENDPOINT, 'BODY'); + }) + .then((res) => { + testRes(res, base, token, 'POST', 'BODY'); + return api.postJson(ENDPOINT, { BODY: 'BODY' }); + }) + .then((res) => { + testRes(res, base, token, 'POST', JSON.stringify({ BODY: 'BODY' })); + return api.put(ENDPOINT, 'BODY'); + }) + .then((res) => { + testRes(res, base, token, 'PUT', 'BODY'); + return api.putJson(ENDPOINT, { BODY: 'BODY' }); + }) + .then((res) => { + testRes(res, base, token, 'PUT', JSON.stringify({ BODY: 'BODY' })); + }); } let api; diff --git a/package.json b/package.json index d551f8a5fb..f1fee26ebd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "homepage": "https://github.com/topcoder-platform/community-app#readme", "engines": { - "node": "^6.10.0" + "node": "^6.10.2" }, "dependencies": { "atob": "^2.0.3", @@ -53,13 +53,6 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.4", "debug": "^2.6.3", - "eslint": "^3.19.0", - "eslint-config-airbnb": "^15.0.1", - "eslint-import-resolver-babel-module": "^3.0.0", - "eslint-plugin-import": "^2.3.0", - "eslint-plugin-jest": "^20.0.0", - "eslint-plugin-jsx-a11y": "^5.0.3", - "eslint-plugin-react": "^7.0.1", "express": "^4.15.2", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^0.11.2", @@ -75,7 +68,6 @@ "moment-timezone": "^0.5.13", "morgan": "^1.8.1", "node-sass": "^4.5.0", - "optimize-css-assets-webpack-plugin": "^2.0.0", "postcss-loader": "^2.0.5", "postcss-scss": "^1.0.0", "prop-types": "^15.5.8", @@ -118,6 +110,14 @@ }, "devDependencies": { "enzyme": "^2.9.1", + "eslint": "^4.3.0", + "eslint-config-airbnb": "^15.1.0", + "eslint-import-resolver-babel-module": "^3.0.0", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-jest": "^20.0.0", + "eslint-plugin-jsx-a11y": "^5.1.1", + "eslint-plugin-react": "^7.1.0", + "optimize-css-assets-webpack-plugin": "^3.0.0", "redux-devtools": "^3.3.2", "redux-devtools-dock-monitor": "^1.1.1", "redux-devtools-log-monitor": "^1.2.0", diff --git a/src/shared/actions/auth.js b/src/shared/actions/auth.js index 41ee3b6378..bfa2933aad 100644 --- a/src/shared/actions/auth.js +++ b/src/shared/actions/auth.js @@ -18,11 +18,11 @@ function loadProfileDone(userTokenV3) { const api = getApiV3(userTokenV3); return Promise.all([ api.get(`/members/${user.handle}`) - .then(res => res.json()).then(res => - (res.result.status === 200 ? res.result.content : {})), + .then(res => res.json()).then(res => + (res.result.status === 200 ? res.result.content : {})), api.get(`/groups?memberId=${user.userId}&membershipType=user`) - .then(res => res.json()).then(res => - (res.result.status === 200 ? res.result.content : [])), + .then(res => res.json()).then(res => + (res.result.status === 200 ? res.result.content : [])), ]).then(([profile, groups]) => ({ ...profile, groups })); } diff --git a/src/shared/actions/challenge-listing/index.js b/src/shared/actions/challenge-listing/index.js index 1856ba5de9..00b71719f7 100644 --- a/src/shared/actions/challenge-listing/index.js +++ b/src/shared/actions/challenge-listing/index.js @@ -44,11 +44,11 @@ function getAll(getter, page = 0, prev) { */ function getChallengeSubtracksDone() { return getService() - .getChallengeSubtracks() - .then(res => - res.map(item => item.description) - .sort((a, b) => a.localeCompare(b)), - ); + .getChallengeSubtracks() + .then(res => + res.map(item => item.description) + .sort((a, b) => a.localeCompare(b)), + ); } /** @@ -57,11 +57,11 @@ function getChallengeSubtracksDone() { */ function getChallengeTagsDone() { return getService() - .getChallengeTags() - .then(res => - res.map(item => item.name) - .sort((a, b) => a.localeCompare(b)), - ); + .getChallengeTags() + .then(res => + res.map(item => item.name) + .sort((a, b) => a.localeCompare(b)), + ); } /** diff --git a/src/shared/actions/challenge-listing/sidebar.js b/src/shared/actions/challenge-listing/sidebar.js index 3d07755faf..3594b685cb 100644 --- a/src/shared/actions/challenge-listing/sidebar.js +++ b/src/shared/actions/challenge-listing/sidebar.js @@ -23,7 +23,7 @@ function changeFilterName(index, name) { */ function deleteSavedFilter(id, tokenV2) { return getUserSettingsService(tokenV2) - .deleteFilter(id).then(() => id); + .deleteFilter(id).then(() => id); } /** @@ -96,7 +96,7 @@ function resetFilterName(index) { */ function saveFilter(name, filter, tokenV2) { return getUserSettingsService(tokenV2) - .saveFilter(name, filter); + .saveFilter(name, filter); } /** @@ -119,7 +119,7 @@ function updateAllSavedFilters(savedFilters, tokenV2) { */ function updateSavedFilter(filter, tokenV2) { return getUserSettingsService(tokenV2) - .updateFilter(filter.id, filter.name, filter); + .updateFilter(filter.id, filter.name, filter); } export default createActions({ diff --git a/src/shared/actions/dashboard.js b/src/shared/actions/dashboard.js index 0131cfc67b..c67fb63ca2 100644 --- a/src/shared/actions/dashboard.js +++ b/src/shared/actions/dashboard.js @@ -15,7 +15,7 @@ import 'isomorphic-fetch'; */ function getSubtrackRanks(tokenV3, handle) { return getService(tokenV3) - .getSubtrackRanks(handle); + .getSubtrackRanks(handle); } /** @@ -57,9 +57,9 @@ function registerIos(tokenV3, userId) { function getBlogs() { return fetch(config.URL.BLOG) - .then(res => (res.ok ? res.text() : new Error(res.statusText))) - .then(res => toJson(res)) - .then(data => data.rss.channel.item.slice(0, 4)); + .then(res => (res.ok ? res.text() : new Error(res.statusText))) + .then(res => toJson(res)) + .then(data => data.rss.channel.item.slice(0, 4)); } function getUserFinancials(tokenV3, handle) { diff --git a/src/shared/actions/leaderboard.js b/src/shared/actions/leaderboard.js index 6c968344d0..4519fc654b 100644 --- a/src/shared/actions/leaderboard.js +++ b/src/shared/actions/leaderboard.js @@ -50,15 +50,15 @@ function fetchLeaderboard(auth, apiUrl) { 'Content-Type': 'application/json', }, }) - .then(res => res.json()) - .then((data) => { + .then(res => res.json()) + .then((data) => { // add rank field to data - data.forEach((element, index) => { - element.rank = index + 1; - }); + data.forEach((element, index) => { + element.rank = index + 1; + }); - return { data, loadedApiUrl: apiUrl }; - }); + return { data, loadedApiUrl: apiUrl }; + }); } export default createActions({ diff --git a/src/shared/actions/stats.js b/src/shared/actions/stats.js index 2d7e3f4063..3085a05504 100644 --- a/src/shared/actions/stats.js +++ b/src/shared/actions/stats.js @@ -34,10 +34,10 @@ function getCommunityStats(community, challenges, token) { }; if (community.groupId) { return groupService.getMembers(community.groupId) - .then((members) => { - result.stats.numMembers = members.length; - return result; - }).catch(() => result); + .then((members) => { + result.stats.numMembers = members.length; + return result; + }).catch(() => result); } return result; } diff --git a/src/shared/actions/tc-communities/index.js b/src/shared/actions/tc-communities/index.js index aa0d08e3e6..03d2ea5b85 100644 --- a/src/shared/actions/tc-communities/index.js +++ b/src/shared/actions/tc-communities/index.js @@ -29,7 +29,7 @@ function getList(auth) { groups = auth.profile.groups.map(g => g.id); } return fetch(`/api/tc-communities?${qs.stringify({ groups })}`) - .then(res => (res.ok ? res.json() : new Error(res.statusText))); + .then(res => (res.ok ? res.json() : new Error(res.statusText))); } export default createActions({ diff --git a/src/shared/actions/tc-communities/news.js b/src/shared/actions/tc-communities/news.js index 991794784e..a08310bd9b 100644 --- a/src/shared/actions/tc-communities/news.js +++ b/src/shared/actions/tc-communities/news.js @@ -15,8 +15,8 @@ import 'isomorphic-fetch'; */ function getNews(url) { return fetch(url) - .then(res => (res.ok ? res.text() : new Error(res.statusText))) - .then(res => toJson(res)); + .then(res => (res.ok ? res.text() : new Error(res.statusText))) + .then(res => toJson(res)); } export default createActions({ diff --git a/src/shared/components/Dashboard/Header/index.jsx b/src/shared/components/Dashboard/Header/index.jsx index 777d8afd11..d682218d9a 100644 --- a/src/shared/components/Dashboard/Header/index.jsx +++ b/src/shared/components/Dashboard/Header/index.jsx @@ -37,7 +37,7 @@ export default function Header(props) {

Earned

} - + } diff --git a/src/shared/components/Dashboard/SubtrackStats/index.jsx b/src/shared/components/Dashboard/SubtrackStats/index.jsx index 80f4e10a92..6941665d1a 100644 --- a/src/shared/components/Dashboard/SubtrackStats/index.jsx +++ b/src/shared/components/Dashboard/SubtrackStats/index.jsx @@ -70,39 +70,39 @@ export default class SubtrackStats extends React.Component { const subtracksEle = tracks => (
{ - tracks.map(subtrack => ( - ( + -
-

- {stripUnderscore(subtrack.subTrack)} -

-

- {subtrack.stat} - { - (subtrack.track === 'DEVELOP' || subtrack.track === 'DATA_SCIENCE') && + } + styleName="track" + key={subtrack.track + subtrack.subTrack} + > +

+

+ {stripUnderscore(subtrack.subTrack)} +

+

+ {subtrack.stat} + { + (subtrack.track === 'DEVELOP' || subtrack.track === 'DATA_SCIENCE') && () - } -

-

{subtrack.statType}

-
-
- )) - } + } +

+

{subtrack.statType}

+
+ + )) + }
- ); + ); const settings = { dots: false, diff --git a/src/shared/components/LeaderboardAvatar/index.jsx b/src/shared/components/LeaderboardAvatar/index.jsx index 20ca13f0a6..29580f8052 100644 --- a/src/shared/components/LeaderboardAvatar/index.jsx +++ b/src/shared/components/LeaderboardAvatar/index.jsx @@ -9,7 +9,6 @@ const VISIBLE_CHARACTERS = 3; const MOCK_PHOTO = 'https://acrobatusers.com/assets/images/template/author_generic.jpg'; class LeaderboardAvatar extends Component { - constructor(props) { super(props); this.state = { diff --git a/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx b/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx index 39e4e92520..3f5ec6a112 100644 --- a/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx +++ b/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx @@ -30,8 +30,8 @@ export default function Menu({ { profile - ? - : + ? + : }
{mainMenu.map(item => )} diff --git a/src/shared/components/challenge-listing/ChallengeCard/Status/index.jsx b/src/shared/components/challenge-listing/ChallengeCard/Status/index.jsx index 3f2004dea3..95b05bf9ce 100644 --- a/src/shared/components/challenge-listing/ChallengeCard/Status/index.jsx +++ b/src/shared/components/challenge-listing/ChallengeCard/Status/index.jsx @@ -336,11 +336,11 @@ class ChallengeStatus extends Component { const challengeURL = track === 'DATA_SCIENCE' ? DS_CHALLENGE_URL : CHALLENGE_URL; let winners = challenge.winners && challenge.winners.filter(winner => winner.type === 'final') - .map(winner => ({ - handle: winner.handle, - position: winner.placement, - photoURL: winner.photoURL || `${config.URL.BASE}/i/m/${winner.handle}.jpeg`, - })); + .map(winner => ({ + handle: winner.handle, + position: winner.placement, + photoURL: winner.photoURL || `${config.URL.BASE}/i/m/${winner.handle}.jpeg`, + })); if (winners && winners.length > MAX_VISIBLE_WINNERS) { const lastItem = { diff --git a/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx b/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx index 3a00f6cb83..70200441e9 100644 --- a/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx +++ b/src/shared/components/challenge-listing/Filters/ChallengeFilters.jsx @@ -66,46 +66,46 @@ export default function ChallengeFilters({ /> { isCardTypeSet === 'Challenges' ? - ( - - - switchTrack(TRACKS.DESIGN, on)} - /> - - - switchTrack(TRACKS.DEVELOP, on)} - /> - - - switchTrack(TRACKS.DATA_SCIENCE, on)} - /> + ( + + + switchTrack(TRACKS.DESIGN, on)} + /> + + + switchTrack(TRACKS.DEVELOP, on)} + /> + + + switchTrack(TRACKS.DATA_SCIENCE, on)} + /> + - - ) : '' + ) : '' } { isCardTypeSet === 'Challenges' ? - ( - showTrackModal(true)} - role="button" - styleName="track-btn" - tabIndex={0} - > + ( + showTrackModal(true)} + role="button" + styleName="track-btn" + tabIndex={0} + > Tracks - - - ) : '' + + + ) : '' } {/* TODO: Two components below are filter switch buttons for * mobile and desktop views. Should be refactored to use the diff --git a/src/shared/components/challenge-listing/Filters/DateRangePicker.jsx b/src/shared/components/challenge-listing/Filters/DateRangePicker.jsx index cded5d8cb4..61e8f55fb2 100644 --- a/src/shared/components/challenge-listing/Filters/DateRangePicker.jsx +++ b/src/shared/components/challenge-listing/Filters/DateRangePicker.jsx @@ -1,4 +1,4 @@ -/* eslint react/prop-types: 0 */ // startDate and endDate are instances of Moment +/* eslint react/prop-types: 0 */ // startDate and endDate are instances of Moment /** * This is an auxiliary wrapper around the DateRangePicker from the react-dates @@ -18,7 +18,6 @@ import 'react-dates/lib/css/_datepicker.css'; import './_fix_DateInput__input.css'; class DateRangePicker extends React.Component { - constructor(props) { super(props); this.state = { diff --git a/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx b/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx index 7a4d73c695..659a259184 100644 --- a/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx +++ b/src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx @@ -115,7 +115,7 @@ function Tip(props) { steps = steps.sort((a, b) => a.date.getTime() - b.date.getTime()); const currentPhaseEnd = c.currentPhases[0] ? new Date(c.currentPhases[0].scheduledEndTime) : - new Date(); + new Date(); steps = steps.map((step, index) => { let progress = 0; if (index < steps.length - 1) { diff --git a/src/shared/components/challenge-listing/index.jsx b/src/shared/components/challenge-listing/index.jsx index efe6314e0e..9abef8bca8 100755 --- a/src/shared/components/challenge-listing/index.jsx +++ b/src/shared/components/challenge-listing/index.jsx @@ -56,7 +56,7 @@ export default function ChallengeListing(props) { let challengeCardContainer; if (!expanded && props.loadingChallenges && !suppressPlaceholders) { const challengeCards = _.range(CHALLENGE_PLACEHOLDER_COUNT) - .map(key => ); + .map(key => ); challengeCardContainer = (
diff --git a/src/shared/components/examples/SvgLoading/index.jsx b/src/shared/components/examples/SvgLoading/index.jsx index d8f17197e3..21a8d512ac 100644 --- a/src/shared/components/examples/SvgLoading/index.jsx +++ b/src/shared/components/examples/SvgLoading/index.jsx @@ -11,7 +11,7 @@ export default function SvgLoading() {

SVG Loading

This component show how to load .svg assets with use of - babel-plugin-inline-react-svg.

+ babel-plugin-inline-react-svg.

{link.title} - : {link.title} + : {link.title} }
} diff --git a/src/shared/components/tc-communities/CommunityStats/index.jsx b/src/shared/components/tc-communities/CommunityStats/index.jsx index 6ba5c9085c..d571998910 100644 --- a/src/shared/components/tc-communities/CommunityStats/index.jsx +++ b/src/shared/components/tc-communities/CommunityStats/index.jsx @@ -9,7 +9,7 @@ import IconStat from '../IconStat'; import Section from '../Section'; import IconBuilding from -'../../../../assets/images/tc-communities/tmp/building.svg'; + '../../../../assets/images/tc-communities/tmp/building.svg'; import IconRocket from '../../../../assets/images/tc-communities/rocket.svg'; import IconMember from '../../../../assets/images/tc-communities/member.svg'; import IconDollar from '../../../../assets/images/tc-communities/dollar.svg'; diff --git a/src/shared/containers/Dashboard/index.jsx b/src/shared/containers/Dashboard/index.jsx index 88050afc19..7e2b999e64 100644 --- a/src/shared/containers/Dashboard/index.jsx +++ b/src/shared/containers/Dashboard/index.jsx @@ -28,7 +28,6 @@ import './styles.scss'; // The container component export class DashboardPageContainer extends React.Component { - componentDidMount() { const { auth: { tokenV2, user, tokenV3 }, @@ -109,7 +108,7 @@ export class DashboardPageContainer extends React.Component { _.filter(challenges, c => c.platforms === 'iOS'), ); - /* When we automatically reload cached challenge objects, we do not want to + /* When we automatically reload cached challenge objects, we do not want to * show the loading state, if the currently loaded challenges are not very * outdated (i.e. no need to show placeholders in the situations when it is * fine to reload silently, keeping showing the previously cached challenges, diff --git a/src/shared/containers/Leaderboard/index.jsx b/src/shared/containers/Leaderboard/index.jsx index bc4f126253..e71d33b82c 100644 --- a/src/shared/containers/Leaderboard/index.jsx +++ b/src/shared/containers/Leaderboard/index.jsx @@ -16,7 +16,6 @@ import style from './styles.scss'; // The container component class LeaderboardPageContainer extends React.Component { - componentDidMount() { if (!(this.props.apiUrl === this.props.loadedApiUrl || this.props.isLoadingLeaderboard)) { this.props.loadLeaderboard(this.props.auth, this.props.apiUrl); diff --git a/src/shared/containers/SubmissionManagement/index.jsx b/src/shared/containers/SubmissionManagement/index.jsx index 74466bf39e..f61d326497 100644 --- a/src/shared/containers/SubmissionManagement/index.jsx +++ b/src/shared/containers/SubmissionManagement/index.jsx @@ -21,7 +21,6 @@ import smpActions from '../../actions/smp'; // The container component class SubmissionManagementPageContainer extends React.Component { - componentDidMount() { if (!(this.props.challenge || this.props.isLoadingChallenge)) { this.props.loadChallengeDetails(this.props.authTokens, this.props.challengeId); @@ -95,7 +94,7 @@ class SubmissionManagementPageContainer extends React.Component { () => this.props.onSubmissionDeleteConfirmed( this.props.authTokens.tokenV3, this.props.toBeDeletedId) - } + } >Delete Submission
diff --git a/src/shared/containers/challenge-listing/FilterPanel.jsx b/src/shared/containers/challenge-listing/FilterPanel.jsx index 79e4c6cf87..4a2842aeaa 100644 --- a/src/shared/containers/challenge-listing/FilterPanel.jsx +++ b/src/shared/containers/challenge-listing/FilterPanel.jsx @@ -18,7 +18,6 @@ import { connect } from 'react-redux'; const DEFAULT_SAVED_FILTER_NAME = 'My Filter'; export class Container extends React.Component { - componentDidMount() { if (!this.props.loadingSubtracks) this.props.getSubtracks(); if (!this.props.loadingKeywords) this.props.getKeywords(); diff --git a/src/shared/containers/challenge-listing/Listing/index.jsx b/src/shared/containers/challenge-listing/Listing/index.jsx index 64286d58bc..c4b751e5b8 100644 --- a/src/shared/containers/challenge-listing/Listing/index.jsx +++ b/src/shared/containers/challenge-listing/Listing/index.jsx @@ -31,7 +31,6 @@ import style from './styles.scss'; let mounted = false; export class ListingContainer extends React.Component { - /* TODO: We should add here an automatic periodical update of the loaded * challenges, say once each 5 minutes. Otherwise, it is possible that a * visitor has the same challenge listing page open for too long, navigating diff --git a/src/shared/containers/challenge-listing/Sidebar.jsx b/src/shared/containers/challenge-listing/Sidebar.jsx index cc4bba3711..2028a26e8a 100644 --- a/src/shared/containers/challenge-listing/Sidebar.jsx +++ b/src/shared/containers/challenge-listing/Sidebar.jsx @@ -16,7 +16,6 @@ import { BUCKETS, getBuckets } from 'utils/challenge-listing/buckets'; export const SidebarPureComponent = Sidebar; export class SidebarContainer extends React.Component { - componentDidMount() { const token = this.props.tokenV2; if (token) this.props.getSavedFilters(token); diff --git a/src/shared/containers/tc-communities/CommunityStats.jsx b/src/shared/containers/tc-communities/CommunityStats.jsx index 5dbef0b3db..ba71628c6b 100644 --- a/src/shared/containers/tc-communities/CommunityStats.jsx +++ b/src/shared/containers/tc-communities/CommunityStats.jsx @@ -16,7 +16,6 @@ import cActions from 'actions/challenge-listing'; import CommunityStats from 'components/tc-communities/CommunityStats'; class CommunityStatsContainer extends React.Component { - /* When container mounts we get / update related stats. */ componentDidMount() { this.props.getCommunityStats(this.props.community, this.props.challenges, this.props.token); diff --git a/src/shared/containers/tc-communities/Page/index.jsx b/src/shared/containers/tc-communities/Page/index.jsx index b3e6bbcefa..e6684b1f43 100644 --- a/src/shared/containers/tc-communities/Page/index.jsx +++ b/src/shared/containers/tc-communities/Page/index.jsx @@ -55,7 +55,6 @@ import AccessDenied, { import './style.scss'; export class Page extends Component { - componentDidMount() { const communityId = this.props.communityId; if ((communityId !== this.props.meta.communityId) diff --git a/src/shared/reducers/challenge-listing/index.js b/src/shared/reducers/challenge-listing/index.js index 8c6a1046cd..18b762adb2 100644 --- a/src/shared/reducers/challenge-listing/index.js +++ b/src/shared/reducers/challenge-listing/index.js @@ -26,8 +26,8 @@ function onGetAllActiveChallengesDone(state, { error, payload }) { const ids = new Set(); loaded.forEach(item => ids.add(item.id)); const challenges = state.challenges - .filter(item => item.status !== 'ACTIVE' && !ids.has(item.id)) - .concat(loaded); + .filter(item => item.status !== 'ACTIVE' && !ids.has(item.id)) + .concat(loaded); return { ...state, @@ -97,7 +97,7 @@ function onGetDraftChallengesDone(state, { error, payload }) { : item => !ids.has(item.id) && item.status !== 'DRAFT'; const challenges = state.challenges - .filter(filter).concat(loaded); + .filter(filter).concat(loaded); return { ...state, diff --git a/src/shared/reducers/challenge.js b/src/shared/reducers/challenge.js index 049bb654db..776c9352f9 100644 --- a/src/shared/reducers/challenge.js +++ b/src/shared/reducers/challenge.js @@ -59,8 +59,8 @@ function create(initialState) { [smpActions.smp.deleteSubmissionDone]: (state, { payload }) => ({ ...state, mySubmissions: { v2: state.mySubmissions.v2.filter(subm => ( - subm.submissionId !== payload - )) }, + subm.submissionId !== payload + )) }, }), }, initialState || {}); } diff --git a/src/shared/services/api.js b/src/shared/services/api.js index f5b8bdc88a..f3f599b891 100644 --- a/src/shared/services/api.js +++ b/src/shared/services/api.js @@ -12,7 +12,6 @@ import config from 'utils/config'; * thing we need to be different is the base URL and auth token to use. */ export default class Api { - /** * @param {String} base Base URL of the API. * @param {String} token Optional. Authorization token. @@ -73,7 +72,7 @@ export default class Api { return this.fetch(endpoint, { body, method: 'POST' }); } -/** + /** * Sends POST request to the specified endpoint, with JSON payload. * @param {String} endpoint * @param {JSON} json diff --git a/src/shared/services/challenges.js b/src/shared/services/challenges.js index 6ce76759eb..d7d5a7b45a 100644 --- a/src/shared/services/challenges.js +++ b/src/shared/services/challenges.js @@ -93,7 +93,6 @@ export function normalizeMarathonMatch(challenge, username) { } class ChallengesService { - /** * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. */ @@ -117,13 +116,13 @@ class ChallengesService { ...params, }; return this.private.api.get(`${endpoint}?${qs.stringify(query)}`) - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 ? { - challenges: res.result.content || [], - totalCount: res.result.metadata.totalCount, - } : new Error(res.result.content) - )); + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => ( + res.result.status === 200 ? { + challenges: res.result.content || [], + totalCount: res.result.metadata.totalCount, + } : new Error(res.result.content) + )); }; this.private = { @@ -141,9 +140,9 @@ class ChallengesService { getChallengeSubtracks() { return Promise.all([ this.private.apiV2.get('/design/challengetypes') - .then(res => (res.ok ? res.json() : new Error(res.statusText))), + .then(res => (res.ok ? res.json() : new Error(res.statusText))), this.private.apiV2.get('/develop/challengetypes') - .then(res => (res.ok ? res.json() : new Error(res.statusText))), + .then(res => (res.ok ? res.json() : new Error(res.statusText))), ]).then(([a, b]) => a.concat(b)); } @@ -153,12 +152,12 @@ class ChallengesService { */ getChallengeTags() { return this.private.api.get('/technologies') - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 ? - res.result.content : - new Error(res.result.content) - )); + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => ( + res.result.status === 200 ? + res.result.content : + new Error(res.result.content) + )); } /** @@ -169,10 +168,10 @@ class ChallengesService { */ getChallenges(filters, params) { return this.private.getChallenges('/challenges/', filters, params) - .then((res) => { - res.challenges.forEach(item => normalizeChallenge(item)); - return res; - }); + .then((res) => { + res.challenges.forEach(item => normalizeChallenge(item)); + return res; + }); } /** @@ -183,10 +182,10 @@ class ChallengesService { */ getMarathonMatches(filters, params) { return this.private.getChallenges('/marathonMatches/', filters, params) - .then((res) => { - res.challenges.forEach(item => normalizeMarathonMatch(item)); - return res; - }); + .then((res) => { + res.challenges.forEach(item => normalizeMarathonMatch(item)); + return res; + }); } /** @@ -199,10 +198,10 @@ class ChallengesService { getUserChallenges(username, filters, params) { const endpoint = `/members/${username.toLowerCase()}/challenges/`; return this.private.getChallenges(endpoint, filters, params) - .then((res) => { - res.challenges.forEach(item => normalizeChallenge(item, username)); - return res; - }); + .then((res) => { + res.challenges.forEach(item => normalizeChallenge(item, username)); + return res; + }); } /** @@ -215,10 +214,10 @@ class ChallengesService { getUserMarathonMatches(username, filters, params) { const endpoint = `/members/${username.toLowerCase()}/mms/`; return this.private.getChallenges(endpoint, filters, params) - .then((res) => { - res.challenges.forEach(item => normalizeMarathonMatch(item, username)); - return res; - }); + .then((res) => { + res.challenges.forEach(item => normalizeMarathonMatch(item, username)); + return res; + }); } } diff --git a/src/shared/services/dashboard.js b/src/shared/services/dashboard.js index c083438a5b..9ab4e6ecf7 100644 --- a/src/shared/services/dashboard.js +++ b/src/shared/services/dashboard.js @@ -184,7 +184,6 @@ function filterStats(ranks) { } class DashboardService { - /** * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. */ @@ -236,12 +235,12 @@ class DashboardService { */ getUserFinancials(handle) { return this.private.api.get(`/members/${handle}/financial/`) - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 - ? res.result.content - : new Error(res.result.content) - )); + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => ( + res.result.status === 200 + ? res.result.content + : new Error(res.result.content) + )); } } diff --git a/src/shared/services/groups.js b/src/shared/services/groups.js index b297fb71fb..23f5554a69 100644 --- a/src/shared/services/groups.js +++ b/src/shared/services/groups.js @@ -5,7 +5,6 @@ import { getApiV3 } from './api'; class GroupService { - /** * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. */ @@ -27,11 +26,11 @@ class GroupService { return this.private.api.postJson(`/groups/${groupId}/members`, { param: { memberId, membershipType }, }).then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 - ? res.result.content - : new Error(res.result.content) - )); + .then(res => ( + res.result.status === 200 + ? res.result.content + : new Error(res.result.content) + )); } /** @@ -42,12 +41,12 @@ class GroupService { */ getMembers(groupId) { return this.private.api.get(`/groups/${groupId}/members`) - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 - ? res.result.content - : new Error(res.result.content) - )); + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => ( + res.result.status === 200 + ? res.result.content + : new Error(res.result.content) + )); } } diff --git a/src/shared/services/memberCert.js b/src/shared/services/memberCert.js index 017da4cfa8..1d8cb90d12 100644 --- a/src/shared/services/memberCert.js +++ b/src/shared/services/memberCert.js @@ -6,7 +6,6 @@ import { getApiV3 } from './api'; class MemberCertService { - /** * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. */ diff --git a/src/shared/services/srm.js b/src/shared/services/srm.js index 06889fe6e7..b2fbdd5676 100644 --- a/src/shared/services/srm.js +++ b/src/shared/services/srm.js @@ -6,7 +6,6 @@ import qs from 'qs'; import { getApiV3 } from './api'; class SRMService { - /** * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. */ @@ -23,12 +22,12 @@ class SRMService { */ getSRMs(params) { return this.private.api.get(`/srms/?${qs.stringify(params)}`) - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 - ? res.result.content - : new Error(res.result.content) - )); + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => ( + res.result.status === 200 + ? res.result.content + : new Error(res.result.content) + )); } /** @@ -38,12 +37,12 @@ class SRMService { */ getUserSRMs(userHandle, params) { return this.private.api.get(`/members/${userHandle}/srms/?${qs.stringify(params)}`) - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => ( - res.result.status === 200 - ? res.result.content - : new Error(res.result.content) - )); + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => ( + res.result.status === 200 + ? res.result.content + : new Error(res.result.content) + )); } } diff --git a/src/shared/services/user-settings.js b/src/shared/services/user-settings.js index b61a086942..324be57c87 100644 --- a/src/shared/services/user-settings.js +++ b/src/shared/services/user-settings.js @@ -9,7 +9,6 @@ import config from 'utils/config'; import Api from './api'; export default class UserSettings { - /** * @param {String} tokenV2 */ @@ -27,7 +26,7 @@ export default class UserSettings { */ deleteFilter(id) { return this.private.api.delete(`/saved-searches/${id}`) - .then(res => (res.ok ? null : new Error(res.statusText))); + .then(res => (res.ok ? null : new Error(res.statusText))); } /** @@ -36,21 +35,21 @@ export default class UserSettings { */ getFilters() { return this.private.api.get('/saved-searches') - .then(res => (res.ok ? res.json() : new Error(res.statusText))) - .then(res => res.map((item) => { + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => res.map((item) => { /* NOTE: Previous version of the challenge listing saved filter in * different format (like an URL query string). This try/catch block * effectively differentiate between the old (unsupported) and new * format of the filters. */ - let filter; - try { - filter = JSON.parse(item.filter); - } catch (e) { - _.noop(); - } - return { ...item, filter }; - })) - .then(res => res.filter(item => item.filter)); + let filter; + try { + filter = JSON.parse(item.filter); + } catch (e) { + _.noop(); + } + return { ...item, filter }; + })) + .then(res => res.filter(item => item.filter)); } /** diff --git a/src/shared/store-factory.js b/src/shared/store-factory.js index ecea73e660..cbc6d60dff 100644 --- a/src/shared/store-factory.js +++ b/src/shared/store-factory.js @@ -34,7 +34,7 @@ export default function storeFactory(req, initialState) { if (USE_DEV_TOOLS && module.hot) { module.hot.accept('./reducers', () => { require('./reducers').factory() - .then(newReducer => store.replaceReducer(newReducer)); + .then(newReducer => store.replaceReducer(newReducer)); }); } resolve(store); diff --git a/src/shared/utils/challenge-listing/filter.js b/src/shared/utils/challenge-listing/filter.js index 5b32a7bb68..8e48d395d1 100644 --- a/src/shared/utils/challenge-listing/filter.js +++ b/src/shared/utils/challenge-listing/filter.js @@ -130,7 +130,7 @@ function filterByText(challenge, state) { if (!state.text) return true; const str = `${challenge.name} ${challenge.platforms} ${challenge.technologies}` - .toLowerCase(); + .toLowerCase(); return str.includes(state.text.toLowerCase()); } diff --git a/src/shared/utils/tc.js b/src/shared/utils/tc.js index 418f84cb8e..b5a3f33fde 100644 --- a/src/shared/utils/tc.js +++ b/src/shared/utils/tc.js @@ -289,7 +289,7 @@ export function challengeLinks(challenge, type) { case 'detail': if (challenge.status === 'PAST') { return `${config.URL.COMMUNITY}/longcontest/stats/?module=ViewOverview&rd=${data.roundId}`; - } // for all other statues (ACTIVE, UPCOMING), show the problem statement + } // for all other statues (ACTIVE, UPCOMING), show the problem statement return `${config.URL.COMMUNITY}/longcontest/?module=ViewProblemStatement&pm=${data.problemId}&rd=${data.roundId}`; default: return ''; @@ -322,7 +322,7 @@ export function challengeLinks(challenge, type) { default: return ''; } - /* eslint no-fallthrough:0 */ + /* eslint no-fallthrough:0 */ case 'submissions': return `${config.URL.BASE}/challenge-details/${data.id}/?type=${data.track}#submissions`; case 'registrants': From 7a477d5de2865a15dc080dac9dd2f91b8d7dca2f Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Fri, 28 Jul 2017 15:58:21 +0200 Subject: [PATCH 02/13] CODE: New Challenge Details page - Part I Submission 240807 by mak17394 to the challenge http://www.topcoder.com/challenge-details/30058449/?type=develop --- __tests__/shared/actions/challenge.js | 3 +- .../examples/__snapshots__/Content.jsx.snap | 9 + src/assets/images/icon-arrow-down.svg | 12 + src/assets/images/icon-arrow-up.svg | 12 + src/shared/actions/challenge.js | 14 +- .../challenge-detail/ChallengeDetailsView.jsx | 57 ++ .../challenge-detail/Header/ChallengeTags.jsx | 67 +++ .../Header/ChallengeViewSelector.jsx | 73 +++ .../challenge-detail/Header/DeadlineCards.jsx | 66 +++ .../challenge-detail/Header/Prizes.jsx | 46 ++ .../challenge-detail/Header/index.jsx | 220 ++++++++ .../challenge-detail/Header/style.scss | 506 ++++++++++++++++++ .../components/challenge-detail/style.scss | 17 + .../components/examples/Content/index.jsx | 4 + .../containers/challenge-detail/index.jsx | 153 ++++++ .../containers/challenge-detail/styles.scss | 31 ++ src/shared/reducers/challenge.js | 3 +- src/shared/routes/index.jsx | 2 + 18 files changed, 1292 insertions(+), 3 deletions(-) create mode 100644 src/assets/images/icon-arrow-down.svg create mode 100644 src/assets/images/icon-arrow-up.svg create mode 100644 src/shared/components/challenge-detail/ChallengeDetailsView.jsx create mode 100644 src/shared/components/challenge-detail/Header/ChallengeTags.jsx create mode 100644 src/shared/components/challenge-detail/Header/ChallengeViewSelector.jsx create mode 100644 src/shared/components/challenge-detail/Header/DeadlineCards.jsx create mode 100644 src/shared/components/challenge-detail/Header/Prizes.jsx create mode 100755 src/shared/components/challenge-detail/Header/index.jsx create mode 100644 src/shared/components/challenge-detail/Header/style.scss create mode 100644 src/shared/components/challenge-detail/style.scss create mode 100644 src/shared/containers/challenge-detail/index.jsx create mode 100644 src/shared/containers/challenge-detail/styles.scss diff --git a/__tests__/shared/actions/challenge.js b/__tests__/shared/actions/challenge.js index b221f7c2fa..9702659606 100644 --- a/__tests__/shared/actions/challenge.js +++ b/__tests__/shared/actions/challenge.js @@ -52,7 +52,8 @@ describe('challenge.fetchChallengeDone', () => { }); test('payload is a promise which resolves to the expected object', () => - a.payload.then(res => expect(res).toEqual('DUMMY DATA'))); + a.payload.then(res => expect(res).toEqual( + ['DUMMY DATA', { result: { content: ['DUMMY DATA'] } }, {}]))); }); diff --git a/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap b/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap index 2dbaae67d8..cc85923a7e 100644 --- a/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap +++ b/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap @@ -134,6 +134,15 @@ exports[`Matches shallow shapshot 1`] = ` – An example of community challenge list apge which shows only challenges with special criteria. In this case only challenges which has JavaScript technology tag. +
  • + + Challenge Management Page + + – Ported Challenge detail for the main website. +
  • + + + icon-arrow-down + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/icon-arrow-up.svg b/src/assets/images/icon-arrow-up.svg new file mode 100644 index 0000000000..966af3c10f --- /dev/null +++ b/src/assets/images/icon-arrow-up.svg @@ -0,0 +1,12 @@ + + + + icon-arrow-up + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/shared/actions/challenge.js b/src/shared/actions/challenge.js index e85e5f07f8..7805f48672 100644 --- a/src/shared/actions/challenge.js +++ b/src/shared/actions/challenge.js @@ -10,9 +10,21 @@ const apiV2 = auth => getApiV2(auth.tokenV2); const apiV3 = auth => getApiV3(auth.tokenV3); function fetchChallenge(tokens, challengeId) { - return apiV3(tokens).fetch(`/challenges/?filter=id%3D${challengeId}`) + const endpoint = `/challenges/?filter=id%3D${challengeId}`; + let apiV3UserDetail = Promise.resolve({}); + if (tokens && tokens.user && tokens.user.handle) { + const endpointWithMember = `/members/${tokens.user.handle}${endpoint}`; + apiV3UserDetail = apiV3(tokens).fetch(endpointWithMember) + .then(response => response.json()) + .then(response => response.result.content[0]); + } + const endpointV2 = `/challenges/${challengeId}`; + const apiV3Promise = apiV3(tokens).fetch(endpoint) .then(response => response.json()) .then(response => response.result.content[0]); + const apiV2Promise = apiV2(tokens).fetch(endpointV2) + .then(response => response.json()); + return Promise.all([apiV3Promise, apiV2Promise, apiV3UserDetail]); } function fetchSubmissions(tokens, challengeId) { diff --git a/src/shared/components/challenge-detail/ChallengeDetailsView.jsx b/src/shared/components/challenge-detail/ChallengeDetailsView.jsx new file mode 100644 index 0000000000..7c746f779c --- /dev/null +++ b/src/shared/components/challenge-detail/ChallengeDetailsView.jsx @@ -0,0 +1,57 @@ +/* + This is currently a dummy componnent. + Future work on rendering challenge details should target this. + + WARNING: + dangerouslySetInnerHTML - is used to render html string. +*/ + +import React from 'react'; +import PT from 'prop-types'; + +import './style.scss'; + +export default function ChallengeDetailsView(props) { + const { + introduction, + detailedRequirements, + } = props; + return ( +
    +
    + { + introduction && +
    +

    CHALLENGE SUMMARY

    +
    +
    + } + { + detailedRequirements && +
    +

    FULL DESCRIPTION & PROJECT GUIDE

    +
    +
    + } +
    +
    + ); +} + +ChallengeDetailsView.defaultProps = { + introduction: undefined, + detailedRequirements: undefined, +}; + +ChallengeDetailsView.propTypes = { + introduction: PT.string, + detailedRequirements: PT.string, +}; diff --git a/src/shared/components/challenge-detail/Header/ChallengeTags.jsx b/src/shared/components/challenge-detail/Header/ChallengeTags.jsx new file mode 100644 index 0000000000..8a00c87da2 --- /dev/null +++ b/src/shared/components/challenge-detail/Header/ChallengeTags.jsx @@ -0,0 +1,67 @@ +/* + A stateless component that renders "sub track", "events" , + "platforms" and "technology" as tag. + Sub Track and Events have topcoder color accent to denote track. + Blue - Design, Green - Develop, Orange - Data Science +*/ + + +import React from 'react'; +import PT from 'prop-types'; +import { Link } from 'react-router-dom'; + +import './style.scss'; + +export default function ChallengeTags(props) { + const { + subTrack, + events, + technPlatforms, + subTrackStyle, + eventStyle, + tagFilterString, + } = props; + + return ( +
    + { + subTrack && + {subTrack} + } + { + events.map( + evnt => + {evnt}, + ) + } + { + technPlatforms.map( + tag => + ( + {tag} + + ), + ) + } +
    + ); +} + +ChallengeTags.defaultProps = { + subTrack: undefined, + events: [], + technPlatforms: [], +}; + +ChallengeTags.propTypes = { + subTrack: PT.string, + events: PT.arrayOf(PT.string), + technPlatforms: PT.arrayOf(PT.string), + subTrackStyle: PT.string.isRequired, + eventStyle: PT.string.isRequired, + tagFilterString: PT.string.isRequired, +}; diff --git a/src/shared/components/challenge-detail/Header/ChallengeViewSelector.jsx b/src/shared/components/challenge-detail/Header/ChallengeViewSelector.jsx new file mode 100644 index 0000000000..e0fe20686e --- /dev/null +++ b/src/shared/components/challenge-detail/Header/ChallengeViewSelector.jsx @@ -0,0 +1,73 @@ +/* eslint jsx-a11y/no-static-element-interactions:0 */ +/* + Stateless tab control to switch between various views available in + challenge detail page. +*/ + +import React from 'react'; +import PT from 'prop-types'; + +import './style.scss'; + +function getSelectorStyle(selectedView, currentView, track) { + return `challenge-selector-common ${(selectedView === currentView ? + `${track}-accent-color ${track}-accent-bottom-border` : 'challenge-unselected-view')}`; +} + +export default function ChallengeViewSelector(props) { + const { + onSelectorClicked, + trackLower, + selectedView, + numRegistrants, + numSubmissions, + } = props; + return ( +
    + { e.preventDefault(); onSelectorClicked('DETAILS'); }} + styleName={getSelectorStyle(selectedView, 'DETAILS', trackLower)} + >DETAILS + + { e.preventDefault(); onSelectorClicked('REGISTRANTS'); }} + styleName={getSelectorStyle(selectedView, 'REGISTRANTS', trackLower)} + >REGISTRANTS {numRegistrants ? `(${numRegistrants})` : ''} + + { e.preventDefault(); onSelectorClicked('CHECKPOINTS'); }} + styleName={getSelectorStyle(selectedView, 'CHECKPOINTS', trackLower)} + >CHECKPOINTS + + { e.preventDefault(); onSelectorClicked('SUBMISSIONS'); }} + styleName={getSelectorStyle(selectedView, 'SUBMISSIONS', trackLower)} + > + SUBMISSIONS {numSubmissions ? `(${numSubmissions})` : ''} + + { e.preventDefault(); onSelectorClicked('WINNERS'); }} + styleName={getSelectorStyle(selectedView, 'WINNERS', trackLower)} + >WINNERS + + { e.preventDefault(); onSelectorClicked('CHALLENGE_FORUM'); }} + styleName={getSelectorStyle(selectedView, 'CHALLENGE_FORUM', trackLower)} + >CHALLENGE FORUM + +
    + ); +} + +ChallengeViewSelector.defaultProps = { + numRegistrants: 0, + numSubmissions: 0, +}; + +ChallengeViewSelector.propTypes = { + onSelectorClicked: PT.func.isRequired, + trackLower: PT.string.isRequired, + selectedView: PT.string.isRequired, + numRegistrants: PT.number, + numSubmissions: PT.number, +}; diff --git a/src/shared/components/challenge-detail/Header/DeadlineCards.jsx b/src/shared/components/challenge-detail/Header/DeadlineCards.jsx new file mode 100644 index 0000000000..c92aee8e28 --- /dev/null +++ b/src/shared/components/challenge-detail/Header/DeadlineCards.jsx @@ -0,0 +1,66 @@ +/* + Component that renders all phases/deadlines for a given challenge +*/ + +import React from 'react'; +import PT from 'prop-types'; +import { localTime } from 'utils/tc'; + +import './style.scss'; + +function hasPassed(date) { + return (new Date(date)).getTime() < Date.now(); +} + +export default function DeadlineCards(props) { + const { + relevantPhases, + } = props; + const deadlineFormat = 'MMM DD, hh:mm a'; + return ( +
    + { + relevantPhases.map((phase, index) => { + const startCrossed = hasPassed(phase.actualStartTime + || phase.scheduledStartTime); + const startString = (startCrossed ? 'Started' : 'Starts'); + const phaseString = (index === relevantPhases.length - 1 ? + 'Winners' : phase.phaseType); + const deadlineKey = index === 0 ? startString : phaseString; + const endCrossed = hasPassed(phase.actualEndTime + || phase.scheduledEndTime); + let dateStyle = ''; + if (index === 0) { + if (startCrossed) { + dateStyle = 'deadline-crossed'; + } else { + dateStyle = 'deadline-live'; + } + } else if (endCrossed) { + dateStyle = 'deadline-crossed'; + } else { + dateStyle = 'deadline-live'; + } + return ( +
    +

    {deadlineKey}

    +

    + { + (index === 0 ? + localTime(phase.actualStartTime + || phase.scheduledStartTime, deadlineFormat) : + localTime(phase.actualEndTime + || phase.scheduledEndTime, deadlineFormat)) + } +

    +
    + ); + }) + } +
    + ); +} + +DeadlineCards.propTypes = { + relevantPhases: PT.arrayOf(PT.object).isRequired, +}; diff --git a/src/shared/components/challenge-detail/Header/Prizes.jsx b/src/shared/components/challenge-detail/Header/Prizes.jsx new file mode 100644 index 0000000000..2d9bb2fc59 --- /dev/null +++ b/src/shared/components/challenge-detail/Header/Prizes.jsx @@ -0,0 +1,46 @@ +/* + Component to show the prize details for given challenge. +*/ + +import React from 'react'; +import PT from 'prop-types'; + +import './style.scss'; + +function getOrdinal(num) { + const ordinals = ['th', 'st', 'nd', 'rd']; + const v = num % 100; + return ordinals[(v - 20) % 10] || ordinals[v] || ordinals[0]; +} + +export default function Prizes({ prizes }) { + return ( +
    + { + (prizes && prizes.length) ? + prizes.map((prize, index) => { + const rank = index + 1; + return ( +
    +
    +

    + {rank} + {getOrdinal(rank)} +

    +

    + $ + {prize} +

    +
    +
    + ); + }) + :
    + } +
    + ); +} + +Prizes.propTypes = { + prizes: PT.arrayOf(PT.number).isRequired, +}; diff --git a/src/shared/components/challenge-detail/Header/index.jsx b/src/shared/components/challenge-detail/Header/index.jsx new file mode 100755 index 0000000000..f0b7093a3d --- /dev/null +++ b/src/shared/components/challenge-detail/Header/index.jsx @@ -0,0 +1,220 @@ +/* eslint jsx-a11y/no-static-element-interactions:0 */ +/** + * Challenge header component. + * This component renders all other child components part of the header. + * Any data massaging needed for a child view should be done here. + */ + +import _ from 'lodash'; +import React from 'react'; +import PT from 'prop-types'; +import moment from 'moment'; +import { challengeLinks } from 'utils/tc'; +import ArrowUp from '../../../../assets/images/icon-arrow-up.svg'; +import ArrowDown from '../../../../assets/images/icon-arrow-down.svg'; + +import Prizes from './Prizes'; +import ChallengeTags from './ChallengeTags'; +import DeadlineCards from './DeadlineCards'; +import ChallengeViewSelector from './ChallengeViewSelector'; +import './style.scss'; + +export default function ChallengeHeader(props) { + const { + name, + track, + subTrack, + events, + technologies, + platforms, + prizes, + numberOfCheckpointsPrizes, + topCheckPointPrize, + reliabilityBonus, + userDetails, + currentPhases, + registrationEndDate, + submissionEndDate, + numRegistrants, + numSubmissions, + allPhases, + } = props.challenge; + + const trackLower = track ? track.toLowerCase() : 'design'; + const stylizedSubTrack = (subTrack || '').replace('_', ' ') + .replace(/\w\S*/g, txt => _.capitalize(txt)); + const subTrackStyle = `${trackLower}-accent-background`; + const eventStyle = `${trackLower}-accent-color`; + const eventNames = (events || []).map((event => (event.eventName || '').toUpperCase())); + const miscTags = _.union((technologies || '').split(', '), platforms.split(', ')); + + const tagFilterString = '/challenges?filter[tags][0]='; + + let bonusType = ''; + if (numberOfCheckpointsPrizes && topCheckPointPrize) { + bonusType = 'Bonus'; + } else if (reliabilityBonus) { + bonusType = 'Reliability Bonus'; + } + const hasRegistered = userDetails && userDetails.roles && userDetails.roles.includes('Submitter'); + const registrationEnded = new Date(registrationEndDate).getTime() < Date.now(); + const submissionEnded = new Date(submissionEndDate).getTime() < Date.now(); + const hasSubmissions = userDetails && userDetails.hasUserSubmittedForReview; + const nextDeadline = currentPhases && currentPhases.length > 0 && currentPhases[0].phaseType; + const deadlineEnd = currentPhases && currentPhases.length > 0 ? + new Date(currentPhases[0].scheduledEndTime).getTime() : Date.now(); + const currentTime = Date.now(); + const timeDiff = deadlineEnd > currentTime ? deadlineEnd - currentTime : 0; + const duration = moment.duration(timeDiff); + const timeLeft = `${duration.days()}d ${duration.hours()}:${duration.minutes()}h`; + let relevantPhases = []; + let registerButtonStyle = 'challenge-action-common'; + let submitButtonStyle = 'challenge-action-common'; + let viewSubmissionsStyle = 'challenge-action-common'; + const buttonEnableStyle = `challenge-action-${trackLower}`; + if (!registrationEnded) { + if (hasRegistered) { + registerButtonStyle = `${registerButtonStyle} challenge-action-warning`; + } else { + registerButtonStyle = `${registerButtonStyle} ${buttonEnableStyle}`; + } + } + if (!submissionEnded && hasRegistered) { + submitButtonStyle = `${submitButtonStyle} ${buttonEnableStyle}`; + } + if (hasRegistered && hasSubmissions) { + viewSubmissionsStyle = `${viewSubmissionsStyle} ${buttonEnableStyle}`; + } + + if (props.showDeadlineDetail) { + relevantPhases = (allPhases || []).filter((phase) => { + const phaseLowerCase = phase.phaseType.toLowerCase(); + if (phaseLowerCase.includes('screening') || phaseLowerCase.includes('specification')) { + return false; + } + if (phaseLowerCase.includes('registration') || phaseLowerCase.includes('checkpoint') || + phaseLowerCase.includes('submission') || phaseLowerCase.includes('approval') || + phaseLowerCase.includes('review')) { + return true; + } + return false; + }); + + relevantPhases.sort((a, b) => { + if (a.phaseType.toLowerCase().includes('registration') + || b.phaseType.toLowerCase().includes('approval')) { + return -1; + } + if (b.phaseType.toLowerCase().includes('registration') + || a.phaseType.toLowerCase().includes('approval')) { + return 1; + } + return (new Date(a.actualEndTime || a.scheduledEndTime)).getTime() - + (new Date(b.actualEndTime || b.scheduledEndTime)).getTime(); + }); + } + + return ( +
    +
    +

    {name}

    + +
    +
    +

    PRIZES

    + + { + bonusType && +
    + { + bonusType === 'Bonus' ? +

    + + BONUS: {numberOfCheckpointsPrizes} CHECKPOINTS AWARDED + WORTH ${topCheckPointPrize} EACH +

    : +

    + + RELIABILITY BONUS: {reliabilityBonus} + +

    + } +
    + } +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + Next Deadline: {nextDeadline || '-'} +
    +
    + {timeLeft} until current deadline ends +
    +
    + + {props.showDeadlineDetail ? + Hide Deadlines + : View All Deadlines + } + +
    + { + props.showDeadlineDetail && + + } +
    + +
    +
    + ); +} + +ChallengeHeader.propTypes = { + onSelectorClicked: PT.func.isRequired, + selectedView: PT.string.isRequired, + challenge: PT.shape().isRequired, + showDeadlineDetail: PT.bool.isRequired, + onToggleDeadlines: PT.func.isRequired, +}; diff --git a/src/shared/components/challenge-detail/Header/style.scss b/src/shared/components/challenge-detail/Header/style.scss new file mode 100644 index 0000000000..264cdc1f81 --- /dev/null +++ b/src/shared/components/challenge-detail/Header/style.scss @@ -0,0 +1,506 @@ +@import "~styles/tc-styles"; +@import "~styles/tc-includes"; +$deadline-text: #c1c1c6; +$deadline-detail-color: #4c4c53; +$bonus-normal-color: #00002a; + +// Should this be added to media mixins? +@mixin md-and-up { + @media (min-width: #{$md-width}) { + @content; + } +} + +@mixin xs-and-up { + @media (min-width: #{$xs-width}) { + @content; + } +} + +@mixin design-accent-bg { + .design-accent-background { + background-color: $tc-dark-blue-90; + } +} + +@mixin develop-accent-bg { + .develop-accent-background { + background-color: $tc-green; + } +} + +@mixin datasci-accent-bg { + .datasci-accent-background { + background-color: $tc-orange; + } +} + +@mixin design-accent-clr { + .design-accent-color { + color: $tc-dark-blue-90; + } +} + +@mixin develop-accent-clr { + .develop-accent-color { + color: $tc-green; + } +} + +@mixin datasci-accent-clr { + .datasci-accent-color { + color: $tc-orange; + } +} + +@mixin design-accent-btm-border { + .design-accent-bottom-border { + border-bottom: 3px solid $tc-dark-blue-90; + } +} + +@mixin develop-accent-btm-border { + .develop-accent-bottom-border { + border-bottom: 3px solid $tc-green; + } +} + +@mixin datasci-accent-btm-border { + .datasci-accent-bottom-border { + border-bottom: 3px solid $tc-orange; + } +} + +.challenge-outer-container { + background: $tc-gray-neutral-light; + border-radius: 6px 6px 0 0; + + @include xxs-to-sm { + padding: 4px 4px 1px 0; + } + + border-bottom: $tc-gray-20 solid 1px; + + .important-detail { + @include xxs-to-sm { + padding: 15px 15px 1px; + } + + @include sm { + padding: 20px 20px 2px; + } + + @include md-and-up { + padding: 20px 19px 2px 30px; + } + + .challenge-header { + font-family: roboto; + color: $tc-black; + font-size: 28px; + line-height: 40px; + letter-spacing: -0.78px; + font-weight: 500; + margin-bottom: 5px; + + @include xxs-to-sm { + font-size: 20px; + line-height: 25px; + margin-right: 43px; + letter-spacing: 0; + } + + @include md-and-up { + margin-right: 60px; + } + + @include sm { + margin-right: -4px; + } + } + + .tag-holder { + .tag-common { + border-radius: 3px; + font-family: roboto; + font-size: 10px; + line-height: 20px; + margin: 5px 5px 0 0; + display: inline-block; + text-align: center; + padding: 0 5px; + font-weight: 700; + color: $tc-white; + background-color: $tc-light-blue-10; + } + + .design-accent-background { + background-color: $tc-light-blue; + } + + .design-accent-color { + background-color: $tc-light-blue; + } + + @include develop-accent-bg; + @include datasci-accent-bg; + @include develop-accent-clr; + @include datasci-accent-clr; + + .misc-tag { + color: $tc-gray-80; + background-color: $tc-gray-neutral-dark; + cursor: pointer; + } + } + + .prizes-ops-container { + background-color: $tc-white; + border: 1px solid $tc-gray-20; + border-radius: 6px 6px 0 0; + margin-top: 20px; + display: flex; + flex-wrap: wrap; + align-items: stretch; + + .prizes-outer-container { + flex: 4; + + .prizes-title { + margin-left: 20px; + + @include xxs-to-sm { + margin-left: 10px; + } + + margin-top: 20px; + font-family: roboto; + font-weight: 700; + font-size: 10px; + color: $tc-gray-50; + line-height: 10px; + letter-spacing: 1.33px; + } + + .prizes-container { + display: flex; + align-content: center; + justify-content: center; + flex-wrap: wrap; + + @include xs-and-up { + min-width: 270px; + } + + .prize-fill { + min-width: 75px; + margin: 15px 15px 0 0; + + .prize-card { + float: right; + display: inline-block; + border-bottom: $tc-gray-20 solid 3px; + text-align: right; + + .prize-rank { + font-family: roboto; + font-weight: 400; + font-size: 13px; + color: $tc-gray-50; + letter-spacing: 0; + line-height: 15px; + margin-bottom: 5px; + + .rank-ordinal { + color: $tc-gray-50; + } + } + + .prize-money { + font-family: roboto; + font-weight: 500; + font-size: 20px; + color: $tc-gray-80; + letter-spacing: -0.5px; + line-height: 30px; + margin-bottom: 5px; + + .prize-currency { + color: $tc-gray-80; + } + } + } + + .prize-card[id=rank1] { + border-bottom: $tc-gold solid 3px; + } + + .prize-card[id=rank2] { + border-bottom: $tc-silver solid 3px; + } + + .prize-card[id=rank3] { + border-bottom: $tc-bronze solid 3px; + } + } + } + + .bonus-div { + text-align: center; + font-family: roboto; + font-weight: 400; + color: $bonus-normal-color; + line-height: 25px; + margin: 25px 15px; + + .bonus-text { + .bonus-highlight { + font-weight: 500; + } + + .design-accent-color { + color: $tc-light-blue; + } + + @include develop-accent-clr; + @include datasci-accent-clr; + } + } + } + + .challenge-ops-container { + flex: 1; + + .challenge-ops { + margin: 10px 20px; + + .challenge-ops-form { + display: flex; + flex-direction: column; + align-content: center; + + .challenge-action-common { + margin: 10px; + font-family: roboto; + font-family: 500; + font-size: 15px; + line-height: 40px; + border-radius: 4px; + white-space: nowrap; + color: $tc-white; + border: 0 hidden; + } + + .challenge-action-datasci { + background-color: $tc-orange; + } + + .challenge-action-datasci:hover { + background-image: linear-gradient(-180deg, $tc-orange-70 0%, $tc-orange 100%); + } + + .challenge-action-datasci:active { + background: $tc-orange; + box-shadow: inset 0 1px 3px 0 rgba(71, 71, 79, 0.38); + } + + .challenge-action-datasci:focus { + border: 1px solid $tc-orange; + box-shadow: 0 0 2px 1px $tc-orange-30; + } + + .challenge-action-develop { + background-color: $tc-green; + } + + .challenge-action-develop:hover { + background-image: linear-gradient(-180deg, $tc-green-70 0%, $tc-green 100%); + } + + .challenge-action-develop:active { + background: $tc-green; + box-shadow: inset 0 1px 3px 0 rgba(71, 71, 79, 0.38); + } + + .challenge-action-develop:focus { + border: 1px solid $tc-green; + box-shadow: 0 0 2px 1px $tc-green-30; + } + + .challenge-action-warning { + background-color: $tc-red-70; + } + + .challenge-action-warning:hover { + background-image: linear-gradient(-180deg, #ff5b52 0%, #f22f24 100%); + } + + .challenge-action-warning:active { + background: $tc-red; + box-shadow: inset 0 1px 3px 0 rgba(71, 71, 79, 0.38); + } + + .challenge-action-warning:focus { + border: 1px solid $tc-red; + box-shadow: 0 0 2px 1px $tc-red-30; + } + + .challenge-action-design { + background-color: $tc-dark-blue-90; + } + + .challenge-action-design:hover { + background-image: linear-gradient(-180deg, #3996ff 0%, #127bf3 100%); + } + + .challenge-action-design:active { + box-shadow: inset 0 1px 3px 0 rgba(71, 71, 79, 0.5); + } + + .challenge-action-design:focus { + border: 1px solid $tc-dark-blue; + box-shadow: 0 0 2px 1px $tc-dark-blue-70; + } + + .challenge-action-common:disabled { + background-color: $tc-gray-40; + color: $tc-gray-neutral-light; + } + } + } + } + } + + .deadlines-view { + border-radius: 0 0 6px 6px; + border: 0 hidden $tc-white; + overflow: hidden; + + .deadlines-overview { + background-color: $tc-gray-70; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + + .deadlines-overview-text { + display: flex; + justify-content: flex-start; + font-family: roboto; + font-weight: 400; + color: $deadline-text; + line-height: 20px; + padding: 15px 20px; + font-size: 15px; + + .next-deadline { + word-wrap: normal; + border-right: $tc-gray-50 solid 1px; + padding-right: 10px; + + .deadline-highlighted { + color: $tc-white; + font-weight: 700; + } + } + + .current-phase { + overflow-wrap: normal; + padding-left: 10px; + + .deadline-highlighted { + color: $tc-white; + font-weight: 700; + } + } + } + + .deadlines-collapser { + font-family: roboto; + font-weight: 500; + font-size: 13px; + color: $tc-gray-20; + letter-spacing: 0; + line-height: 20px; + padding: 15px 20px; + cursor: pointer; + + .collapse-text { + svg { + transform: rotate(180deg); + } + } + } + } + + .deadline-detailed { + background-color: $deadline-detail-color; + display: flex; + flex-wrap: wrap; + padding: 30px 0 15px; + + .deadline-card { + margin-bottom: 10px; + padding: 0 20px; + border-right: 1px solid $tc-gray-10; + font-family: roboto; + font-weight: 400; + + .deadline-info { + font-size: 13px; + color: $tc-gray-40; + letter-spacing: 0; + line-height: 15px; + margin-bottom: 5px; + } + + .deadline-date { + min-width: 150px; + font-size: 20px; + letter-spacing: -0.5px; + line-height: 25px; + } + + .deadline-crossed { + color: $tc-green; + } + + .deadline-live { + color: $tc-white; + } + } + } + } + + .challenge-view-selector { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin-top: 20px; + + .challenge-selector-common { + font-family: roboto; + font-size: 13px; + line-height: 30px; + margin: 10px 20px 0; + cursor: pointer; + } + + .challenge-selected-view { + font-weight: 700; + } + + .challenge-unselected-view { + font-weight: 400; + color: $tc-gray-70; + border-bottom: 3px hidden $tc-white; + } + + @include datasci-accent-clr; + @include develop-accent-clr; + @include design-accent-clr; + @include datasci-accent-btm-border; + @include develop-accent-btm-border; + @include design-accent-btm-border; + } + } +} diff --git a/src/shared/components/challenge-detail/style.scss b/src/shared/components/challenge-detail/style.scss new file mode 100644 index 0000000000..feb9d593e5 --- /dev/null +++ b/src/shared/components/challenge-detail/style.scss @@ -0,0 +1,17 @@ +@import "~styles/tc-styles"; +@import "~styles/tc-includes"; + +.challenge-details-view { + text-align: left; + padding: 25px; + + div { + font-family: roboto; + font-weight: 400; + width: 100%; + + div { + max-width: 720px; + } + } +} diff --git a/src/shared/components/examples/Content/index.jsx b/src/shared/components/examples/Content/index.jsx index 92dee94788..4730a828f7 100644 --- a/src/shared/components/examples/Content/index.jsx +++ b/src/shared/components/examples/Content/index.jsx @@ -75,6 +75,10 @@ export default function Content() { with special criteria. In this case only challenges which has JavaScript technology tag.
  • +
  • + Challenge Management + Page – Ported Challenge detail for the main website. +
  • Leaderboard – Leaderboard page.
  • diff --git a/src/shared/containers/challenge-detail/index.jsx b/src/shared/containers/challenge-detail/index.jsx new file mode 100644 index 0000000000..80a6fa66fa --- /dev/null +++ b/src/shared/containers/challenge-detail/index.jsx @@ -0,0 +1,153 @@ +/** + * This container component load data into its state, and pass them to children via props. + * Its should have in its state, and properly manage the showDetails set + * (thus allowing to show/hide detail panels for different submissions), + * and it should define all necessary handlers to pass to the children. + */ + +import _ from 'lodash'; +import LoadingIndicator from 'components/LoadingIndicator'; +import ChallengeHeader from 'components/challenge-detail/Header'; +import ChallengeDetailsView from 'components/challenge-detail/ChallengeDetailsView'; +import React from 'react'; +import PT from 'prop-types'; +import { connect } from 'react-redux'; +import challengeActions from 'actions/challenge'; +import './styles.scss'; + +// The container component +class ChallengeDetailPageContainer extends React.Component { + + constructor(props, context) { + super(props, context); + + this.state = { + showDeadlineDetail: false, + selectedView: 'DETAILS', + }; + + this.onToggleDeadlines = this.onToggleDeadlines.bind(this); + this.onSelectorClicked = this.onSelectorClicked.bind(this); + } + + componentDidMount() { + this.props.loadChallengeDetails(this.props.authTokens, this.props.challengeId); + } + + componentWillReceiveProps(nextProps) { + if (this.props.tokenV3 !== nextProps.tokenV3) { + this.props.reloadChallengeDetails(nextProps.authTokens, this.props.challengeId); + } + } + + onToggleDeadlines(event) { + event.preventDefault(); + this.setState({ + showDeadlineDetail: !this.state.showDeadlineDetail, + }); + } + + onSelectorClicked(view) { + this.setState({ + selectedView: view, + }); + } + + render() { + const isEmpty = _.isEmpty(this.props.challenge); + + return ( +
    +
    + {this.props.isLoadingChallenge && } + { + !isEmpty && + + } + { + !isEmpty && this.state.selectedView === 'DETAILS' && + + } +
    +
    + ); + } +} + +ChallengeDetailPageContainer.defaultProps = { + tokenV3: null, + isLoadingChallenge: false, +}; + +ChallengeDetailPageContainer.propTypes = { + tokenV3: PT.string, + challenge: PT.shape().isRequired, + isLoadingChallenge: PT.bool, + loadChallengeDetails: PT.func.isRequired, + authTokens: PT.shape().isRequired, + challengeId: PT.number.isRequired, + reloadChallengeDetails: PT.func.isRequired, +}; + +function extractChallengeDetail(v3, v2) { + let challenge = {}; + if (!_.isEmpty(v3)) { + challenge = _.clone(v3); + if (!_.isEmpty(v2)) { + challenge.numberOfCheckpointsPrizes = v2.numberOfCheckpointsPrizes; + challenge.introduction = v2.introduction; + challenge.detailedRequirements = v2.detailedRequirements; + challenge.topCheckPointPrize = v2.topCheckPointPrize; + } + } else if (!_.isEmpty(v2)) { + challenge = { + name: v2.challengeName, + track: v2.type, + subTrack: v2.challengeType, + events: v2.event ? [{ eventName: v2.event.shortDescription, eventId: v2.event.id }] : [], + technologies: v2.technology ? v2.technology.join(', ') : '', + platforms: v2.platforms ? v2.platforms.join(', ') : '', + prizes: v2.prize, + topCheckPointPrize: v2.topCheckPointPrize, + numberOfCheckpointsPrizes: v2.numberOfCheckpointsPrizes, + introduction: v2.introduction, + detailedRequirements: v2.detailedRequirements, + }; + } + return challenge; +} + +const mapStateToProps = (state, props) => ({ + challengeId: Number(props.match.params.challengeId), + challenge: extractChallengeDetail(state.challenge.details, state.challenge.detailsV2), + isLoadingChallenge: state.challenge.loadingDetails, + authTokens: state.auth, + tokenV3: (state.auth ? state.auth.tokenV3 : null), +}); + +const mapDispatchToProps = dispatch => ({ + loadChallengeDetails: (tokens, challengeId) => { + dispatch(challengeActions.fetchChallengeInit()); + dispatch(challengeActions.fetchChallengeDone(tokens, challengeId)); + }, + reloadChallengeDetails: (tokens, challengeId) => { + dispatch(challengeActions.fetchChallengeDone(tokens, challengeId)); + }, +}); + +const ChallengeDetailContainer = connect( + mapStateToProps, + mapDispatchToProps, +)(ChallengeDetailPageContainer); + +export default ChallengeDetailContainer; diff --git a/src/shared/containers/challenge-detail/styles.scss b/src/shared/containers/challenge-detail/styles.scss new file mode 100644 index 0000000000..f5ee78306c --- /dev/null +++ b/src/shared/containers/challenge-detail/styles.scss @@ -0,0 +1,31 @@ +@import "~styles/tc-styles"; +@import "~styles/tc-includes"; + +// Should this be added to media mixins? +@mixin md-and-up { + @media (min-width: #{$md-width}) { + @content; + } +} + +.outer-container { + background: $tc-gray-10; + + @include xxs-to-sm { + padding: 8px 10px 20px; + } + + @include sm { + padding: 30px 14px 30px 20px; + } + + @include md-and-up { + padding: 30px 64px; + } +} + +.challenge-detail-container { + background: $tc-white; + width: 100%; + margin: auto; +} diff --git a/src/shared/reducers/challenge.js b/src/shared/reducers/challenge.js index 049bb654db..61136ec108 100644 --- a/src/shared/reducers/challenge.js +++ b/src/shared/reducers/challenge.js @@ -16,7 +16,8 @@ import mySubmissionsManagement from './my-submissions-management'; function onFetchChallengeDone(state, action) { return { ...state, - details: action.error ? null : action.payload, + details: action.error ? null : action.payload[2] || action.payload[0], + detailsV2: action.error ? null : action.payload[1], fetchChallengeFailure: action.error || false, loadingDetails: false, }; diff --git a/src/shared/routes/index.jsx b/src/shared/routes/index.jsx index 5a4e2a0d10..f0adaef292 100644 --- a/src/shared/routes/index.jsx +++ b/src/shared/routes/index.jsx @@ -6,6 +6,7 @@ import Content from 'components/examples/Content'; import Error404 from 'components/Error404'; import SubmissionManagement from 'containers/SubmissionManagement'; import ChallengeListing from 'containers/challenge-listing/Listing'; +import ChallengeDetail from 'containers/challenge-detail'; import Leaderboard from 'containers/Leaderboard'; import Dashboard from 'containers/Dashboard'; import 'isomorphic-fetch'; @@ -75,6 +76,7 @@ function Routes({ subdomains }) { path="/community-challenge-listing/:keyword" render={props => } /> + } From cd096cbd35b12f5ea1c3b4e9d5b8209904671bd4 Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Fri, 28 Jul 2017 16:23:27 +0200 Subject: [PATCH 03/13] ESLint and unit tests fixes --- __tests__/shared/reducers/challenge.js | 10 ++++++++-- .../challenge-detail/Header/ChallengeTags.jsx | 16 ++++++++-------- .../challenge-detail/Header/DeadlineCards.jsx | 6 +++--- .../challenge-detail/Header/Prizes.jsx | 2 +- src/shared/containers/challenge-detail/index.jsx | 1 - 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/__tests__/shared/reducers/challenge.js b/__tests__/shared/reducers/challenge.js index 4dae274b35..83fe33ce66 100644 --- a/__tests__/shared/reducers/challenge.js +++ b/__tests__/shared/reducers/challenge.js @@ -4,7 +4,7 @@ const mockChallengeActions = { fetchChallengeInit: mockAction('FETCH_CHALLENGE_INIT'), fetchChallengeDone: mockAction( 'FETCH_CHALLENGE_DONE', - 'payload', + ['v3-details', 'v2-details', 'v3-user-details'], ), fetchChallengeDoneError: mockAction( 'FETCH_CHALLENGE_DONE', @@ -72,7 +72,8 @@ function testReducer(reducer, istate) { mySubmissionsManagement: {}, loadingDetails: false, fetchChallengeFailure: false, - details: 'payload', + details: 'v3-user-details', + detailsV2: 'v2-details', }); }); @@ -83,6 +84,7 @@ function testReducer(reducer, istate) { loadingDetails: false, fetchChallengeFailure: 'Unknown error', details: null, + detailsV2: null, }); }); @@ -93,6 +95,7 @@ function testReducer(reducer, istate) { loadingDetails: false, fetchChallengeFailure: 'Unknown error', details: null, + detailsV2: null, loadingMySubmissions: true, mySubmissions: { v2: null }, }); @@ -105,6 +108,7 @@ function testReducer(reducer, istate) { loadingDetails: false, fetchChallengeFailure: 'Unknown error', details: null, + detailsV2: null, mySubmissions: { v2: [{ submissionId: '1' }] }, fetchMySubmissionsFailure: false, loadingMySubmissions: false, @@ -118,6 +122,7 @@ function testReducer(reducer, istate) { loadingDetails: false, fetchChallengeFailure: 'Unknown error', details: null, + detailsV2: null, fetchMySubmissionsFailure: false, loadingMySubmissions: false, mySubmissions: { v2: [{ submissionId: '1' }] }, @@ -131,6 +136,7 @@ function testReducer(reducer, istate) { loadingDetails: false, fetchChallengeFailure: 'Unknown error', details: null, + detailsV2: null, mySubmissions: { v2: [] }, loadingMySubmissions: false, fetchMySubmissionsFailure: 'Unknown error', diff --git a/src/shared/components/challenge-detail/Header/ChallengeTags.jsx b/src/shared/components/challenge-detail/Header/ChallengeTags.jsx index 8a00c87da2..66fa7e7a7d 100644 --- a/src/shared/components/challenge-detail/Header/ChallengeTags.jsx +++ b/src/shared/components/challenge-detail/Header/ChallengeTags.jsx @@ -37,14 +37,14 @@ export default function ChallengeTags(props) { { technPlatforms.map( tag => - ( - {tag} - - ), + ( + {tag} + + ), ) }
    diff --git a/src/shared/components/challenge-detail/Header/DeadlineCards.jsx b/src/shared/components/challenge-detail/Header/DeadlineCards.jsx index c92aee8e28..22933c99ea 100644 --- a/src/shared/components/challenge-detail/Header/DeadlineCards.jsx +++ b/src/shared/components/challenge-detail/Header/DeadlineCards.jsx @@ -46,10 +46,10 @@ export default function DeadlineCards(props) {

    {deadlineKey}

    { - (index === 0 ? - localTime(phase.actualStartTime + (index === 0 ? + localTime(phase.actualStartTime || phase.scheduledStartTime, deadlineFormat) : - localTime(phase.actualEndTime + localTime(phase.actualEndTime || phase.scheduledEndTime, deadlineFormat)) }

    diff --git a/src/shared/components/challenge-detail/Header/Prizes.jsx b/src/shared/components/challenge-detail/Header/Prizes.jsx index 2d9bb2fc59..b3abf3bb3b 100644 --- a/src/shared/components/challenge-detail/Header/Prizes.jsx +++ b/src/shared/components/challenge-detail/Header/Prizes.jsx @@ -35,7 +35,7 @@ export default function Prizes({ prizes }) { ); }) - :
    + :
    }
    ); diff --git a/src/shared/containers/challenge-detail/index.jsx b/src/shared/containers/challenge-detail/index.jsx index 80a6fa66fa..4187a82bd7 100644 --- a/src/shared/containers/challenge-detail/index.jsx +++ b/src/shared/containers/challenge-detail/index.jsx @@ -17,7 +17,6 @@ import './styles.scss'; // The container component class ChallengeDetailPageContainer extends React.Component { - constructor(props, context) { super(props, context); From 78838517facb46a00cd99402d9b6461c3c31b6e8 Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Sat, 29 Jul 2017 22:49:51 +0200 Subject: [PATCH 04/13] Clean-up: Global SCSS styles Originally, there was a need for a quick fix to ensure that buttons (Register / Unregister, Submit, etc.) in the upper part of the New Challenge Details page work properly. As it turned out, the markup and SCSS styling around these buttons is messy, there was a need to refactor related styling; in turn it was noted that some related global SCSS styles are messy. Thus, it resulted in quite a large fix, and the actual fix of the New Challenge Details page will be in the follow-up commit. In global SCSS styles: - _tc-includes.scss was removed (it had just two imports, which were moved directly to _tc-styles, to avoid confusion, which one of them should be typically imported into .scss files); - mixins/_media.scss was enhanced in few ways: (a) breakpoint constants were moved into _variables.scss (actually, they were already defined there, but with different values, and were not used anywhere in code - thus, this change ensures consistency); (b) few new media mixins were added; (c) ensures that for all mixins the name A-to-B means "the sizes from A to B, both inclusive". - slight modification of color values in _variables.scss: some of the color values were slightly different from the values stated on Topcoder Brand Guidelines page (https://www.topcoder.com/about-topcoder/brand-guidelines/), they were updated to match. New Challenge Details page: - Custom colors replaced by close colors already present in _variables.scss --- src/shared/actions/challenge.js | 20 +++++- .../MyChallenges/ChallengeFilter/style.scss | 2 +- .../MyChallenges/ChallengeLinks/style.scss | 2 +- .../Dashboard/MyChallenges/styles.scss | 2 +- .../Dashboard/SubtrackStats/styles.scss | 2 +- src/shared/components/Error404/style.scss | 2 +- .../Leaderboard/PodiumSpot/styles.scss | 2 +- .../components/TopcoderFooter/style.scss | 2 +- .../components/TopcoderHeader/Auth/style.scss | 2 +- .../desktop/SubMenu/Item/style.scss | 2 +- .../TopcoderHeader/desktop/SubMenu/style.scss | 2 +- .../TopcoderHeader/mobile/Header/style.scss | 2 +- .../TopcoderHeader/mobile/Menu/style.scss | 2 +- .../TopcoderHeader/mobile/Search/style.scss | 2 +- .../TopcoderHeader/mobile/SubMenu/style.scss | 2 +- .../components/TopcoderHeader/style.scss | 2 +- .../challenge-detail/Header/style.scss | 29 ++------ .../components/challenge-detail/style.scss | 1 - .../ChallengeCard/Status/style.scss | 19 +++-- .../ChallengeCard/style.scss | 12 ++-- .../placeholders/ChallengeCard/style.scss | 4 +- .../components/examples/Content/index.jsx | 57 ++++++--------- .../components/examples/Content/style.scss | 6 +- .../Accordion/Accordion/style.scss | 4 +- .../Accordion/AccordionItem/style.scss | 4 +- .../tc-communities/ArticleCard/style.scss | 6 +- .../tc-communities/Banner/Link/style.scss | 2 +- .../tc-communities/Banner/style.scss | 12 ++-- .../tc-communities/CommunityStats/style.scss | 2 +- .../tc-communities/Footer/style.scss | 12 ++-- .../tc-communities/Header/style.scss | 36 +++++----- .../tc-communities/IconStat/style.scss | 6 +- .../tc-communities/ImageText/style.scss | 14 ++-- .../tc-communities/LinksCard/style.scss | 2 +- .../NewsletterSignup/style.scss | 14 ++-- .../tc-communities/ResourceCard/style.scss | 6 +- .../tc-communities/Section/style.scss | 6 +- .../components/tc-communities/Text/style.scss | 2 +- .../communities/community-2/Home/style.scss | 6 +- .../communities/community-2/Learn/style.scss | 12 ++-- .../communities/demo-expert/Home/style.scss | 6 +- .../communities/demo-expert/Learn/style.scss | 12 ++-- .../communities/taskforce/Home/style.scss | 8 +-- .../communities/tc-prod-dev/Home/style.scss | 6 +- .../communities/tc-prod-dev/Learn/style.scss | 12 ++-- .../communities/wipro/Home/style.scss | 6 +- .../wipro/Home/themes/IconStatStyles.scss | 8 +-- .../wipro/Home/themes/articleCardStyles.scss | 4 +- .../communities/wipro/Home/themes/banner.scss | 6 +- .../wipro/Home/themes/imageTextStyle.scss | 8 +-- .../wipro/Home/themes/newsSectionStyles.scss | 2 +- .../wipro/Home/themes/newsletter_signup.scss | 6 +- .../wipro/Home/themes/resourceCardStyles.scss | 4 +- .../communities/wipro/Learn/style.scss | 12 ++-- src/shared/containers/Dashboard/styles.scss | 2 +- src/shared/containers/Leaderboard/styles.scss | 6 +- .../containers/challenge-detail/index.jsx | 34 ++++++--- .../containers/challenge-detail/styles.scss | 12 +--- .../challenge-listing/Listing/styles.scss | 6 +- src/shared/services/challenges.js | 36 ++++++++-- src/shared/utils/tc.js | 11 +++ src/styles/_tc-includes.scss | 3 - src/styles/_tc-styles.scss | 2 + src/styles/_typography.scss | 2 - src/styles/_variables.scss | 41 ++++++++--- src/styles/mixins/_media.scss | 69 +++++++++++-------- 66 files changed, 352 insertions(+), 296 deletions(-) delete mode 100644 src/styles/_tc-includes.scss diff --git a/src/shared/actions/challenge.js b/src/shared/actions/challenge.js index 7805f48672..1baf8867cb 100644 --- a/src/shared/actions/challenge.js +++ b/src/shared/actions/challenge.js @@ -4,6 +4,7 @@ import _ from 'lodash'; import { createActions } from 'redux-actions'; +import { getService as getChallengesService } from 'services/challenges'; import { getApiV2, getApiV3 } from '../services/api'; const apiV2 = auth => getApiV2(auth.tokenV2); @@ -33,10 +34,27 @@ function fetchSubmissions(tokens, challengeId) { .then(response => response.submissions); } +/** + * Registers user for the challenge. + * @param {String} tokenV2 Auth token for Topcoder API v2. + * @param {String} challengeId + * @return {Promise} + */ +function registerDone(tokenV2, challengeId) { + return getChallengesService(undefined, tokenV2).register(challengeId); +} + export default createActions({ + /* TODO: Move these actions into the CHALLENGE object below. It does not make + * any technical difference, but will lead to better action names displayed in + * Redux dev tools, which is convenient. */ FETCH_CHALLENGE_INIT: _.noop, FETCH_CHALLENGE_DONE: fetchChallenge, - FETCH_SUBMISSIONS_INIT: _.noop, FETCH_SUBMISSIONS_DONE: fetchSubmissions, + + CHALLENGE: { + REGISTER_INIT: _.noop, + REGISTER_DONE: registerDone, + }, }); diff --git a/src/shared/components/Dashboard/MyChallenges/ChallengeFilter/style.scss b/src/shared/components/Dashboard/MyChallenges/ChallengeFilter/style.scss index 4aee30b821..36eb166cab 100644 --- a/src/shared/components/Dashboard/MyChallenges/ChallengeFilter/style.scss +++ b/src/shared/components/Dashboard/MyChallenges/ChallengeFilter/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .container { width: 270px; diff --git a/src/shared/components/Dashboard/MyChallenges/ChallengeLinks/style.scss b/src/shared/components/Dashboard/MyChallenges/ChallengeLinks/style.scss index a43c42033c..405c3cd5a4 100644 --- a/src/shared/components/Dashboard/MyChallenges/ChallengeLinks/style.scss +++ b/src/shared/components/Dashboard/MyChallenges/ChallengeLinks/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .challenge-links.tile-view { display: flex; diff --git a/src/shared/components/Dashboard/MyChallenges/styles.scss b/src/shared/components/Dashboard/MyChallenges/styles.scss index e12ed8d8bb..e2744fa49b 100644 --- a/src/shared/components/Dashboard/MyChallenges/styles.scss +++ b/src/shared/components/Dashboard/MyChallenges/styles.scss @@ -1,6 +1,6 @@ // adopt from 'topcoder-app/assets/css/my-dashboard/my-challenges.scss' -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .challenges { header { diff --git a/src/shared/components/Dashboard/SubtrackStats/styles.scss b/src/shared/components/Dashboard/SubtrackStats/styles.scss index e44212586f..223e7492c2 100644 --- a/src/shared/components/Dashboard/SubtrackStats/styles.scss +++ b/src/shared/components/Dashboard/SubtrackStats/styles.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .subtrack-stats { width: 100%; diff --git a/src/shared/components/Error404/style.scss b/src/shared/components/Error404/style.scss index 78f415a330..ba6c0d7ffb 100644 --- a/src/shared/components/Error404/style.scss +++ b/src/shared/components/Error404/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .container { @include roboto; diff --git a/src/shared/components/Leaderboard/PodiumSpot/styles.scss b/src/shared/components/Leaderboard/PodiumSpot/styles.scss index bf09c58499..feee909ed5 100644 --- a/src/shared/components/Leaderboard/PodiumSpot/styles.scss +++ b/src/shared/components/Leaderboard/PodiumSpot/styles.scss @@ -48,7 +48,7 @@ } } -@media (min-width: #{$sm-width}) and (max-width: #{$lg-width - 1px}) { +@include sm-to-md { .PodiumSpot--first .leaderboard-avatar { height: 128px; width: 128px; diff --git a/src/shared/components/TopcoderFooter/style.scss b/src/shared/components/TopcoderFooter/style.scss index 9fc55e6dae..21bc9cc92f 100644 --- a/src/shared/components/TopcoderFooter/style.scss +++ b/src/shared/components/TopcoderFooter/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .copyright-notice { text-align: center; diff --git a/src/shared/components/TopcoderHeader/Auth/style.scss b/src/shared/components/TopcoderHeader/Auth/style.scss index 4acd07b443..9b6858ba93 100644 --- a/src/shared/components/TopcoderHeader/Auth/style.scss +++ b/src/shared/components/TopcoderHeader/Auth/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .auth { display: inline-block; diff --git a/src/shared/components/TopcoderHeader/desktop/SubMenu/Item/style.scss b/src/shared/components/TopcoderHeader/desktop/SubMenu/Item/style.scss index f50573964b..a902898141 100644 --- a/src/shared/components/TopcoderHeader/desktop/SubMenu/Item/style.scss +++ b/src/shared/components/TopcoderHeader/desktop/SubMenu/Item/style.scss @@ -1,4 +1,4 @@ -@import "~styles/tc-includes"; +@import "~styles/tc-styles"; .item { color: $tc-white; diff --git a/src/shared/components/TopcoderHeader/desktop/SubMenu/style.scss b/src/shared/components/TopcoderHeader/desktop/SubMenu/style.scss index edb80a3702..c8d7067695 100644 --- a/src/shared/components/TopcoderHeader/desktop/SubMenu/style.scss +++ b/src/shared/components/TopcoderHeader/desktop/SubMenu/style.scss @@ -1,4 +1,4 @@ -@import "~styles/tc-includes"; +@import "~styles/tc-styles"; @mixin menu { background: $tc-black; diff --git a/src/shared/components/TopcoderHeader/mobile/Header/style.scss b/src/shared/components/TopcoderHeader/mobile/Header/style.scss index e57da73c62..5b754e2038 100644 --- a/src/shared/components/TopcoderHeader/mobile/Header/style.scss +++ b/src/shared/components/TopcoderHeader/mobile/Header/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .auth { position: absolute; diff --git a/src/shared/components/TopcoderHeader/mobile/Menu/style.scss b/src/shared/components/TopcoderHeader/mobile/Menu/style.scss index 0d80955bfe..1a001bb873 100644 --- a/src/shared/components/TopcoderHeader/mobile/Menu/style.scss +++ b/src/shared/components/TopcoderHeader/mobile/Menu/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .menu { background: $tc-gray-90; diff --git a/src/shared/components/TopcoderHeader/mobile/Search/style.scss b/src/shared/components/TopcoderHeader/mobile/Search/style.scss index d8d64c2d07..b76d3272d3 100644 --- a/src/shared/components/TopcoderHeader/mobile/Search/style.scss +++ b/src/shared/components/TopcoderHeader/mobile/Search/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .icon { left: 18px; diff --git a/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss b/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss index a01b863720..b67459f030 100644 --- a/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss +++ b/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .item { color: $tc-white; diff --git a/src/shared/components/TopcoderHeader/style.scss b/src/shared/components/TopcoderHeader/style.scss index cabb3e6c73..e8c7be52e1 100644 --- a/src/shared/components/TopcoderHeader/style.scss +++ b/src/shared/components/TopcoderHeader/style.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .auth-buttons { display: inline-block; diff --git a/src/shared/components/challenge-detail/Header/style.scss b/src/shared/components/challenge-detail/Header/style.scss index 264cdc1f81..f24e129d94 100644 --- a/src/shared/components/challenge-detail/Header/style.scss +++ b/src/shared/components/challenge-detail/Header/style.scss @@ -1,21 +1,4 @@ @import "~styles/tc-styles"; -@import "~styles/tc-includes"; -$deadline-text: #c1c1c6; -$deadline-detail-color: #4c4c53; -$bonus-normal-color: #00002a; - -// Should this be added to media mixins? -@mixin md-and-up { - @media (min-width: #{$md-width}) { - @content; - } -} - -@mixin xs-and-up { - @media (min-width: #{$xs-width}) { - @content; - } -} @mixin design-accent-bg { .design-accent-background { @@ -90,7 +73,7 @@ $bonus-normal-color: #00002a; padding: 20px 20px 2px; } - @include md-and-up { + @include md-to-lg { padding: 20px 19px 2px 30px; } @@ -110,7 +93,7 @@ $bonus-normal-color: #00002a; letter-spacing: 0; } - @include md-and-up { + @include md-to-lg { margin-right: 60px; } @@ -188,7 +171,7 @@ $bonus-normal-color: #00002a; justify-content: center; flex-wrap: wrap; - @include xs-and-up { + @include xs-to-lg { min-width: 270px; } @@ -249,7 +232,7 @@ $bonus-normal-color: #00002a; text-align: center; font-family: roboto; font-weight: 400; - color: $bonus-normal-color; + color: $tc-black; line-height: 25px; margin: 25px 15px; @@ -387,7 +370,7 @@ $bonus-normal-color: #00002a; justify-content: flex-start; font-family: roboto; font-weight: 400; - color: $deadline-text; + color: $tc-gray-30; line-height: 20px; padding: 15px 20px; font-size: 15px; @@ -433,7 +416,7 @@ $bonus-normal-color: #00002a; } .deadline-detailed { - background-color: $deadline-detail-color; + background-color: $tc-gray-80; display: flex; flex-wrap: wrap; padding: 30px 0 15px; diff --git a/src/shared/components/challenge-detail/style.scss b/src/shared/components/challenge-detail/style.scss index feb9d593e5..a4221e5c33 100644 --- a/src/shared/components/challenge-detail/style.scss +++ b/src/shared/components/challenge-detail/style.scss @@ -1,5 +1,4 @@ @import "~styles/tc-styles"; -@import "~styles/tc-includes"; .challenge-details-view { text-align: left; diff --git a/src/shared/components/challenge-listing/ChallengeCard/Status/style.scss b/src/shared/components/challenge-listing/ChallengeCard/Status/style.scss index a7f00511e7..be420dfc6f 100644 --- a/src/shared/components/challenge-listing/ChallengeCard/Status/style.scss +++ b/src/shared/components/challenge-listing/ChallengeCard/Status/style.scss @@ -14,12 +14,9 @@ $status-radius-4: $corner-radius * 2; text-align: right; width: 77.7%; - @include xs-to-md { - width: 100%; - } - - @media (min-width: #{$xs-width}) and (max-width: #{$md-width - 1px}) { + @include xs-to-sm { margin-top: $status-space-20 + 3; + width: 100%; } @include xs { @@ -27,7 +24,7 @@ $status-radius-4: $corner-radius * 2; } &.completed { - @include xs-to-md { + @include xs-to-sm { text-align: left; padding-left: 0; margin-bottom: $base-unit + 2; @@ -38,7 +35,7 @@ $status-radius-4: $corner-radius * 2; align-items: center; height: 30px; - @include xs-to-md { + @include xs-to-sm { display: flex; align-items: center; } @@ -48,7 +45,7 @@ $status-radius-4: $corner-radius * 2; .avatar-container { margin-right: $status-space-15 + 2; - @media (min-width: $lg-width - 1) and (max-width: 1440px) { + @include lg { margin-right: 14px; } @@ -120,17 +117,17 @@ $status-radius-4: $corner-radius * 2; a.num-reg.past, a.num-sub.past, a.link-forum.past { - @media (min-width: $lg-width - 1) and (max-width: 1380px) { + @include lg { margin-left: 10px; } } - @media (min-width: #{$md-width + 1px}) and (max-width: $base-unit * 252) { + @include lg { margin-left: auto; display: block; } - @include xs-to-md { + @include xs-to-sm { margin-left: auto; min-width: auto; } diff --git a/src/shared/components/challenge-listing/ChallengeCard/style.scss b/src/shared/components/challenge-listing/ChallengeCard/style.scss index 1727c3f4f5..f9c73b2518 100644 --- a/src/shared/components/challenge-listing/ChallengeCard/style.scss +++ b/src/shared/components/challenge-listing/ChallengeCard/style.scss @@ -28,11 +28,11 @@ $challenge-radius-4: $corner-radius * 2; @include xs-to-sm { flex-wrap: wrap; - position: relative; + padding: $base-unit * 3; } @include xs { - padding: $base-unit * 3; + position: relative; } a, @@ -49,7 +49,7 @@ $challenge-radius-4: $corner-radius * 2; justify-content: flex-start; width: 50%; - @include xs-to-md { + @include xs-to-sm { width: 100%; } } @@ -59,7 +59,7 @@ $challenge-radius-4: $corner-radius * 2; justify-content: space-between; width: 50%; - @include xs-to-md { + @include xs-to-sm { width: 100%; } @@ -151,7 +151,7 @@ $challenge-radius-4: $corner-radius * 2; margin-right: $challenge-space-20; min-width: $challenge-space-50 + 2; - @media (min-width: #{$xs-width}) and (max-width: #{$md-width - 1px}) { + @include xs-to-sm { position: absolute; right: 0; top: 20px; @@ -168,7 +168,7 @@ $challenge-radius-4: $corner-radius * 2; } &.with-register-button { - @media (min-width: #{$sm-width}) and (max-width: #{$md-width - 1px}) { + @include sm { right: 108px; } } diff --git a/src/shared/components/challenge-listing/placeholders/ChallengeCard/style.scss b/src/shared/components/challenge-listing/placeholders/ChallengeCard/style.scss index 07e26070e8..017ae89ba9 100644 --- a/src/shared/components/challenge-listing/placeholders/ChallengeCard/style.scss +++ b/src/shared/components/challenge-listing/placeholders/ChallengeCard/style.scss @@ -27,7 +27,7 @@ $challenge-radius-4: $corner-radius * 2; justify-content: flex-start; width: 50%; - @include xs-to-md { + @include xs-to-sm { width: 100%; } } @@ -37,7 +37,7 @@ $challenge-radius-4: $corner-radius * 2; justify-content: space-between; width: 50%; - @include xs-to-md { + @include xs-to-sm { width: 100%; } diff --git a/src/shared/components/examples/Content/index.jsx b/src/shared/components/examples/Content/index.jsx index 4730a828f7..99305ba8fb 100644 --- a/src/shared/components/examples/Content/index.jsx +++ b/src/shared/components/examples/Content/index.jsx @@ -56,39 +56,33 @@ export default function Content() {
  • Webpack;
  • -

    New Topcoder Pages

    + +

    Main Topcoder website

    • - Submission Management - Page – New submission management page, is available at - the endpoint /challenge/:challengeId/my-submissions. - The link here leads to the test challenge. -
    • -
    • - Main Website Challenge Listing - Page – Ported Challenge Listing for the main website. + Challenge Details + Page – generally availabel at the + endpoint /challenges/:challengeId; the link here leads + to a sample challenge.
    • - Community - Challenge Listing Page – An example of community - challenge list apge which shows only challenges - with special criteria. - In this case only challenges which has JavaScript technology tag. + Dashboard
    • - Challenge Management - Page – Ported Challenge detail for the main website. + Main Challenge Listing
    • - Leaderboard – Leaderboard page. + Submission Management + Page – generally available at + the endpoint /challenges/:challengeId/my-submissions; + the link here leads to a test challenge.
    • +
    + +

    Separate Topcoder Communities

    +
    • - Community header - example – An example of cummunity header with default - style. Also, there are examples of - custom red theme, custom - green theme and non-existent - community page. + Community 2
    • Demo Expert Community @@ -98,24 +92,13 @@ export default function Content() {
    • Topcoder Product - Development -
    • -
    • - Wipro Community Homepage – - Example of community implementation with new design. - This community has three more pages: Learn - , Challenges - and Leaderboard. - There is also an example of non-existent community - page. + Development community
    • - Community 2 -
    • -
    • - Dashboard – Dashboard page. + TopGear (Wipro) community
    +

    Misc Examples

    • diff --git a/src/shared/components/examples/Content/style.scss b/src/shared/components/examples/Content/style.scss index 62c8a8ad30..fdc7187227 100644 --- a/src/shared/components/examples/Content/style.scss +++ b/src/shared/components/examples/Content/style.scss @@ -1,4 +1,8 @@ +@import "~styles/tc-styles"; + .Content { + @include roboto-light; + padding: 24px; h1 { @@ -11,7 +15,7 @@ li { list-style-type: disc; - margin-left: 24px; + margin: 9px 24px; a { color: blue; diff --git a/src/shared/components/tc-communities/Accordion/Accordion/style.scss b/src/shared/components/tc-communities/Accordion/Accordion/style.scss index f3b5d7638d..d4a3a4eb53 100644 --- a/src/shared/components/tc-communities/Accordion/Accordion/style.scss +++ b/src/shared/components/tc-communities/Accordion/Accordion/style.scss @@ -26,7 +26,7 @@ width: 250px; } - @include xxs-to-sm { + @include xxs-to-xs { display: none; } } @@ -66,7 +66,7 @@ width: 430px; } - @include xxs-to-sm { + @include xxs-to-xs { width: 100%; } } diff --git a/src/shared/components/tc-communities/Accordion/AccordionItem/style.scss b/src/shared/components/tc-communities/Accordion/AccordionItem/style.scss index 416563e37d..11c81175b2 100644 --- a/src/shared/components/tc-communities/Accordion/AccordionItem/style.scss +++ b/src/shared/components/tc-communities/Accordion/AccordionItem/style.scss @@ -10,7 +10,7 @@ padding: 0 16px; position: relative; - @include xxs-to-sm { + @include xxs-to-xs { display: block; } @@ -44,7 +44,7 @@ display: none; padding-top: 25px; - @include xxs-to-sm { + @include xxs-to-xs { background-color: #fafafa; padding: 20px 15px 26px; } diff --git a/src/shared/components/tc-communities/ArticleCard/style.scss b/src/shared/components/tc-communities/ArticleCard/style.scss index 36eb53ba10..f30afd1508 100644 --- a/src/shared/components/tc-communities/ArticleCard/style.scss +++ b/src/shared/components/tc-communities/ArticleCard/style.scss @@ -11,7 +11,7 @@ width: 220px; } - @include xxs-to-sm { + @include xxs-to-xs { height: auto; margin: 0 15px; padding-bottom: 30px; @@ -29,7 +29,7 @@ display: block; height: 200px; - @include xxs-to-sm { + @include xxs-to-xs { height: auto; min-height: 205px; @@ -73,7 +73,7 @@ .linkWrap { margin-top: 27px; - @include xxs-to-md { + @include xxs-to-sm { margin-top: 20px; } } diff --git a/src/shared/components/tc-communities/Banner/Link/style.scss b/src/shared/components/tc-communities/Banner/Link/style.scss index 4e73f82d93..3ffcafad5d 100644 --- a/src/shared/components/tc-communities/Banner/Link/style.scss +++ b/src/shared/components/tc-communities/Banner/Link/style.scss @@ -3,7 +3,7 @@ .linkWrap { margin-top: 30px; - @include xxs-to-sm { + @include xxs-to-xs { margin-top: 20px; } } diff --git a/src/shared/components/tc-communities/Banner/style.scss b/src/shared/components/tc-communities/Banner/style.scss index 62273fc153..552f04cfb3 100644 --- a/src/shared/components/tc-communities/Banner/style.scss +++ b/src/shared/components/tc-communities/Banner/style.scss @@ -7,7 +7,7 @@ overflow: hidden; position: relative; - @include xxs-to-sm { + @include xxs-to-xs { height: 600px; } } @@ -20,7 +20,7 @@ transform: translateY(-50%); width: 538px; - @include xxs-to-sm { + @include xxs-to-xs { bottom: 0; max-height: 100%; min-width: 0; @@ -40,7 +40,7 @@ top: 0; width: 100%; - @include xxs-to-sm { + @include xxs-to-xs { background: linear-gradient(to bottom, rgba(#0c53a6, 0) 0%, rgba(#0c53a6, 0.8) 61%, rgba(#0c53a6, 0.8) 100%); transform: none; } @@ -52,7 +52,7 @@ position: relative; z-index: 1; - @include xxs-to-sm { + @include xxs-to-xs { padding: 86px 15px 80px; text-align: center; } @@ -64,7 +64,7 @@ h2.title { line-height: 136.11%; text-transform: uppercase; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 30px; } } @@ -76,7 +76,7 @@ h2.title { line-height: 24px; margin-top: 10px; - @include xxs-to-sm { + @include xxs-to-xs { line-height: 22px; } } diff --git a/src/shared/components/tc-communities/CommunityStats/style.scss b/src/shared/components/tc-communities/CommunityStats/style.scss index 77cbdcda86..579cab3865 100644 --- a/src/shared/components/tc-communities/CommunityStats/style.scss +++ b/src/shared/components/tc-communities/CommunityStats/style.scss @@ -8,7 +8,7 @@ border-bottom: 1px solid #ddd; padding: 43px 38px 50px; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; justify-content: space-around; padding: 30px 12px 32px; diff --git a/src/shared/components/tc-communities/Footer/style.scss b/src/shared/components/tc-communities/Footer/style.scss index e645bf0c36..51cbfc7edd 100644 --- a/src/shared/components/tc-communities/Footer/style.scss +++ b/src/shared/components/tc-communities/Footer/style.scss @@ -6,7 +6,7 @@ justify-content: center; height: 80px; - @include xxs-to-sm { + @include xxs-to-xs { flex-wrap: wrap; height: auto; justify-content: space-around; @@ -18,13 +18,13 @@ .item + .item { margin-left: 78px; - @include xxs-to-sm { + @include xxs-to-xs { margin-left: 15px; } } .item { - @include xxs-to-sm { + @include xxs-to-xs { margin-right: 15px; margin-left: 15px; line-height: 30px; @@ -40,7 +40,7 @@ a.link:visited { } .itemLogo { - @include xxs-to-sm { + @include xxs-to-xs { left: 50%; line-height: normal; margin: 0; @@ -54,7 +54,7 @@ a.link:visited { max-height: 30px; width: auto; - @include xxs-to-sm { + @include xxs-to-xs { max-height: 20px; } } @@ -64,7 +64,7 @@ a.link:visited { padding: 13px 0 12px; text-align: center; - @include xxs-to-md { + @include xxs-to-sm { display: block; } } diff --git a/src/shared/components/tc-communities/Header/style.scss b/src/shared/components/tc-communities/Header/style.scss index db84f57024..e7f1e6da6a 100644 --- a/src/shared/components/tc-communities/Header/style.scss +++ b/src/shared/components/tc-communities/Header/style.scss @@ -10,7 +10,7 @@ cursor: pointer; } - @include xxs-to-md { + @include xxs-to-sm { display: block; } } @@ -22,13 +22,13 @@ margin-left: 30px; margin-right: 30px; - @include xxs-to-md { + @include xxs-to-sm { justify-content: space-between; margin-left: 0; margin-right: 0; } - @include xxs-to-sm { + @include xxs-to-xs { height: 64px; } } @@ -37,7 +37,7 @@ align-items: center; display: flex; - @include xxs-to-sm { + @include xxs-to-xs { display: block; } } @@ -48,7 +48,7 @@ flex-shrink: 0; height: 100%; - @include xxs-to-sm { + @include xxs-to-xs { height: auto; margin-top: 12px; justify-content: center; @@ -65,7 +65,7 @@ max-height: 40px; } - @include xxs-to-sm { + @include xxs-to-xs { & + .logo { margin-left: 10px; } @@ -89,7 +89,7 @@ padding-left: 13px; text-transform: uppercase; - @include xxs-to-sm { + @include xxs-to-xs { border-left: 0; font-size: 12px; height: 17px; @@ -111,7 +111,7 @@ transition-timing-function: ease-in; } - @include xxs-to-md { + @include xxs-to-sm { max-width: 100%; overflow: hidden; } @@ -123,7 +123,7 @@ white-space: nowrap; text-align: center; - @include xxs-to-md { + @include xxs-to-sm { display: block; text-align: left; } @@ -132,7 +132,7 @@ .menu-item { display: block; - @include xxs-to-md { + @include xxs-to-sm { padding: 0 15px; } } @@ -184,7 +184,7 @@ position: absolute; right: 4px; - @include xxs-to-md { + @include xxs-to-sm { display: none; } } @@ -227,7 +227,7 @@ } } - @media (min-width: #{$md-width}) { + @include md-to-lg { display: none; } } @@ -241,7 +241,7 @@ float: right; margin-top: 12px; - @include xxs-to-sm { + @include xxs-to-xs { margin-top: 16px; } } @@ -257,7 +257,7 @@ display: none; margin-right: 15px; - @include xxs-to-md { + @include xxs-to-sm { display: block; } } @@ -267,7 +267,7 @@ height: 100%; vertical-align: bottom; - @include xxs-to-md { + @include xxs-to-sm { display: none; } } @@ -280,7 +280,7 @@ padding: 18px 0 17px; vertical-align: bottom; - @include xxs-to-lg { + @include xxs-to-md { display: none; } } @@ -288,7 +288,7 @@ .authorize { padding-top: 12px; - @include xxs-to-md { + @include xxs-to-sm { display: none; } } @@ -355,7 +355,7 @@ width: 1px; } - @include xxs-to-md { + @include xxs-to-sm { display: none; } } diff --git a/src/shared/components/tc-communities/IconStat/style.scss b/src/shared/components/tc-communities/IconStat/style.scss index 2fbd187f5f..7be4cbc6af 100644 --- a/src/shared/components/tc-communities/IconStat/style.scss +++ b/src/shared/components/tc-communities/IconStat/style.scss @@ -10,7 +10,7 @@ max-height: 50px; max-width: 50px; - @include xxs-to-sm { + @include xxs-to-xs { max-height: 30px; max-width: 30px; } @@ -21,7 +21,7 @@ font: 600 40px/137.5% 'Open Sans'; margin-top: 17px; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 24px; } } @@ -31,7 +31,7 @@ font: 15px 'Open Sans'; text-transform: uppercase; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 11px; } } diff --git a/src/shared/components/tc-communities/ImageText/style.scss b/src/shared/components/tc-communities/ImageText/style.scss index ae1eba216f..bf3f5e5e7b 100644 --- a/src/shared/components/tc-communities/ImageText/style.scss +++ b/src/shared/components/tc-communities/ImageText/style.scss @@ -3,7 +3,7 @@ .container { display: flex; - @include xxs-to-sm { + @include xxs-to-xs { display: block; } @@ -18,7 +18,7 @@ height: 320px; width: 50%; - @include xxs-to-sm { + @include xxs-to-xs { height: auto; min-height: 180px; width: 100%; @@ -37,7 +37,7 @@ display: flex; width: 50%; - @include xxs-to-sm { + @include xxs-to-xs { width: 100%; } } @@ -49,7 +49,7 @@ padding: 0 40px 0 20px; } - @include xxs-to-sm { + @include xxs-to-xs { padding: 20px 15px 40px; text-align: center; @@ -63,7 +63,7 @@ color: #394146; font: 600 36px/136.11% 'Open Sans'; - @include xxs-to-md { + @include xxs-to-sm { font-size: 24px; } } @@ -74,7 +74,7 @@ margin-top: 19px; opacity: 0.7; - @include xxs-to-md { + @include xxs-to-sm { margin-top: 10px; } } @@ -82,7 +82,7 @@ .linkWrap { margin-top: 27px; - @include xxs-to-md { + @include xxs-to-sm { margin-top: 20px; } } diff --git a/src/shared/components/tc-communities/LinksCard/style.scss b/src/shared/components/tc-communities/LinksCard/style.scss index 093d99a86e..b932da73b9 100644 --- a/src/shared/components/tc-communities/LinksCard/style.scss +++ b/src/shared/components/tc-communities/LinksCard/style.scss @@ -13,7 +13,7 @@ width: 220px; } - @include xxs-to-sm { + @include xxs-to-xs { border-bottom: 1px solid #ddd; border-right: 0; margin: 0 15px 30px; diff --git a/src/shared/components/tc-communities/NewsletterSignup/style.scss b/src/shared/components/tc-communities/NewsletterSignup/style.scss index 650ce9da99..7aacbbbcf9 100644 --- a/src/shared/components/tc-communities/NewsletterSignup/style.scss +++ b/src/shared/components/tc-communities/NewsletterSignup/style.scss @@ -20,7 +20,7 @@ width: 100%; } - @include xxs-to-sm { + @include xxs-to-xs { height: auto; } } @@ -34,7 +34,7 @@ width: 720px; } - @include xxs-to-sm { + @include xxs-to-xs { padding: 20px 15px 37px; width: auto; } @@ -44,7 +44,7 @@ color: #fff; font: 600 32px/134.38% 'Open Sans'; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 24px; } } @@ -54,7 +54,7 @@ font: 18px/133.33% 'Open Sans'; margin-top: 20px; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 15px; margin-top: 10px; } @@ -66,7 +66,7 @@ margin-top: 40px; overflow: hidden; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; margin-top: 20px; width: 100%; @@ -96,7 +96,7 @@ input:not([type="checkbox"]).formEmail { border: 0; } - @include xxs-to-sm { + @include xxs-to-xs { flex-grow: 1; width: 100%; } @@ -112,7 +112,7 @@ input:not([type="checkbox"]).formEmail { text-transform: uppercase; width: 124px; - @include xxs-to-sm { + @include xxs-to-xs { width: 95px; } } diff --git a/src/shared/components/tc-communities/ResourceCard/style.scss b/src/shared/components/tc-communities/ResourceCard/style.scss index 92cdebbce1..cbba16ba9e 100644 --- a/src/shared/components/tc-communities/ResourceCard/style.scss +++ b/src/shared/components/tc-communities/ResourceCard/style.scss @@ -8,7 +8,7 @@ width: 200px; } - @include xxs-to-sm { + @include xxs-to-xs { padding: 0 15px; width: auto; @@ -23,7 +23,7 @@ max-height: 100px; width: 100px; - @include xxs-to-md { + @include xxs-to-sm { max-height: 60px; width: 60px; } @@ -34,7 +34,7 @@ font: 600 20px/150% 'Open Sans'; margin-top: 30px; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 24px; } } diff --git a/src/shared/components/tc-communities/Section/style.scss b/src/shared/components/tc-communities/Section/style.scss index 9daae2eaa5..5577a59704 100644 --- a/src/shared/components/tc-communities/Section/style.scss +++ b/src/shared/components/tc-communities/Section/style.scss @@ -4,7 +4,7 @@ padding: 35px 0; padding-bottom: 70px; - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 42px; } } @@ -15,7 +15,7 @@ padding: 60px 0 30px; text-align: center; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 24px; padding-top: 30px; } @@ -31,7 +31,7 @@ width: 720px; } - @include xxs-to-sm { + @include xxs-to-xs { display: block; width: 100%; } diff --git a/src/shared/components/tc-communities/Text/style.scss b/src/shared/components/tc-communities/Text/style.scss index 84e8787cf9..05124f23ec 100644 --- a/src/shared/components/tc-communities/Text/style.scss +++ b/src/shared/components/tc-communities/Text/style.scss @@ -11,7 +11,7 @@ margin-bottom: 0; } - @include xxs-to-sm { + @include xxs-to-xs { margin-bottom: 10px; &:last-child { diff --git a/src/shared/components/tc-communities/communities/community-2/Home/style.scss b/src/shared/components/tc-communities/communities/community-2/Home/style.scss index 9b305b6d87..e66cd00b00 100644 --- a/src/shared/components/tc-communities/communities/community-2/Home/style.scss +++ b/src/shared/components/tc-communities/communities/community-2/Home/style.scss @@ -8,7 +8,7 @@ border-bottom: 1px solid #ddd; padding: 43px 38px 50px; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; justify-content: space-around; padding: 30px 12px 32px; @@ -19,7 +19,7 @@ background-color: #fafafa; padding: 70px 0; - @include xxs-to-sm { + @include xxs-to-xs { padding: 40px 0; } } @@ -27,7 +27,7 @@ .linksContainer { padding: 24px 0 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding: 0; } } diff --git a/src/shared/components/tc-communities/communities/community-2/Learn/style.scss b/src/shared/components/tc-communities/communities/community-2/Learn/style.scss index 0cf7ca7cff..9464523ea6 100644 --- a/src/shared/components/tc-communities/communities/community-2/Learn/style.scss +++ b/src/shared/components/tc-communities/communities/community-2/Learn/style.scss @@ -8,7 +8,7 @@ padding-bottom: 50px; padding-top: 0; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 36px; padding-bottom: 30px; } @@ -17,7 +17,7 @@ .resourcesContainer { padding-bottom: 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 50px; } } @@ -25,7 +25,7 @@ .bannerContainer { height: 252px; - @include xxs-to-sm { + @include xxs-to-xs { height: 300px; } } @@ -36,14 +36,14 @@ transform: none; width: 600px; - @include xxs-to-sm { + @include xxs-to-xs { top: auto; width: 100%; } } .bannerContentInner { - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 20px; padding-top: 48px; } @@ -56,7 +56,7 @@ .joinnowWrap { margin-top: 10px; - @include xxs-to-sm { + @include xxs-to-xs { margin-top: 20px; text-align: center; } diff --git a/src/shared/components/tc-communities/communities/demo-expert/Home/style.scss b/src/shared/components/tc-communities/communities/demo-expert/Home/style.scss index 9b305b6d87..e66cd00b00 100644 --- a/src/shared/components/tc-communities/communities/demo-expert/Home/style.scss +++ b/src/shared/components/tc-communities/communities/demo-expert/Home/style.scss @@ -8,7 +8,7 @@ border-bottom: 1px solid #ddd; padding: 43px 38px 50px; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; justify-content: space-around; padding: 30px 12px 32px; @@ -19,7 +19,7 @@ background-color: #fafafa; padding: 70px 0; - @include xxs-to-sm { + @include xxs-to-xs { padding: 40px 0; } } @@ -27,7 +27,7 @@ .linksContainer { padding: 24px 0 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding: 0; } } diff --git a/src/shared/components/tc-communities/communities/demo-expert/Learn/style.scss b/src/shared/components/tc-communities/communities/demo-expert/Learn/style.scss index 0cf7ca7cff..9464523ea6 100644 --- a/src/shared/components/tc-communities/communities/demo-expert/Learn/style.scss +++ b/src/shared/components/tc-communities/communities/demo-expert/Learn/style.scss @@ -8,7 +8,7 @@ padding-bottom: 50px; padding-top: 0; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 36px; padding-bottom: 30px; } @@ -17,7 +17,7 @@ .resourcesContainer { padding-bottom: 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 50px; } } @@ -25,7 +25,7 @@ .bannerContainer { height: 252px; - @include xxs-to-sm { + @include xxs-to-xs { height: 300px; } } @@ -36,14 +36,14 @@ transform: none; width: 600px; - @include xxs-to-sm { + @include xxs-to-xs { top: auto; width: 100%; } } .bannerContentInner { - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 20px; padding-top: 48px; } @@ -56,7 +56,7 @@ .joinnowWrap { margin-top: 10px; - @include xxs-to-sm { + @include xxs-to-xs { margin-top: 20px; text-align: center; } diff --git a/src/shared/components/tc-communities/communities/taskforce/Home/style.scss b/src/shared/components/tc-communities/communities/taskforce/Home/style.scss index 4ed5f39830..2b58751e91 100644 --- a/src/shared/components/tc-communities/communities/taskforce/Home/style.scss +++ b/src/shared/components/tc-communities/communities/taskforce/Home/style.scss @@ -8,7 +8,7 @@ border-bottom: 1px solid #ddd; padding: 43px 38px 50px; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; justify-content: space-around; padding: 30px 12px 32px; @@ -19,7 +19,7 @@ background-color: #fafafa; padding: 70px 0; - @include xxs-to-sm { + @include xxs-to-xs { padding: 40px 0; } } @@ -27,13 +27,13 @@ .linksContainer { padding: 24px 0 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding: 0; } } .BannerContent { - @include sm-and-up { + @include sm-to-lg { width: 800px; } } diff --git a/src/shared/components/tc-communities/communities/tc-prod-dev/Home/style.scss b/src/shared/components/tc-communities/communities/tc-prod-dev/Home/style.scss index 9b305b6d87..e66cd00b00 100644 --- a/src/shared/components/tc-communities/communities/tc-prod-dev/Home/style.scss +++ b/src/shared/components/tc-communities/communities/tc-prod-dev/Home/style.scss @@ -8,7 +8,7 @@ border-bottom: 1px solid #ddd; padding: 43px 38px 50px; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; justify-content: space-around; padding: 30px 12px 32px; @@ -19,7 +19,7 @@ background-color: #fafafa; padding: 70px 0; - @include xxs-to-sm { + @include xxs-to-xs { padding: 40px 0; } } @@ -27,7 +27,7 @@ .linksContainer { padding: 24px 0 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding: 0; } } diff --git a/src/shared/components/tc-communities/communities/tc-prod-dev/Learn/style.scss b/src/shared/components/tc-communities/communities/tc-prod-dev/Learn/style.scss index 0cf7ca7cff..9464523ea6 100644 --- a/src/shared/components/tc-communities/communities/tc-prod-dev/Learn/style.scss +++ b/src/shared/components/tc-communities/communities/tc-prod-dev/Learn/style.scss @@ -8,7 +8,7 @@ padding-bottom: 50px; padding-top: 0; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 36px; padding-bottom: 30px; } @@ -17,7 +17,7 @@ .resourcesContainer { padding-bottom: 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 50px; } } @@ -25,7 +25,7 @@ .bannerContainer { height: 252px; - @include xxs-to-sm { + @include xxs-to-xs { height: 300px; } } @@ -36,14 +36,14 @@ transform: none; width: 600px; - @include xxs-to-sm { + @include xxs-to-xs { top: auto; width: 100%; } } .bannerContentInner { - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 20px; padding-top: 48px; } @@ -56,7 +56,7 @@ .joinnowWrap { margin-top: 10px; - @include xxs-to-sm { + @include xxs-to-xs { margin-top: 20px; text-align: center; } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/style.scss b/src/shared/components/tc-communities/communities/wipro/Home/style.scss index 539d6dce3b..000486da49 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/style.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/style.scss @@ -8,7 +8,7 @@ border-bottom: 1px solid #ddd; padding: 43px 38px 50px; - @include xxs-to-sm { + @include xxs-to-xs { display: flex; justify-content: space-around; padding: 30px 12px 32px; @@ -19,7 +19,7 @@ background-color: rgba(#f2f2f2, 0.5); padding: 20px 0; - @include xxs-to-sm { + @include xxs-to-xs { padding: 40px 0; } } @@ -27,7 +27,7 @@ .linksContainer { padding: 24px 0 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding: 0; } } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/IconStatStyles.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/IconStatStyles.scss index e92506f425..7680ac1d34 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/IconStatStyles.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/IconStatStyles.scss @@ -8,7 +8,7 @@ display: flex; align-items: center; - @include xxs-to-sm { + @include xxs-to-xs { display: block; text-align: center; } @@ -18,7 +18,7 @@ max-height: 100px; max-width: 100px; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 11px; margin-right: 0; } @@ -31,7 +31,7 @@ margin-top: 10px; text-transform: none; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 14px; } } @@ -44,7 +44,7 @@ color: #646363; text-align: left; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 18px; } } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/articleCardStyles.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/articleCardStyles.scss index 396a5847b0..a756552c13 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/articleCardStyles.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/articleCardStyles.scss @@ -12,7 +12,7 @@ width: 220px; } - @include xxs-to-sm { + @include xxs-to-xs { height: auto; margin: 0 15px; padding-bottom: 30px; @@ -30,7 +30,7 @@ display: block; height: 150px; - @include xxs-to-sm { + @include xxs-to-xs { height: auto; min-height: 205px; diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/banner.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/banner.scss index f86b41a2d6..0303bddb1c 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/banner.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/banner.scss @@ -22,7 +22,7 @@ height: 364px; transform: none; - @include xxs-to-sm { + @include xxs-to-xs { left: 0; bottom: 0; transform: none; @@ -35,7 +35,7 @@ position: relative; z-index: 1; - @include xxs-to-sm { + @include xxs-to-xs { padding: 86px 15px 80px; text-align: center; } @@ -48,7 +48,7 @@ h2.title { text-decoration: underline; text-transform: none; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 30px; } } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/imageTextStyle.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/imageTextStyle.scss index 96263135f5..bfe03c995a 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/imageTextStyle.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/imageTextStyle.scss @@ -22,7 +22,7 @@ padding: 0 40px 0 20px; } - @include xxs-to-sm { + @include xxs-to-xs { padding: 20px 15px 40px; text-align: center; @@ -51,7 +51,7 @@ z-index: -1; } - @include xxs-to-md { + @include xxs-to-sm { font-size: 17px; } } @@ -63,7 +63,7 @@ margin-top: 19px; opacity: 0.7; - @include xxs-to-md { + @include xxs-to-sm { margin-top: 10px; } } @@ -71,7 +71,7 @@ .linkWrap { margin-top: 27px; - @include xxs-to-md { + @include xxs-to-sm { margin-top: 20px; } } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/newsSectionStyles.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/newsSectionStyles.scss index 9cdc76cae8..d1f0d7100c 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/newsSectionStyles.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/newsSectionStyles.scss @@ -11,7 +11,7 @@ font-size: 21px; padding-top: 0; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 18px; padding-top: 0; } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/newsletter_signup.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/newsletter_signup.scss index e2da721afd..fab4ad2149 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/newsletter_signup.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/newsletter_signup.scss @@ -17,7 +17,7 @@ font-family: 'Akkurat bold', sans-serif; font-size: 21px; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 18px; } } @@ -47,7 +47,7 @@ input:not([type="checkbox"]).formEmail { line-height: 40px; height: 40px; - @include xxs-to-sm { + @include xxs-to-xs { width: 50%; } @@ -80,7 +80,7 @@ input:not([type="checkbox"]).formEmail { width: auto; padding: 0 30px 0 20px; - @include xxs-to-sm { + @include xxs-to-xs { width: 135px; } } diff --git a/src/shared/components/tc-communities/communities/wipro/Home/themes/resourceCardStyles.scss b/src/shared/components/tc-communities/communities/wipro/Home/themes/resourceCardStyles.scss index 11c7807739..3b7b7d8af6 100644 --- a/src/shared/components/tc-communities/communities/wipro/Home/themes/resourceCardStyles.scss +++ b/src/shared/components/tc-communities/communities/wipro/Home/themes/resourceCardStyles.scss @@ -8,7 +8,7 @@ width: 200px; } - @include xxs-to-sm { + @include xxs-to-xs { padding: 0 15px; width: auto; @@ -40,7 +40,7 @@ top: 0; } - @include xxs-to-sm { + @include xxs-to-xs { font-size: 16px; } } diff --git a/src/shared/components/tc-communities/communities/wipro/Learn/style.scss b/src/shared/components/tc-communities/communities/wipro/Learn/style.scss index 3480f5f624..c781aff4e5 100644 --- a/src/shared/components/tc-communities/communities/wipro/Learn/style.scss +++ b/src/shared/components/tc-communities/communities/wipro/Learn/style.scss @@ -8,7 +8,7 @@ padding-bottom: 50px; padding-top: 0; - @include xxs-to-sm { + @include xxs-to-xs { font-size: 36px; padding-bottom: 30px; } @@ -17,7 +17,7 @@ .resourcesContainer { padding-bottom: 60px; - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 50px; } } @@ -25,7 +25,7 @@ .bannerContainer { height: 252px; - @include xxs-to-sm { + @include xxs-to-xs { height: 300px; } } @@ -36,14 +36,14 @@ transform: none; width: 600px; - @include xxs-to-sm { + @include xxs-to-xs { top: auto; width: 100%; } } .bannerContentInner { - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 20px; padding-top: 48px; } @@ -56,7 +56,7 @@ .joinnowWrap { margin-top: 10px; - @include xxs-to-sm { + @include xxs-to-xs { margin-top: 20px; text-align: center; } diff --git a/src/shared/containers/Dashboard/styles.scss b/src/shared/containers/Dashboard/styles.scss index e370e9a483..ba21005ffe 100644 --- a/src/shared/containers/Dashboard/styles.scss +++ b/src/shared/containers/Dashboard/styles.scss @@ -1,4 +1,4 @@ -@import '~styles/tc-includes'; +@import '~styles/tc-styles'; .dashboard-container { background-color: #f6f6f6; diff --git a/src/shared/containers/Leaderboard/styles.scss b/src/shared/containers/Leaderboard/styles.scss index a5f1f69385..00ab959d63 100644 --- a/src/shared/containers/Leaderboard/styles.scss +++ b/src/shared/containers/Leaderboard/styles.scss @@ -32,7 +32,7 @@ .bannerContainer { height: 252px; - @include xxs-to-sm { + @include xxs-to-xs { height: 300px; } } @@ -43,14 +43,14 @@ transform: none; width: 600px; - @include xxs-to-sm { + @include xxs-to-xs { top: auto; width: 100%; } } .bannerContentInner { - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 20px; padding-top: 48px; } diff --git a/src/shared/containers/challenge-detail/index.jsx b/src/shared/containers/challenge-detail/index.jsx index 4187a82bd7..78b8dd48f2 100644 --- a/src/shared/containers/challenge-detail/index.jsx +++ b/src/shared/containers/challenge-detail/index.jsx @@ -67,6 +67,9 @@ class ChallengeDetailPageContainer extends React.Component { showDeadlineDetail={this.state.showDeadlineDetail} onToggleDeadlines={this.onToggleDeadlines} onSelectorClicked={this.onSelectorClicked} + registerForChallenge={challengeId => + this.props.registerForChallenge(this.props.tokenV2, challengeId) + } selectedView={this.state.selectedView} /> } @@ -84,17 +87,20 @@ class ChallengeDetailPageContainer extends React.Component { } ChallengeDetailPageContainer.defaultProps = { + tokenV2: null, tokenV3: null, isLoadingChallenge: false, }; ChallengeDetailPageContainer.propTypes = { + tokenV2: PT.string, tokenV3: PT.string, challenge: PT.shape().isRequired, isLoadingChallenge: PT.bool, loadChallengeDetails: PT.func.isRequired, authTokens: PT.shape().isRequired, challengeId: PT.number.isRequired, + registerForChallenge: PT.func.isRequired, reloadChallengeDetails: PT.func.isRequired, }; @@ -131,18 +137,26 @@ const mapStateToProps = (state, props) => ({ challenge: extractChallengeDetail(state.challenge.details, state.challenge.detailsV2), isLoadingChallenge: state.challenge.loadingDetails, authTokens: state.auth, - tokenV3: (state.auth ? state.auth.tokenV3 : null), + tokenV2: state.auth && state.auth.tokenV2, + tokenV3: state.auth && state.auth.tokenV3, }); -const mapDispatchToProps = dispatch => ({ - loadChallengeDetails: (tokens, challengeId) => { - dispatch(challengeActions.fetchChallengeInit()); - dispatch(challengeActions.fetchChallengeDone(tokens, challengeId)); - }, - reloadChallengeDetails: (tokens, challengeId) => { - dispatch(challengeActions.fetchChallengeDone(tokens, challengeId)); - }, -}); +const mapDispatchToProps = (dispatch) => { + const a = challengeActions.challenge; + return { + loadChallengeDetails: (tokens, challengeId) => { + dispatch(challengeActions.fetchChallengeInit()); + dispatch(challengeActions.fetchChallengeDone(tokens, challengeId)); + }, + registerForChallenge: (tokenV2, challengeId) => { + dispatch(a.registerInit()); + dispatch(a.registerDone(tokenV2, challengeId)); + }, + reloadChallengeDetails: (tokens, challengeId) => { + dispatch(challengeActions.fetchChallengeDone(tokens, challengeId)); + }, + }; +}; const ChallengeDetailContainer = connect( mapStateToProps, diff --git a/src/shared/containers/challenge-detail/styles.scss b/src/shared/containers/challenge-detail/styles.scss index f5ee78306c..a185ca1816 100644 --- a/src/shared/containers/challenge-detail/styles.scss +++ b/src/shared/containers/challenge-detail/styles.scss @@ -1,17 +1,9 @@ @import "~styles/tc-styles"; -@import "~styles/tc-includes"; - -// Should this be added to media mixins? -@mixin md-and-up { - @media (min-width: #{$md-width}) { - @content; - } -} .outer-container { background: $tc-gray-10; - @include xxs-to-sm { + @include xxs-to-xs { padding: 8px 10px 20px; } @@ -19,7 +11,7 @@ padding: 30px 14px 30px 20px; } - @include md-and-up { + @include md-to-lg { padding: 30px 64px; } } diff --git a/src/shared/containers/challenge-listing/Listing/styles.scss b/src/shared/containers/challenge-listing/Listing/styles.scss index 7b99569944..296c0f66aa 100644 --- a/src/shared/containers/challenge-listing/Listing/styles.scss +++ b/src/shared/containers/challenge-listing/Listing/styles.scss @@ -53,7 +53,7 @@ $sm-space-40: $base-unit * 8; .bannerContainer { height: 252px; - @include xxs-to-sm { + @include xxs-to-xs { height: 300px; } } @@ -64,14 +64,14 @@ $sm-space-40: $base-unit * 8; transform: none; width: 600px; - @include xxs-to-sm { + @include xxs-to-xs { top: auto; width: 100%; } } .bannerContentInner { - @include xxs-to-sm { + @include xxs-to-xs { padding-bottom: 20px; padding-top: 48px; } diff --git a/src/shared/services/challenges.js b/src/shared/services/challenges.js index d7d5a7b45a..2105790dd4 100644 --- a/src/shared/services/challenges.js +++ b/src/shared/services/challenges.js @@ -95,8 +95,9 @@ export function normalizeMarathonMatch(challenge, username) { class ChallengesService { /** * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. + * @param {String} tokenV2 Optional. Auth token for Topcoder API v2. */ - constructor(tokenV3) { + constructor(tokenV3, tokenV2) { /** * Private function being re-used in all methods related to getting * challenges. It handles query-related arguments in the uniform way: @@ -127,8 +128,9 @@ class ChallengesService { this.private = { api: getApiV3(tokenV3), - apiV2: getApiV2(), + apiV2: getApiV2(tokenV2), getChallenges, + tokenV2, tokenV3, }; } @@ -219,17 +221,41 @@ class ChallengesService { return res; }); } + + /** + * Registers user to the specified challenge. + * @param {String} challengeId + * @return {Promise} + */ + register(challengeId) { + const endpoint = `/challenges/${challengeId}/register`; + return this.private.apiV2.get(endpoint) + .then(res => (res.ok ? res.json() : new Error(res.statusText))); + } + + /** + * Unregisters user from the specified challenge. + * @param {String} challengeId + * @return {Promise} + */ + unregister(challengeId) { + const endpoint = `/challenges/${challengeId}/unregister`; + return this.private.apiV2.get(endpoint) + .then(res => (res.ok ? res.json() : new Error(res.statusText))); + } } /** * Returns a new or existing challenges service. * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. + * @param {String} tokenV2 Optional. Auth token for Topcoder API v2. * @return {Challenges} Challenges service object */ let lastInstance = null; -export function getService(tokenV3) { - if (!lastInstance || lastInstance.tokenV3 !== tokenV3) { - lastInstance = new ChallengesService(tokenV3); +export function getService(tokenV3, tokenV2) { + if (!lastInstance || lastInstance.tokenV3 !== tokenV3 + || lastInstance.tokenV2 !== tokenV2) { + lastInstance = new ChallengesService(tokenV3, tokenV2); } return lastInstance; } diff --git a/src/shared/utils/tc.js b/src/shared/utils/tc.js index b5a3f33fde..3102f76802 100644 --- a/src/shared/utils/tc.js +++ b/src/shared/utils/tc.js @@ -10,12 +10,23 @@ import config from './config'; /** * Codes of the Topcoder communities. */ +/* TODO: These are originally motivated by Topcoder API v2. Topcoder API v3 + * uses upper-case literals to encode the tracks. At some point, we should + * update it in this code as well! */ export const COMPETITION_TRACKS = { DATA_SCIENCE: 'datasci', DESIGN: 'design', DEVELOP: 'develop', }; +/** + * Possible user roles in a challenge (at the moment it is not a full list, + * just those we already have used in this repo for any purpose). + */ +export const USER_ROLES = { + SUBMITTER: 'Submitter', +}; + /** * Given a rating value, returns corresponding color. * @param {Number} rating Rating. diff --git a/src/styles/_tc-includes.scss b/src/styles/_tc-includes.scss deleted file mode 100644 index 1f7c5f701f..0000000000 --- a/src/styles/_tc-includes.scss +++ /dev/null @@ -1,3 +0,0 @@ -// @import 'bourbon'; -@import 'mixins'; -@import 'variables'; diff --git a/src/styles/_tc-styles.scss b/src/styles/_tc-styles.scss index 7f5b8353d0..417c95cfa0 100644 --- a/src/styles/_tc-styles.scss +++ b/src/styles/_tc-styles.scss @@ -1,6 +1,8 @@ @import 'reset'; // Use to list all modules to include +@import 'mixins'; +@import 'variables'; @import 'typography'; @import 'buttons'; @import 'checkboxes'; diff --git a/src/styles/_typography.scss b/src/styles/_typography.scss index 5fdc07698f..5b878f7c20 100644 --- a/src/styles/_typography.scss +++ b/src/styles/_typography.scss @@ -1,5 +1,3 @@ -@import 'tc-includes'; - html, body { text-rendering: geometricPrecision; diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 544835b271..c620963606 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -14,22 +14,45 @@ $base-unit: 5px; $corner-radius: 2px; +/* COLORS */ + +/* + * NOTE: Topcoder brand colors are listed at + * https://www.topcoder.com/about-topcoder/brand-guidelines/ + * + * According to that page, the primary colors are (of the ones defined below): + * $tc-dark-blue + * $tc-gray-90 + * $tc-gray-30 + * and secondary colors are: + * $tc-orange + * $tc-green + * $tc-yellow + * $tc-red + * $tc-purple + * + * Other colors, already present below, are fine. If you have to use a color + * slighly different from the colors available below, please consider to use + * the closest available color instead; i.g. if you need #c1c1c6 color, in + * most cases nobody will notice if you use $tc-gray-30 (#c3c3c7) instead. + */ + // Colors // // Primary Brand $tc-light-blue: #26ade9; -$tc-dark-blue: #0b71e6; +$tc-dark-blue: #0a71e6; $tc-green: #60c700; -$tc-orange: #fb7d22; -$tc-yellow: #fff100; +$tc-orange: #fb7d21; +$tc-yellow: #fce217; $tc-black: #262628; -$tc-gray-90: #37373c; +$tc-gray-90: #37373b; $tc-gray-80: #47474f; $tc-gray-70: #5d5d66; $tc-gray-60: #747480; $tc-gray-50: #888894; $tc-gray-40: #a3a3ad; -$tc-gray-30: #c3c3c8; +$tc-gray-30: #c3c3c7; $tc-gray-20: #dcdce0; $tc-gray-10: #ededf2; $tc-gray-neutral-dark: #ebebeb; @@ -54,7 +77,7 @@ $tc-orange: $tc-orange; $tc-orange-70: #fda464; $tc-orange-30: #fee3d0; $tc-orange-10: #fff0eb; -$tc-red: #f22f24; +$tc-red: #f22e24; $tc-red-70: #ff5b52; $tc-red-30: #ffd4d1; $tc-red-10: #fff4f4; @@ -110,7 +133,7 @@ $tc-heading-sm: $tc-body-md; $tc-heading-xs: $tc-body-sm; // breakpoints -$screen-lg: 1376px; -$screen-md: 768px; -$screen-sm: 640px; $screen-xs: 320px; +$screen-sm: 768px; +$screen-md: 1024px; +$screen-lg: 1280px; diff --git a/src/styles/mixins/_media.scss b/src/styles/mixins/_media.scss index ed2f0d0d78..dc43188089 100644 --- a/src/styles/mixins/_media.scss +++ b/src/styles/mixins/_media.scss @@ -1,76 +1,85 @@ -// Create mixin and render the 4 sizes - lg, md, sm, xs +/** + * Mixins for different layout sizes: xs, sm, md, lg. + * Breaking points are defined in _variables.scss + * The range mixins A-to-B all means "for the sizes from A to B, both + * inclusive", in particular it means that mixin A-to-lg is equivalent to + * all sizes from A (inclusive) and larger. + */ -// extra small size -$xs-width: 320px; -// small size - mobile -$sm-width: 768px; -// medium size - tablet -$md-width: 1024px; -// large size - desktop -$lg-width: 1280px; +@mixin lg { + @media (min-width: #{$screen-lg}) { + @content; + } +} -@mixin xxs { - @media (max-width: #{$xs-width - 1px}) { +@mixin md { + @media (min-width: #{$screen-md + 1px}) and (max-width: #{$screen-lg - 1px}) { @content; } } -@mixin xxs-to-sm { - @media (max-width: #{$sm-width - 1px}) { +@mixin md-to-lg { + @media (min-width: #{$screen-md}) { @content; } } -@mixin xxs-to-md { - @media (max-width: #{$md-width - 1px}) { +@mixin sm { + @media (min-width: #{$screen-sm}) and (max-width: #{$screen-md - 1px}) { @content; } } -@mixin xxs-to-lg { - @media (max-width: #{$lg-width - 1px}) { +@mixin sm-to-lg { + @media (min-width: #{$screen-sm}) { @content; } } -@mixin xs { - @media (min-width: #{$xs-width}) and (max-width: #{$sm-width - 1px}) { +@mixin sm-to-md { + @media (min-width: #{$screen-sm}) and (max-width: #{$screen-lg - 1px}) { @content; } } -@mixin sm { - @media (min-width: #{$sm-width}) and (max-width: #{$md-width}) { +@mixin xs { + @media (min-width: #{$screen-xs}) and (max-width: #{$screen-sm - 1px}) { @content; } } -@mixin sm-and-up { - @media (min-width: #{$sm-width}) { +@mixin xs-to-lg { + @media (min-width: #{$screen-xs}) { @content; } } @mixin xs-to-sm { - @media (min-width: #{$xs-width}) and (max-width: #{$md-width} - 1px) { + @media (min-width: #{$screen-xs}) and (max-width: #{$screen-md - 1px}) { @content; } } -@mixin xs-to-md { - @media (min-width: #{$xs-width}) and (max-width: #{$lg-width - 1px}) { +@mixin xxs { + @media (max-width: #{$screen-xs - 1px}) { @content; } } -@mixin md { - @media (min-width: #{$md-width + 1px}) and (max-width: #{$lg-width - 1px}) { +@mixin xxs-to-md { + @media (max-width: #{$screen-lg - 1px}) { @content; } } -@mixin lg { - @media (min-width: #{$lg-width}) { +@mixin xxs-to-sm { + @media (max-width: #{$screen-md - 1px}) { + @content; + } +} + +@mixin xxs-to-xs { + @media (max-width: #{$screen-sm - 1px}) { @content; } } From b1d011c024449631ede5660784f8d90679c06c9b Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Sun, 30 Jul 2017 14:34:29 +0200 Subject: [PATCH 05/13] Minor enhancement of our custom Link and NavLink wrappers Fixes #115 --- .../examples/__snapshots__/Content.jsx.snap | 132 ++++-------------- .../__snapshots__/Header.jsx.snap | 3 - .../tc-communities/Header/index.jsx | 1 - .../tc-communities/ImageText/index.jsx | 2 +- src/shared/utils/router.jsx | 113 ++++++++++----- 5 files changed, 109 insertions(+), 142 deletions(-) diff --git a/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap b/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap index cc85923a7e..09764a7a24 100644 --- a/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap +++ b/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap @@ -101,86 +101,62 @@ exports[`Matches shallow shapshot 1`] = `

    - New Topcoder Pages + Main Topcoder website

      -
    • - - Submission Management Page - - – New submission management page, is available at the endpoint - - /challenge/:challengeId/my-submissions - - . The link here leads to the test challenge. -
    • - Main Website Challenge Listing Page + Challenge Details Page - – Ported Challenge Listing for the main website. + – generally availabel at the endpoint + + /challenges/:challengeId + + ; the link here leads to a sample challenge.
    • - Community Challenge Listing Page + Dashboard - – An example of community challenge list apge which shows only challenges with special criteria. In this case only challenges which has JavaScript technology tag.
    • - Challenge Management Page + Main Challenge Listing - – Ported Challenge detail for the main website.
    • - - Leaderboard - - – Leaderboard page. + Submission Management Page + + – generally available at the endpoint + + /challenges/:challengeId/my-submissions + + ; the link here leads to a test challenge.
    • +
    +

    + Separate Topcoder Communities +

    +
    • - Community header example - - – An example of cummunity header with default style. Also, there are examples of - - custom red theme - - , - - custom green theme - - and - - non-existent community page + Community 2 - .
    • - Topcoder Product Development - -
    • -
    • - - Wipro Community Homepage - - – Example of community implementation with new design. This community has three more pages: - - Learn - - , - - Challenges + Topcoder Product Development community - and - - Leaderboard - - . There is also an example of - - non-existent community page - - .
    • - Community 2 - -
    • -
    • - - Dashboard + TopGear (Wipro) community - – Dashboard page.

    diff --git a/__tests__/shared/components/tc-communities/__snapshots__/Header.jsx.snap b/__tests__/shared/components/tc-communities/__snapshots__/Header.jsx.snap index 82b07277d4..b8681cbbb8 100644 --- a/__tests__/shared/components/tc-communities/__snapshots__/Header.jsx.snap +++ b/__tests__/shared/components/tc-communities/__snapshots__/Header.jsx.snap @@ -179,7 +179,6 @@ exports[`Snapshot match 2`] = ` activeClassName="menu-link_active tc-communities__header__menu-link_active" className="tc-communities__header__menu-link src-shared-components-tc-communities-Header-___style__menu-link___1pPZf" isActive={[Function]} - openExternalLinkInNewPage={true} to="pageId1" > Menu Item 1 @@ -192,7 +191,6 @@ exports[`Snapshot match 2`] = ` activeClassName="menu-link_active tc-communities__header__menu-link_active" className="tc-communities__header__menu-link src-shared-components-tc-communities-Header-___style__menu-link___1pPZf" isActive={[Function]} - openExternalLinkInNewPage={true} to="pageId2" > Menu Item 2 @@ -205,7 +203,6 @@ exports[`Snapshot match 2`] = ` activeClassName="menu-link_active tc-communities__header__menu-link_active" className="tc-communities__header__menu-link src-shared-components-tc-communities-Header-___style__menu-link___1pPZf" isActive={[Function]} - openExternalLinkInNewPage={true} to="pageId3" > Menu Item 3 diff --git a/src/shared/components/tc-communities/Header/index.jsx b/src/shared/components/tc-communities/Header/index.jsx index e3784dc14f..4db07ee5da 100644 --- a/src/shared/components/tc-communities/Header/index.jsx +++ b/src/shared/components/tc-communities/Header/index.jsx @@ -184,7 +184,6 @@ export default function Header(props) { className="tc-communities__header__menu-link" activeClassName="menu-link_active tc-communities__header__menu-link_active" isActive={() => currentPage === item.url} - openExternalLinkInNewPage to={item.url} > {item.title} diff --git a/src/shared/components/tc-communities/ImageText/index.jsx b/src/shared/components/tc-communities/ImageText/index.jsx index 1f20d7bc9a..bd2fbe91b6 100644 --- a/src/shared/components/tc-communities/ImageText/index.jsx +++ b/src/shared/components/tc-communities/ImageText/index.jsx @@ -18,7 +18,7 @@ function ImageText(props) {
    {item.title}
    diff --git a/src/shared/utils/router.jsx b/src/shared/utils/router.jsx index 67f587a824..982024801d 100644 --- a/src/shared/utils/router.jsx +++ b/src/shared/utils/router.jsx @@ -1,5 +1,5 @@ /** - * Various utils that faciliate usage of react-router. + * Various utils that faciliate the usage of react-router. */ import PT from 'prop-types'; @@ -13,53 +13,73 @@ import { Link as RRLink, NavLink as RRNavLink } from 'react-router-dom'; * Original components work properly only with URLs reffering routes within the * app. Our versions of and compare the URL hostname with the * hostname of the page where they are rendered: + * * 1) If the same, the hyper-reference is rendered as react-router's or * , thus avoiding re-load of the app; + * * 2) If different, the hyper-reference is rendered as HTML element, thus * properly leading visitors outside of the app. * + * 3) Additionally, the links are rendered as HTML element if "openNewTab" + * prop is passed in (in this case target="_blank" is passed into , to + * make sure that the link will be opened in a new browser tab). Also, the + * link is rendered as if it starts with # symbol (i.e. it is supposed + * to scroll the currenty page to an ankor point). + * * NOTE that assumption (1) can be wrong (i.e. we can configure our server in * such way that different endpoints in the same domain are served by different * web applications). However, in many use cases the assumption (1) holds, and * as it allows an elegant solution, we implement it here. */ -function RRLinkWrapper(props) { - const url = new URL(props.to); - if ((props.hostname !== url.hostname) - || props.to.startsWith('#')) { +function RRLinkWrapper({ + children, + className, + enforceA, + hostname, + onClick, + openNewTab, + replace, + to, +}) { + const url = new URL(to); + if (enforceA || openNewTab || (hostname !== url.hostname) + || to.startsWith('#')) { return ( {props.children} + className={className} + href={to} + onClick={onClick} + target={openNewTab ? '_blank' : ''} + >{children} ); } return ( {props.children} + className={className} + onClick={onClick} + replace={replace} + to={to} + >{children} ); } RRLinkWrapper.defaultProps = { children: null, className: null, + enforceA: false, onClick: null, - openExternalLinkInNewPage: false, + openNewTab: false, replace: false, }; RRLinkWrapper.propTypes = { children: PT.node, className: PT.string, + enforceA: PT.bool, hostname: PT.string.isRequired, onClick: PT.func, - openExternalLinkInNewPage: PT.bool, + openNewTab: PT.bool, replace: PT.bool, to: PT.oneOfType([PT.object, PT.string]).isRequired, }; @@ -68,34 +88,51 @@ export const Link = connect(state => ({ hostname: state.hostname, }))(RRLinkWrapper); -function RRNavLinkWrapper(props) { - const url = new URL(props.to); - if ((props.hostname !== url.hostname) - || props.to.startsWith('#')) { +function RRNavLinkWrapper({ + activeClassName, + activeStyle, + children, + className, + enforceA, + exact, + hostname, + isActive, + location, + onClick, + openNewTab, + replace, + strict, + to, +}) { + const url = new URL(to); + if (enforceA || openNewTab || (hostname !== url.hostname) + || to.startsWith('#')) { return ( /* NOTE: Currently we don't handle isActive check here. Though, as this * element is a fallback for URLs leading outside of the app, in * usual use cases it never should be rendered as active within the app. */ {props.children} + className={className} + href={to} + onClick={onClick} + target={openNewTab ? '_blank' : ''} + >{children} ); } return ( {props.children} + activeClassName={activeClassName} + activeStyle={activeStyle} + className={className} + exact={exact} + isActive={isActive} + location={location} + onClick={onClick} + replace={replace} + strict={strict} + to={to} + >{children} ); } @@ -104,9 +141,11 @@ RRNavLinkWrapper.defaultProps = { activeStyle: null, children: null, className: null, + enforceA: false, exact: false, location: null, - openExternalLinkInNewPage: false, + onClick: null, + openNewTab: false, replace: false, strict: false, }; @@ -116,11 +155,13 @@ RRNavLinkWrapper.propTypes = { activeStyle: PT.shape(), children: PT.node, className: PT.string, + enforceA: PT.bool, exact: PT.bool, hostname: PT.string.isRequired, isActive: PT.func.isRequired, location: PT.shape(), - openExternalLinkInNewPage: PT.bool, + onClick: PT.func, + openNewTab: PT.bool, replace: PT.bool, strict: PT.bool, to: PT.string.isRequired, From 6d758fb14e76c213cf1fe135706cead321052cde Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Sun, 30 Jul 2017 20:16:32 +0200 Subject: [PATCH 06/13] New solution for standard context-themable buttons The new button component(s) allows to create buttons and button-like links in a convenient and consistent way. It also allows to style such buttons using react-css-themr. See the demo at /examples/buttons/ endpoint --- .../examples/__snapshots__/Content.jsx.snap | 13 ++ config/webpack/default.js | 1 - config/webpack/production.js | 1 + src/shared/components/buttons/danger.scss | 42 ++++++ src/shared/components/buttons/default.scss | 59 ++++++++ src/shared/components/buttons/index.jsx | 126 ++++++++++++++++++ .../buttons/primaryDataScience.scss | 41 ++++++ .../components/buttons/primaryDesign.scss | 41 ++++++ .../components/buttons/primaryDevelop.scss | 41 ++++++ src/shared/components/buttons/secondary.scss | 41 ++++++ .../components/examples/Buttons/index.jsx | 77 +++++++++++ .../components/examples/Buttons/style.scss | 32 +++++ .../components/examples/Content/index.jsx | 5 + src/shared/routes/examples/index.jsx | 2 + src/styles/_buttons.scss | 43 ++++++ 15 files changed, 564 insertions(+), 1 deletion(-) create mode 100644 src/shared/components/buttons/danger.scss create mode 100644 src/shared/components/buttons/default.scss create mode 100644 src/shared/components/buttons/index.jsx create mode 100644 src/shared/components/buttons/primaryDataScience.scss create mode 100644 src/shared/components/buttons/primaryDesign.scss create mode 100644 src/shared/components/buttons/primaryDevelop.scss create mode 100644 src/shared/components/buttons/secondary.scss create mode 100644 src/shared/components/examples/Buttons/index.jsx create mode 100644 src/shared/components/examples/Buttons/style.scss diff --git a/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap b/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap index 09764a7a24..463527c608 100644 --- a/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap +++ b/__tests__/shared/components/examples/__snapshots__/Content.jsx.snap @@ -195,6 +195,19 @@ exports[`Matches shallow shapshot 1`] = ` Misc Examples