From 3507ab71ddb02d13fb1310711897af60f3d1c8fc Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Wed, 14 Jun 2017 00:51:19 +0200 Subject: [PATCH 1/3] Fix - Challenge Listing - No prize tooltip on some challenges In challenge listing prize tooltips were missing for challenges with more than 3 placements. This is the fix. --- .../challenge-listing/Tooltips/PrizesTooltip/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shared/components/challenge-listing/Tooltips/PrizesTooltip/index.jsx b/src/shared/components/challenge-listing/Tooltips/PrizesTooltip/index.jsx index a559e18e77..832b016507 100644 --- a/src/shared/components/challenge-listing/Tooltips/PrizesTooltip/index.jsx +++ b/src/shared/components/challenge-listing/Tooltips/PrizesTooltip/index.jsx @@ -41,9 +41,13 @@ Bonus.propTypes = { * and the prize, formatted as currency, next to it. */ function Prize(props) { + let medalStyleName = 'medal'; + if (props.place <= 3) { + medalStyleName += ` place-${props.place}`; + } return (
- {props.place} + {props.place} ${props.prize.toLocaleString()}
); From 56f7e2dc49b798760d732f7d3c4aba533acc3df5 Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Wed, 14 Jun 2017 15:01:09 +0200 Subject: [PATCH 2/3] Stats integration with real data Fixes #62. Current implementation shows: - Number of active challenges - Number of members in the group - Total prize of active challenges Number of projects is not rendered for now. --- .../components/tc-communities/IconStat.jsx | 13 +- .../__snapshots__/IconStat.jsx.snap | 44 +-- .../communities/demo-expert/Home.jsx | 12 + .../communities/demo-expert/Learn.jsx | 12 + .../demo-expert/__snapshots__/Home.jsx.snap | 130 +++++++ .../demo-expert/__snapshots__/Learn.jsx.snap | 368 ++++++++++++++++++ .../communities/tc-prod-dev/Home.jsx | 12 + .../communities/tc-prod-dev/Learn.jsx | 12 + .../tc-prod-dev/__snapshots__/Home.jsx.snap | 130 +++++++ .../tc-prod-dev/__snapshots__/Learn.jsx.snap | 356 +++++++++++++++++ .../communities/{wipro2 => wipro}/Home.jsx | 0 .../communities/{wipro2 => wipro}/Learn.jsx | 0 .../__snapshots__/Home.jsx.snap | 46 +-- .../__snapshots__/Learn.jsx.snap | 0 src/shared/actions/stats.js | 58 +++ .../tc-communities/GroupStats/index.jsx | 69 ++++ .../tc-communities/GroupStats/style.scss | 16 + .../tc-communities/IconStat/index.jsx | 2 +- .../communities/demo-expert/Home/index.jsx | 18 +- .../communities/tc-prod-dev/Home/index.jsx | 18 +- .../communities/wipro/Home/index.jsx | 18 +- .../containers/tc-communities/GroupStats.jsx | 64 +++ src/shared/reducers/index.js | 2 + src/shared/reducers/stats.js | 56 +++ src/shared/services/groups.js | 50 +++ 25 files changed, 1376 insertions(+), 130 deletions(-) create mode 100644 __tests__/shared/components/tc-communities/communities/demo-expert/Home.jsx create mode 100644 __tests__/shared/components/tc-communities/communities/demo-expert/Learn.jsx create mode 100644 __tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Home.jsx.snap create mode 100644 __tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Learn.jsx.snap create mode 100644 __tests__/shared/components/tc-communities/communities/tc-prod-dev/Home.jsx create mode 100644 __tests__/shared/components/tc-communities/communities/tc-prod-dev/Learn.jsx create mode 100644 __tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Home.jsx.snap create mode 100644 __tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Learn.jsx.snap rename __tests__/shared/components/tc-communities/communities/{wipro2 => wipro}/Home.jsx (100%) rename __tests__/shared/components/tc-communities/communities/{wipro2 => wipro}/Learn.jsx (100%) rename __tests__/shared/components/tc-communities/communities/{wipro2 => wipro}/__snapshots__/Home.jsx.snap (78%) rename __tests__/shared/components/tc-communities/communities/{wipro2 => wipro}/__snapshots__/Learn.jsx.snap (100%) create mode 100644 src/shared/actions/stats.js create mode 100644 src/shared/components/tc-communities/GroupStats/index.jsx create mode 100644 src/shared/components/tc-communities/GroupStats/style.scss create mode 100644 src/shared/containers/tc-communities/GroupStats.jsx create mode 100644 src/shared/reducers/stats.js create mode 100644 src/shared/services/groups.js diff --git a/__tests__/shared/components/tc-communities/IconStat.jsx b/__tests__/shared/components/tc-communities/IconStat.jsx index 0c00569992..bdb14e2273 100644 --- a/__tests__/shared/components/tc-communities/IconStat.jsx +++ b/__tests__/shared/components/tc-communities/IconStat.jsx @@ -1,20 +1,13 @@ import React from 'react'; -import Rnd from 'react-test-renderer/shallow'; +import Render from 'react-test-renderer'; import IconStat from 'components/tc-communities/IconStat'; -const rnd = new Rnd(); - function Icon() { return
; } test('Snapshot match', () => { - rnd.render(( - - )); - expect(rnd.getRenderOutput()).toMatchSnapshot(); - - rnd.render(( + const render = Render.create(( { }} /> )); - expect(rnd.getRenderOutput()).toMatchSnapshot(); + expect(render.toJSON()).toMatchSnapshot(); }); diff --git a/__tests__/shared/components/tc-communities/__snapshots__/IconStat.jsx.snap b/__tests__/shared/components/tc-communities/__snapshots__/IconStat.jsx.snap index 8ab4a9b73f..b4c5b3d144 100644 --- a/__tests__/shared/components/tc-communities/__snapshots__/IconStat.jsx.snap +++ b/__tests__/shared/components/tc-communities/__snapshots__/IconStat.jsx.snap @@ -1,33 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Snapshot match 1`] = ` - -`; - -exports[`Snapshot match 2`] = ` - +
+
+

+ 5 +

+

+ Projects +

+
`; diff --git a/__tests__/shared/components/tc-communities/communities/demo-expert/Home.jsx b/__tests__/shared/components/tc-communities/communities/demo-expert/Home.jsx new file mode 100644 index 0000000000..45b7ddc32b --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/demo-expert/Home.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Rnd from 'react-test-renderer/shallow'; +import Home from 'components/tc-communities/communities/demo-expert/Home'; + +const rnd = new Rnd(); + +test('Snapshot match', () => { + rnd.render(( + + )); + expect(rnd.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/__tests__/shared/components/tc-communities/communities/demo-expert/Learn.jsx b/__tests__/shared/components/tc-communities/communities/demo-expert/Learn.jsx new file mode 100644 index 0000000000..830461a8b8 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/demo-expert/Learn.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Rnd from 'react-test-renderer/shallow'; +import Learn from 'components/tc-communities/communities/demo-expert/Learn'; + +const rnd = new Rnd(); + +test('Snapshot match', () => { + rnd.render(( + + )); + expect(rnd.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/__tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Home.jsx.snap b/__tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Home.jsx.snap new file mode 100644 index 0000000000..3355ea2778 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Home.jsx.snap @@ -0,0 +1,130 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Snapshot match 1`] = ` +
+ + + +
+ + +
+
+ + + + + + + +
+`; diff --git a/__tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Learn.jsx.snap b/__tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Learn.jsx.snap new file mode 100644 index 0000000000..d7c8f55708 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/demo-expert/__snapshots__/Learn.jsx.snap @@ -0,0 +1,368 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Snapshot match 1`] = ` +
+ + + + + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+ + Join Now + +
+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+ + Join Now + +
+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+ + Join Now + +
+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+
+
+
+ + + + + + + + + + + +
+`; diff --git a/__tests__/shared/components/tc-communities/communities/tc-prod-dev/Home.jsx b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/Home.jsx new file mode 100644 index 0000000000..c0071cd127 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/Home.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Rnd from 'react-test-renderer/shallow'; +import Home from 'components/tc-communities/communities/tc-prod-dev/Home'; + +const rnd = new Rnd(); + +test('Snapshot match', () => { + rnd.render(( + + )); + expect(rnd.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/__tests__/shared/components/tc-communities/communities/tc-prod-dev/Learn.jsx b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/Learn.jsx new file mode 100644 index 0000000000..2a5992a909 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/Learn.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Rnd from 'react-test-renderer/shallow'; +import Learn from 'components/tc-communities/communities/tc-prod-dev/Learn'; + +const rnd = new Rnd(); + +test('Snapshot match', () => { + rnd.render(( + + )); + expect(rnd.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/__tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Home.jsx.snap b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Home.jsx.snap new file mode 100644 index 0000000000..cb83de32b7 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Home.jsx.snap @@ -0,0 +1,130 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Snapshot match 1`] = ` +
+ + + +
+ + +
+
+ + + + + + + +
+`; diff --git a/__tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Learn.jsx.snap b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Learn.jsx.snap new file mode 100644 index 0000000000..81b74c58b5 --- /dev/null +++ b/__tests__/shared/components/tc-communities/communities/tc-prod-dev/__snapshots__/Learn.jsx.snap @@ -0,0 +1,356 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Snapshot match 1`] = ` +
+ + + + + +

