Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 5c7a8de

Browse files
Merge pull request #56 from topcoder-platform/challenge-listing-part-2
Challenge listing part 2
2 parents acbd4be + 17ffae3 commit 5c7a8de

File tree

178 files changed

+2947
-9510
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

178 files changed

+2947
-9510
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ workflows:
7777
branches:
7878
only:
7979
- dev
80-
- challenge-listing-part-1
80+
- challenge-listing-part-2
8181

8282
# Production builds are exectuted only on tagged commits to the
8383
# master branch.

config/default.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ module.exports = {
33
DEBOUNCE_ON_CHANGE_TIME: 150,
44
},
55
API: {
6-
V5: 'https://api.topcoder-dev.com/v5',
7-
V3: 'https://api.topcoder-dev.com/v3',
6+
V5: "https://api.topcoder-dev.com/v5",
7+
V3: "https://api.topcoder-dev.com/v3",
88
},
99
URL: {
10-
COMMUNITY_APP: 'https://community-app.topcoder-dev.com',
10+
COMMUNITY_APP: "https://community-app.topcoder-dev.com",
1111
}
1212
}

config/jest/setup.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const config = require("config");
2+
3+
global.process.env = {...global.process.env, ...config};

jest.config.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ module.exports = {
44
"^.+\\.(j|t)sx?$": "babel-jest",
55
},
66
moduleNameMapper: {
7-
"\\.(css)$": "identity-obj-proxy",
8-
"\\.svg$": "<rootDir>/__mocks__/fileMock.js",
7+
"\\.(css|scss)$": "identity-obj-proxy",
8+
"\\.(png|eot|otf|ttf|woff|woff2|svg)$": "<rootDir>/__mocks__/fileMock.js",
9+
910
},
1011
setupFilesAfterEnv: [
1112
"../node_modules/@testing-library/jest-dom/dist/index.js",
13+
"../config/jest/setup.js",
1214
],
1315
};

package-lock.json