+ First things first, ...make sure you sign up for Topcoder. It's quick and painless and opens the door to a whole new world. +

+
+ + Join Now + +
+
+
+ + +

+ It's likely that you'll be working on code in a specific git repo. We have many of them, so be sure to ask if you're not sure which one you shoudl be looking at. +

+

+ In general, you'll typically find them at https://github.com/topcoder-platform +

+
+
+ + +

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+

+ Nam dapibus nisl vitae elit fringilla rutrum. Aenean sollicitudin, erat a elementum rutrum, neque sem pretium metus, quis mollis nisl nunc et massa. Vestibulum sed metus in lorem tristique ullamcorper id vitae erat. Nulla mollis sapien sollicitudin lacinia lacinia. Vivamus facilisis dolor et massa placerat, at vestibulum nisl egestas. Nullam rhoncus lacus non odio luctus, eu condimentum mauris ultrices. Praesent blandit, augue a posuere aliquam, arcu tortor feugiat turpis, +

+
+ + Join Now + +
+
+
+ + +

+ If you'd like to be a copilot for Topcoder projects, send an email to support@topcoder.com and tell us a little about yourself. +

+

+ If you'd like to work on challenges, check out what's active on the + + challenges + + page. If there is nothing active, send us a note and tell us to stop slacking! :) +

+
+ + Join Now + +
+
+
+ + +

+ Make sure you've setup your payment preferences at https://community.topcoder.com/tc?module=EditPaymentPreferences. When you've earned some money and want to withdraw it, you can do that at https://community.topcoder.com/PactsMemberServlet?module=PaymentHistory&full_list=false +

+
+
+ + +

+ Everything to do with managing your profile and preferences can be found at https://www.topcoder.com/settings/account/. If you can't find what you're looking for, send us a note at support@topcoder.com. +

+
+
+
+
+ + + + + + + + + + + +
+`; diff --git a/__tests__/shared/components/tc-communities/communities/wipro2/Home.jsx b/__tests__/shared/components/tc-communities/communities/wipro/Home.jsx similarity index 100% rename from __tests__/shared/components/tc-communities/communities/wipro2/Home.jsx rename to __tests__/shared/components/tc-communities/communities/wipro/Home.jsx diff --git a/__tests__/shared/components/tc-communities/communities/wipro2/Learn.jsx b/__tests__/shared/components/tc-communities/communities/wipro/Learn.jsx similarity index 100% rename from __tests__/shared/components/tc-communities/communities/wipro2/Learn.jsx rename to __tests__/shared/components/tc-communities/communities/wipro/Learn.jsx diff --git a/__tests__/shared/components/tc-communities/communities/wipro2/__snapshots__/Home.jsx.snap b/__tests__/shared/components/tc-communities/communities/wipro/__snapshots__/Home.jsx.snap similarity index 78% rename from __tests__/shared/components/tc-communities/communities/wipro2/__snapshots__/Home.jsx.snap rename to __tests__/shared/components/tc-communities/communities/wipro/__snapshots__/Home.jsx.snap index 7ed0c4c096..fee6b3d60f 100644 --- a/__tests__/shared/components/tc-communities/communities/wipro2/__snapshots__/Home.jsx.snap +++ b/__tests__/shared/components/tc-communities/communities/wipro/__snapshots__/Home.jsx.snap @@ -16,51 +16,7 @@ exports[`Snapshot match 1`] = ` theme={Object {}} title="Wipro Crowd" /> - - - - - - + { + if ((challenges.challenges.length < challenges.totalCount) + || (mms.challenges.length < mms.totalCount)) { + logger.error(`Total prize stats in the group ${groupId} is screwed, as there are too many active challenges to get them all in one pass!`); + } + /* NOTE: MMs objects do not include total prize data, so their prizes + * are not include into the stats, at the moment. */ + const totalPrize = challenges.challenges + .reduce((total, challenge) => total + challenge.totalPrize, 0); + return { + groupId, + stats: { + numChallenges: challenges.totalCount + mms.totalCount, + numMembers: members.length, + openPrizes: `$${totalPrize}`, + }, + }; + }); +} + +export default createActions({ + STATS: { + GET_GROUP_STATS: getGroupStats, + }, +}); diff --git a/src/shared/components/tc-communities/GroupStats/index.jsx b/src/shared/components/tc-communities/GroupStats/index.jsx new file mode 100644 index 0000000000..be95de08d0 --- /dev/null +++ b/src/shared/components/tc-communities/GroupStats/index.jsx @@ -0,0 +1,69 @@ +/** + * Component for group statistics section. + */ + +import _ from 'lodash'; +import PT from 'prop-types'; +import React from 'react'; +import IconStat from '../IconStat'; +import Section from '../Section'; + +import IconSuitcase from '../../../../assets/images/tc-communities/suitcase.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'; + +import style from './style.scss'; + +/* Mapping between statistic keys and icons. */ +const ICONS = { + numChallenges: IconRocket, + numMembers: IconMember, + numProjects: IconSuitcase, + openPrizes: IconDollar, +}; + +/* Mapping between statistics keys and rendered labels. */ +const LABELS = { + numChallenges: 'Challenges', + numMembers: 'Members', + numProjects: 'Projects', + openPrizes: 'Prizes', +}; + +export default function GroupStats(props) { + const icons = []; + _.forIn(props.stats, (value, key) => + icons.push(( + + )), + ); + return ( +
{icons}
+ ); +} + +GroupStats.defaultProps = { + stats: {}, +}; + +const numberOrString = PT.oneOfType([PT.number, PT.string]); + +GroupStats.propTypes = { + stats: PT.shape({ + numChallenges: numberOrString, + numMembers: numberOrString, + numProjects: numberOrString, + openPrizes: numberOrString, + }), +}; diff --git a/src/shared/components/tc-communities/GroupStats/style.scss b/src/shared/components/tc-communities/GroupStats/style.scss new file mode 100644 index 0000000000..77cbdcda86 --- /dev/null +++ b/src/shared/components/tc-communities/GroupStats/style.scss @@ -0,0 +1,16 @@ +@import '~styles/tc-styles'; + +.container { + padding: 0; +} + +.content { + border-bottom: 1px solid #ddd; + padding: 43px 38px 50px; + + @include xxs-to-sm { + display: flex; + justify-content: space-around; + padding: 30px 12px 32px; + } +} diff --git a/src/shared/components/tc-communities/IconStat/index.jsx b/src/shared/components/tc-communities/IconStat/index.jsx index 08de963f52..9bb6674bf7 100644 --- a/src/shared/components/tc-communities/IconStat/index.jsx +++ b/src/shared/components/tc-communities/IconStat/index.jsx @@ -25,7 +25,7 @@ IconStat.defaultProps = { IconStat.propTypes = { icon: PT.func.isRequired, - number: PT.string.isRequired, + number: PT.oneOfType([PT.number, PT.string]).isRequired, label: PT.string.isRequired, theme: PT.shape({ container: PT.string, diff --git a/src/shared/components/tc-communities/communities/demo-expert/Home/index.jsx b/src/shared/components/tc-communities/communities/demo-expert/Home/index.jsx index 46e1bc9b51..0a6a6c36a5 100644 --- a/src/shared/components/tc-communities/communities/demo-expert/Home/index.jsx +++ b/src/shared/components/tc-communities/communities/demo-expert/Home/index.jsx @@ -9,17 +9,15 @@ import React from 'react'; import Section from 'components/tc-communities/Section'; import Banner from 'components/tc-communities/Banner'; -import IconStat from 'components/tc-communities/IconStat'; import ImageText from 'components/tc-communities/ImageText'; import ResourceCard from 'components/tc-communities/ResourceCard'; import NewsletterSignup from 'components/tc-communities/NewsletterSignup'; import NewsSection from 'components/tc-communities/NewsSection'; import PT from 'prop-types'; -import IconSuitcase from '../../../../../../assets/images/tc-communities/suitcase.svg'; +import GroupStats from 'containers/tc-communities/GroupStats'; + 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'; import IconNetwork from '../../../../../../assets/images/tc-communities/network.svg'; import IconMedal from '../../../../../../assets/images/tc-communities/medal.svg'; @@ -38,17 +36,7 @@ export default function Home(props) { imageSrc="/themes/wipro/home/banner.jpg" /> -
- - - - -
+
-
- - - - -
+
-
- - - - -
+
+ ); + } +} + +GroupStatsContainer.defaultProps = { + stats: {}, + token: '', +}; + +GroupStatsContainer.propTypes = { + getGroupStats: PT.func.isRequired, + groupId: PT.string.isRequired, + stats: PT.shape(), + token: PT.string, +}; + +function mapDispatchToProps(dispatch) { + return { + getGroupStats: (...args) => dispatch(actions.stats.getGroupStats(...args)), + }; +} + +function mapStateToProps(state) { + const groupId = state.tcCommunities.meta.challengeGroupId; + return { + groupId, + stats: state.stats.groups[groupId], + token: state.auth.tokenV3, + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(GroupStatsContainer); diff --git a/src/shared/reducers/index.js b/src/shared/reducers/index.js index dfd13f5f18..0a7d069017 100644 --- a/src/shared/reducers/index.js +++ b/src/shared/reducers/index.js @@ -20,6 +20,7 @@ import { factory as authFactory } from './auth'; import { factory as challengeFactory } from './challenge'; import { factory as challengeListingFactory } from './challenge-listing'; import { factory as examplesFactory } from './examples'; +import { factory as statsFactory } from './stats'; import { factory as tcCommunitiesFactory } from './tc-communities'; import { factory as leaderboardFactory } from './leaderboard'; import topcoderHeader from './topcoder_header'; @@ -30,6 +31,7 @@ export function factory(req) { challenge: challengeFactory(req), challengeListing: challengeListingFactory(req), examples: examplesFactory(req), + stats: statsFactory(req), tcCommunities: tcCommunitiesFactory(req), leaderboard: leaderboardFactory(req), }).then(reducers => combine((state) => { diff --git a/src/shared/reducers/stats.js b/src/shared/reducers/stats.js new file mode 100644 index 0000000000..128f48827a --- /dev/null +++ b/src/shared/reducers/stats.js @@ -0,0 +1,56 @@ +/** + * Reducer for state.stats. That part of Redux state is intended to keep user- + * and group-related statistics to render in the frontend. + */ + +import _ from 'lodash'; +import actions from 'actions/stats'; +import logger from 'utils/logger'; +import { handleActions } from 'redux-actions'; + +/** + * Handles result of the getGroupStats action. + * @param {Object} state Previous state. + * @param {Object} action Action result. + * @return {Object} New state. + */ +function onGetGroupStats(state, action) { + if (action.error) { + logger.error(action.payload); + return state; + } + return { + ...state, + groups: { + ...state.groups, + [action.payload.groupId]: action.payload.stats, + }, + }; +} + +/** + * Creates a new Challenge Listing redurect with the specified initial state. + * @param {Object} initialState Optional. Initial state. + * @return {Function} Reducer. + */ +function create(initialState = {}) { + const a = actions.stats; + return handleActions({ + [a.getGroupStats]: onGetGroupStats, + }, _.defaults(_.clone(initialState), { + groups: {}, + })); +} + +/** + * The factory creates a new reducer with its initial state tailored to + * the given ExpressJS HTTP request, if specified (for server-side rendering). + * If no HTTP request is specified, it uses default initial state. + */ +export function factory() { + /* Server-side rendering is not implemented yet. */ + return Promise.resolve(create()); +} + +/* Reducer with the default initial state. */ +export default create(); diff --git a/src/shared/services/groups.js b/src/shared/services/groups.js new file mode 100644 index 0000000000..68cebf4487 --- /dev/null +++ b/src/shared/services/groups.js @@ -0,0 +1,50 @@ +/** + * Service for communication with group-related part of Topcoder API. + */ + +import { getApiV3 } from './api'; + +class GroupService { + + /** + * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. + */ + constructor(tokenV3) { + this.private = { + api: getApiV3(tokenV3), + tokenV3, + }; + } + + /** + * Gets group members. + * @param {String} groupId + * @return {Promise} On sucess resolves to the array of member objects, + * which include user IDs, membership time, and some bookkeeping data. + */ + 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) + )); + } +} + +/** + * Returns a new or existing instance of challenge service, which works with + * the specified auth token. + * @param {String} tokenV3 Optional. Topcoder API v3 auth token. + * @return {GroupService} Instance of the service. + */ +let lastInstance = null; +export function getService(tokenV3) { + if (!lastInstance || tokenV3 !== lastInstance.tokenV3) { + lastInstance = new GroupService(tokenV3); + } + return lastInstance; +} + +export default undefined; From 83b66109e732bc2921bf04f7b6e6a964c4eb7a15 Mon Sep 17 00:00:00 2001 From: "Dr. Sergey Pogodin" Date: Wed, 14 Jun 2017 15:55:47 +0200 Subject: [PATCH 3/3] Fix: Return URL in Login and Register buttons Fixes #74. Return URL for Login and Regiser buttons in header and footer of TC Community mini-sites was hard-coded to lead to Wipro community. Now it always redirects back to the page where the button was clicked. As a minor side-effect, the buttons were converted from links into
(
  • @@ -44,8 +47,20 @@ function Footer(props) { {!props.isAuthorized && (
    - Register - Login + +
    )} @@ -61,8 +76,6 @@ Footer.propTypes = { title: PT.string.isRequired, url: PT.string.isRequired, })).isRequired, - registerUrl: PT.string.isRequired, - loginUrl: PT.string.isRequired, isAuthorized: PT.bool.isRequired, theme: PT.shape({ container: PT.string, diff --git a/src/shared/components/tc-communities/Footer/style.scss b/src/shared/components/tc-communities/Footer/style.scss index 555d1d2f11..e645bf0c36 100644 --- a/src/shared/components/tc-communities/Footer/style.scss +++ b/src/shared/components/tc-communities/Footer/style.scss @@ -69,13 +69,14 @@ a.link:visited { } } -a.btnRegister { +.btnRegister { border: 1px solid #60c700; border-radius: 30px; background: #60c700; color: #fafafa; display: inline-block; font: 700 14px/30px 'Open Sans'; + height: 32px; outline: none; text-transform: uppercase; width: 100px; @@ -89,13 +90,14 @@ a.btnRegister { } } -a.btnLogin { +.btnLogin { background: #0092ff; border: 1px solid #0b71e6; border-radius: 30px; color: #fafafa; display: inline-block; font: 700 14px/30px 'Open Sans'; + height: 32px; margin-left: 60px; outline: none; text-transform: uppercase; diff --git a/src/shared/components/tc-communities/Header/index.jsx b/src/shared/components/tc-communities/Header/index.jsx index f56b1f3948..b341441ef0 100644 --- a/src/shared/components/tc-communities/Header/index.jsx +++ b/src/shared/components/tc-communities/Header/index.jsx @@ -5,6 +5,8 @@ * so the whole design can be overridden by an additional css file */ +/* global window */ + import _ from 'lodash'; import config from 'utils/config'; import DesktopSubMenu from 'components/TopcoderHeader/desktop/SubMenu'; @@ -34,8 +36,6 @@ export default function Header(props) { isMobileOpen, onMobileToggleClick, profile, - registerUrl, - loginUrl, } = props; const BASE_URL = config.URL.BASE; @@ -85,8 +85,20 @@ export default function Header(props) {
  • ) : (
    - Register - Login + +
    ); @@ -197,8 +209,6 @@ Header.propTypes = { activeTrigger: PT.shape({}), closeMenu: PT.func.isRequired, communitySelector: PT.arrayOf(PT.shape()).isRequired, - registerUrl: PT.string.isRequired, - loginUrl: PT.string.isRequired, menuItems: PT.arrayOf(PT.shape({ title: PT.string.isRequired, url: PT.string.isRequired, diff --git a/src/shared/components/tc-communities/Header/style.scss b/src/shared/components/tc-communities/Header/style.scss index fb54cec26a..9d7ad4679e 100644 --- a/src/shared/components/tc-communities/Header/style.scss +++ b/src/shared/components/tc-communities/Header/style.scss @@ -293,13 +293,14 @@ } } -a.btnRegister { +.btnRegister { border: 1px solid #60c700; border-radius: 30px; background: #60c700; color: #fafafa; display: inline-block; font: 700 14px/30px 'Open Sans'; + height: 32px; text-align: center; text-transform: uppercase; width: 100px; @@ -313,13 +314,14 @@ a.btnRegister { } } -a.btnLogin { +.btnLogin { background: #0092ff; border: 1px solid #0b71e6; border-radius: 30px; color: #fafafa; display: inline-block; font: 700 14px/30px 'Open Sans'; + height: 32px; margin-left: 10px; text-align: center; text-transform: uppercase; diff --git a/src/shared/containers/tc-communities/Page/index.jsx b/src/shared/containers/tc-communities/Page/index.jsx index d9ff1441bd..c7b51d8910 100644 --- a/src/shared/containers/tc-communities/Page/index.jsx +++ b/src/shared/containers/tc-communities/Page/index.jsx @@ -14,7 +14,6 @@ */ import _ from 'lodash'; -import config from 'utils/config'; import PT from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; @@ -149,10 +148,6 @@ class Page extends Component { } render() { - const returnUrl = encodeURIComponent(`${config.URL.WIPRO}/community/wipro/home`); - const loginUrl = `${config.URL.AUTH}?retUrl=${returnUrl}`; - const registerUrl = `${config.URL.AUTH}/registration`; - const communityId = this.props.communityId; // true, if is loading now, or if not started loading yet @@ -178,15 +173,11 @@ class Page extends Component { communitySelector={this.props.meta.communitySelector} onMobileToggleClick={this.props.mobileToggle} cssUrl={this.props.meta.cssUrl} - registerUrl={registerUrl} - loginUrl={loginUrl} /> {this.renderPageContent()}