+366-263
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"start": "node server.js",
55
"dev": "cross-env APPMODE=development webpack-dev-server --port 8008",
66
"dev-https": "cross-env APPMODE=development webpack-dev-server --https --port 8008",
7-
"build": "webpack --mode=production",
7+
"build": "webpack --mode=${APPMODE:-development} --env.config=${APPENV:-dev}",
88
"analyze": "webpack --mode=production --env.analyze=true",
99
"lint": "eslint src --ext js,jsx",
1010
"format": "prettier --write \"./**\"",
@@ -18,7 +18,6 @@
1818
"@babel/plugin-transform-runtime": "^7.8.3",
1919
"@babel/preset-env": "^7.7.6",
2020
"@babel/preset-react": "^7.7.4",
21-
"@babel/runtime": "^7.8.7",
2221
"@testing-library/jest-dom": "^5.5.0",
2322
"@testing-library/react": "^9.4.0",
2423
"@types/jest": "^25.2.1",
@@ -59,13 +58,16 @@
5958
"webpack-merge": "^4.2.2"
6059
},
6160
"dependencies": {
61+
"@babel/runtime": "^7.13.10",
6262
"@reach/router": "^1.3.4",
6363
"autoprefixer": "^8.6.5",
6464
"express": "^4.17.1",
65+
"joi": "^17.4.0",
6566
"lodash": "^4.17.21",
6667
"moment": "^2.29.1",
6768
"moment-duration-format": "^2.3.2",
6869
"qs": "^6.10.1",
70+
"rc-tooltip": "^4.2.3",
6971
"react": "^16.12.0",
7072
"react-date-range": "^1.1.3",
7173
"react-dom": "^16.12.0",

src/App.jsx

+51-16
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,33 @@
11
/**
22
* Main App component
33
*/
4-
import React, { useState, useEffect, useLayoutEffect } from "react";
5-
import { Router, useNavigate } from "@reach/router";
4+
import React, { useState, useLayoutEffect, useEffect, useRef } from "react";
5+
import { Router, useLocation } from "@reach/router";
66
import Challenges from "./containers/Challenges";
77
import Filter from "./containers/Filter";
88
import Menu from "./components/Menu";
99
import { disableSidebarForRoute } from "@topcoder/micro-frontends-navbar-app";
10-
import AuthDemo from "./components/AuthDemo";
1110
import NoSidebarDemo from "./components/NoSidebarDemo";
1211
import * as constants from "./constants";
12+
import actions from "./actions";
1313
import * as utils from "./utils";
14+
import store from "./store";
15+
import { initialChallengeFilter } from "./reducers/filter";
16+
import _ from "lodash";
1417

15-
import "./styles/react-date-range/default.scss";
16-
import "./styles/react-date-range/styles.scss";
18+
import "react-date-range/dist/theme/default.css";
19+
import "react-date-range/dist/styles.css";
20+
import "rc-tooltip/assets/bootstrap.css";
1721

1822
import "./styles/main.scss";
1923

2024
const App = () => {
2125
const [selectedMenuItem, setSelectedMenuItem] = useState(null);
22-
const navigateTo = useNavigate();
2326

2427
useLayoutEffect(() => {
2528
disableSidebarForRoute("/earn/*");
2629
}, []);
2730

28-
useEffect(() => {
29-
const path = utils.menu.getMenuItemUrlPath(
30-
constants.NAV_MENU,
31-
selectedMenuItem
32-
);
33-
if (path) {
34-
navigateTo(path);
35-
}
36-
}, [selectedMenuItem]);
37-
3831
const menu = (
3932
<Menu
4033
menu={constants.NAV_MENU}
@@ -46,6 +39,48 @@ const App = () => {
4639
/>
4740
);
4841

42+
const location = useLocation();
43+
44+
const getChallengesDebounced = useRef(_.debounce((f) => f(), 500));
45+
46+
useEffect(() => {
47+
if (!location.search) {
48+
store.dispatch(actions.challenges.getChallenges(initialChallengeFilter));
49+
return;
50+
}
51+
52+
if (location.pathname === "/earn/find/challenges") {
53+
const params = utils.url.parseUrlQuery(location.search);
54+
const toUpdate = utils.challenge.createChallengeFilter(params);
55+
56+
if (!toUpdate.types) toUpdate.types = [];
57+
if (!toUpdate.tracks) toUpdate.tracks = [];
58+
if (!toUpdate.bucket) toUpdate.bucket = "";
59+
60+
const updatedFilter = { ...initialChallengeFilter, ...toUpdate };
61+
const currentFilter = store.getState().filter.challenge;
62+
const diff = !_.isEqual(updatedFilter, currentFilter);
63+
if (diff) {
64+
store.dispatch(actions.filter.updateFilter(updatedFilter));
65+
}
66+
getChallengesDebounced.current(() =>
67+
store.dispatch(actions.challenges.getChallenges(updatedFilter))
68+
);
69+
}
70+
}, [location]);
71+
72+
useEffect(() => {
73+
const name = utils.menu.getNameByPath(
74+
constants.NAV_MENU,
75+
location.pathname
76+
);
77+
if (name) {
78+
setSelectedMenuItem(name);
79+
} else {
80+
setSelectedMenuItem(null);
81+
}
82+
}, [location]);
83+
4984
return (
5085
<div className="layout">
5186
<aside className="sidebar">

src/actions/auth.js

-8
This file was deleted.

src/actions/challenges.js

+44-42
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ async function doGetChallenges(filter) {
77
return service.getChallenges(filter);
88
}
99

10-
async function getActiveChallenges(filter) {
11-
const activeFilter = {
10+
async function getAllActiveChallenges(filter) {
11+
const allActiveFilter = {
1212
...util.createChallengeCriteria(filter),
13-
...util.createActiveChallengeCriteria(),
13+
...util.createAllActiveChallengeCriteria(),
1414
};
15-
return doGetChallenges(activeFilter);
15+
return doGetChallenges(allActiveFilter);
1616
}
1717

1818
async function getOpenForRegistrationChallenges(filter) {
@@ -23,66 +23,68 @@ async function getOpenForRegistrationChallenges(filter) {
2323
return doGetChallenges(openForRegistrationFilter);
2424
}
2525

26-
async function getPastChallenges(filter) {
27-
const pastFilter = {
26+
async function getClosedChallenges(filter) {
27+
const closedFilter = {
2828
...util.createChallengeCriteria(filter),
29-
...util.createPastChallengeCriteria(),
29+
...util.createClosedChallengeCriteria(),
3030
};
31-
return doGetChallenges(pastFilter);
31+
return doGetChallenges(closedFilter);
3232
}
3333

34-
function doFilterBySubSommunities(challenges) {
35-
return challenges;
36-
}
37-
function doFilterByPrizeFrom(challenges) {
38-
return challenges;
39-
}
40-
function doFilterByPrizeTo(challenges) {
41-
return challenges;
34+
async function getOpenForRegistrationCount(filter) {
35+
const openForRegistrationCountCriteria = {
36+
...util.createChallengeCriteria(filter),
37+
...util.createOpenForRegistrationCountCriteria(),
38+
};
39+
return doGetChallenges(openForRegistrationCountCriteria);
4240
}
4341

44-
async function getChallenges(filter, change) {
45-
const FILTER_BUCKETS = constants.FILTER_BUCKETS;
42+
async function getChallenges(filter) {
43+
const ALL_ACTIVE_CHALLENGES_BUCKET = constants.FILTER_BUCKETS[0];
44+
const OPEN_FOR_REGISTRATION_BUCKET = constants.FILTER_BUCKETS[1];
45+
const CLOSED_CHALLENGES = constants.FILTER_BUCKETS[2];
46+
4647
let challenges;
4748
let challengesFiltered;
4849
let total;
49-
let filterChange = change;
50+
let openForRegistrationCount;
5051

5152
const getChallengesByBucket = async (f) => {
53+
const promises = [];
5254
switch (f.bucket) {
53-
case FILTER_BUCKETS[0]:
54-
return getActiveChallenges(f);
55-
case FILTER_BUCKETS[1]:
56-
return getOpenForRegistrationChallenges(f);
57-
case FILTER_BUCKETS[2]:
58-
return getPastChallenges(f);
55+
case ALL_ACTIVE_CHALLENGES_BUCKET:
56+
promises.push(getAllActiveChallenges(f));
57+
break;
58+
case OPEN_FOR_REGISTRATION_BUCKET:
59+
promises.push(getOpenForRegistrationChallenges(f));
60+
break;
61+
case CLOSED_CHALLENGES:
62+
promises.push(getClosedChallenges(f));
63+
break;
5964
default:
60-
return [];
65+
return [util.createEmptyResult(), 0];
6166
}
67+
promises.push(getOpenForRegistrationCount(f));
68+
return Promise.all(promises).then((result) => [
69+
result[0],
70+
result[1].meta.total,
71+
]);
6272
};
6373

6474
if (!util.checkRequiredFilterAttributes(filter)) {
65-
return { challenges: [], challengesFiltered: [], total: 0 };
66-
}
67-
68-
if (!filterChange) {
69-
const chs = await getChallengesByBucket(filter);
70-
return { challenges: chs, challengesFiltered: chs, total: chs.meta.total };
71-
}
72-
73-
if (util.shouldFetchChallenges(filterChange)) {
74-
challenges = await getChallengesByBucket(filter);
75+
return {
76+
challenges: [],
77+
challengesFiltered: [],
78+
total: 0,
79+
openForRegistrationCount: 0,
80+
};
7581
}
7682

83+
[challenges, openForRegistrationCount] = await getChallengesByBucket(filter);
7784
challengesFiltered = challenges;
7885
total = challenges.meta.total;
79-
if (util.shouldFilterChallenges(filterChange)) {
80-
challengesFiltered = doFilterBySubSommunities(challengesFiltered);
81-
challengesFiltered = doFilterByPrizeFrom(challengesFiltered);
82-
challengesFiltered = doFilterByPrizeTo(challengesFiltered);
83-
}
8486

85-
return { challenges, challengesFiltered, total };
87+
return { challenges, challengesFiltered, total, openForRegistrationCount };
8688
}
8789

8890
export default createActions({

src/actions/filter.js

+13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
import { createActions } from "redux-actions";
2+
import * as utils from "../utils";
23

34
function updateFilter(partialUpdate) {
45
return partialUpdate;
56
}
67

8+
function clearChallengeFilter(defaultFilter) {
9+
return defaultFilter;
10+
}
11+
12+
function updateChallengeQuery(filter) {
13+
const params = utils.challenge.createChallengeParams(filter);
14+
utils.url.updateQuery(params);
15+
return params;
16+
}
17+
718
export default createActions({
819
UPDATE_FILTER: updateFilter,
20+
CLEAR_CHALLENGE_FILTER: clearChallengeFilter,
21+
UPDATE_CHALLENGE_QUERY: updateChallengeQuery,
922
});

src/actions/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import challenges from "./challenges";
22
import filter from "./filter";
33
import lookup from "./lookup";
4+
import init from "./initApp";
45

56
export default {
67
challenges,
78
filter,
89
lookup,
10+
init,
911
};

src/actions/initApp.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createActions } from "redux-actions";
2+
3+
function initApp(initialFilter) {
4+
return initialFilter;
5+
}
6+
7+
export default createActions({
8+
INIT_APP: initApp,
9+
});

0 commit comments

Comments
 (0)