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

Commit 30b4fda

Browse files
author
dengjun
committed
Add Filter Completed
1 parent 82dcf75 commit 30b4fda

File tree

14 files changed

+242
-15
lines changed

14 files changed

+242
-15
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"dev-https": "cross-env APPMODE=development webpack-dev-server --https --port 8008",
77
"build": "webpack --mode=${APPMODE:-development} --env.config=${APPENV:-dev}",
88
"analyze": "webpack --mode=production --env.analyze=true",
9-
"lint": "eslint src --ext js,jsx",
9+
"lint": "eslint src --ext js,jsx --fix",
1010
"format": "prettier --write \"./**\"",
1111
"test": "cross-env BABEL_ENV=test jest",
1212
"watch-tests": "cross-env BABEL_ENV=test jest --watch",

src/App.jsx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import React, { useState, useLayoutEffect, useEffect, useRef } from "react";
55
import { Router, useLocation, Redirect } from "@reach/router";
66
import Challenges from "./containers/Challenges";
77
import Filter from "./containers/Filter";
8+
import MyGigsFilter from "./containers/MyGigsFilter";
89
import MyGigs from "./containers/MyGigs";
910
import Menu from "./components/Menu";
1011
import { disableSidebarForRoute } from "@topcoder/micro-frontends-navbar-app";
1112
import * as constants from "./constants";
1213
import actions from "./actions";
1314
import * as utils from "./utils";
1415
import store from "./store";
15-
import { initialChallengeFilter } from "./reducers/filter";
16+
import { initialChallengeFilter, initialGigFilter } from "./reducers/filter";
1617
import _ from "lodash";
1718
import { usePreviousLocation } from "./utils/hooks";
1819
import { useSelector } from "react-redux";
@@ -49,7 +50,7 @@ const App = () => {
4950
const location = useLocation();
5051
const previousLocation = usePreviousLocation();
5152

52-
const getChallengesDebounced = useRef(_.debounce((f) => f(), 500));
53+
const getDataDebounced = useRef(_.debounce((f) => f(), 500));
5354

5455
useEffect(() => {
5556
store.dispatch(actions.lookup.checkIsLoggedIn());
@@ -77,12 +78,44 @@ const App = () => {
7778
if (diff) {
7879
store.dispatch(actions.filter.updateFilter(updatedFilter));
7980
}
80-
getChallengesDebounced.current(() =>
81+
getDataDebounced.current(() =>
8182
store.dispatch(actions.challenges.getChallenges(updatedFilter))
8283
);
8384
}
8485
}, [location]);
8586

87+
useEffect(() => {
88+
if (location.pathname === "/earn/my-gigs" && isLoggedIn) {
89+
if (!location.search) {
90+
store.dispatch(
91+
actions.myGigs.getMyGigs(
92+
constants.GIGS_FILTER_STATUSES_PARAM[initialGigFilter.status]
93+
)
94+
);
95+
return;
96+
}
97+
const params = utils.url.parseUrlQuery(location.search);
98+
if (_.keys(params).length == 1 && params.externalId) {
99+
return;
100+
}
101+
const updatedGigFilter = {
102+
status: params.status || "Open Applications",
103+
};
104+
const currentGig = store.getState().filter.gig;
105+
const diff = !_.isEqual(updatedGigFilter, currentGig);
106+
if (diff) {
107+
store.dispatch(actions.filter.updateGigFilter(updatedGigFilter));
108+
}
109+
getDataDebounced.current(() =>
110+
store.dispatch(
111+
actions.myGigs.getMyGigs(
112+
constants.GIGS_FILTER_STATUSES_PARAM[updatedGigFilter.status]
113+
)
114+
)
115+
);
116+
}
117+
}, [location, isLoggedIn]);
118+
86119
const varsRef = useRef();
87120
varsRef.current = { previousLocation };
88121

@@ -111,7 +144,8 @@ const App = () => {
111144
<div className="sidebar-content">
112145
{menu}
113146
<hr />
114-
<Filter />
147+
{location.pathname === "/earn/find/challenges" && <Filter />}
148+
{location.pathname === "/earn/my-gigs" && <MyGigsFilter />}
115149
</div>
116150
<div className="sidebar-footer">
117151
<a

src/actions/filter.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ function updateFilter(partialUpdate) {
55
return partialUpdate;
66
}
77

8+
function updateGigFilter(partialUpdate) {
9+
return partialUpdate;
10+
}
11+
812
function clearChallengeFilter(defaultFilter) {
913
return defaultFilter;
1014
}
@@ -15,8 +19,16 @@ function updateChallengeQuery(filter) {
1519
return params;
1620
}
1721

22+
function updateGigQuery(filter) {
23+
const params = utils.myGig.createGigParams(filter);
24+
utils.url.updateQuery(params);
25+
return params;
26+
}
27+
1828
export default createActions({
1929
UPDATE_FILTER: updateFilter,
2030
CLEAR_CHALLENGE_FILTER: clearChallengeFilter,
2131
UPDATE_CHALLENGE_QUERY: updateChallengeQuery,
32+
UPDATE_GIG_FILTER: updateGigFilter,
33+
UPDATE_GIG_QUERY: updateGigQuery,
2234
});

src/actions/myGigs.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import service from "../services/myGigs";
1212
* @param {number} perPage items per page. by default is 10.
1313
* @returns
1414
*/
15-
async function getMyGigs(page = 1, perPage = PER_PAGE) {
16-
return service.getMyGigs(page, perPage);
15+
async function getMyGigs(status = "open_jobs", page = 1, perPage = PER_PAGE) {
16+
return service.getMyGigs(status, page, perPage);
1717
}
1818

1919
/**
@@ -22,8 +22,8 @@ async function getMyGigs(page = 1, perPage = PER_PAGE) {
2222
* @param {*} perPage items per page. by default is 10
2323
* @returns
2424
*/
25-
async function loadMoreMyGigs(nextPage, perPage = PER_PAGE) {
26-
return service.getMyGigs(nextPage, perPage);
25+
async function loadMoreMyGigs(status, nextPage, perPage = PER_PAGE) {
26+
return service.getMyGigs(status, nextPage, perPage);
2727
}
2828

2929
async function getProfile() {

src/constants/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ export const CURRENCY_SYMBOL = {
103103
USD: "$",
104104
};
105105

106+
export const GIGS_FILTER_STATUSES = {
107+
OPEN_JOBS: "Open Applications",
108+
COMPLETED_JOBS: "Completed Gigs",
109+
ARCHIVED_JOBS: "Archived Applications",
110+
};
111+
112+
export const GIGS_FILTER_STATUSES_PARAM = {
113+
[GIGS_FILTER_STATUSES.OPEN_JOBS]: "open_jobs",
114+
[GIGS_FILTER_STATUSES.COMPLETED_JOBS]: "completed_jobs",
115+
[GIGS_FILTER_STATUSES.ARCHIVED_JOBS]: "archived_jobs",
116+
};
117+
106118
export const MY_GIG_PHASE = {
107119
APPLIED: "Applied",
108120
SKILLS_TEST: "Skills Test",

src/containers/MyGigs/JobListing/index.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import PT from "prop-types";
33
import JobCard from "./JobCard";
44
import Button from "../../../components/Button";
55
import { useScrollLock } from "../../../utils/hooks";
6+
import * as constants from "../../../constants";
67

78
import "./styles.scss";
89

9-
const JobListing = ({ jobs, loadMore, total, numLoaded }) => {
10+
const JobListing = ({ jobs, loadMore, total, numLoaded, gigStatus }) => {
1011
const scrollLock = useScrollLock();
1112
const [page, setPage] = useState(1);
1213

@@ -17,13 +18,17 @@ const JobListing = ({ jobs, loadMore, total, numLoaded }) => {
1718
const nextPage = page + 1;
1819
scrollLock(true);
1920
setPage(nextPage);
20-
loadMore(nextPage);
21+
loadMore(constants.GIGS_FILTER_STATUSES_PARAM[gigStatus], nextPage);
2122
};
2223

2324
useEffect(() => {
2425
varsRef.current.scrollLock(false);
2526
}, [jobs]);
2627

28+
useEffect(() => {
29+
setPage(1);
30+
}, [gigStatus]);
31+
2732
return (
2833
<div styleName="card-container">
2934
{jobs.map((job, index) => (
@@ -32,7 +37,7 @@ const JobListing = ({ jobs, loadMore, total, numLoaded }) => {
3237
</div>
3338
))}
3439

35-
{numLoaded < total && (
40+
{numLoaded < total && page * constants.PER_PAGE < total && (
3641
<div styleName="load-more">
3742
<Button onClick={handleLoadMoreClick}>LOAD MORE</Button>
3843
</div>
@@ -46,6 +51,7 @@ JobListing.propTypes = {
4651
loadMore: PT.func,
4752
total: PT.number,
4853
numLoaded: PT.number,
54+
gigStatus: PT.string,
4955
};
5056

5157
export default JobListing;

src/containers/MyGigs/index.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const MyGigs = ({
2828
getAllCountries,
2929
checkingGigs,
3030
startCheckingGigs,
31+
gigStatus,
3132
}) => {
3233
const location = useLocation();
3334
const params = utils.url.parseUrlQuery(location.search);
@@ -46,7 +47,7 @@ const MyGigs = ({
4647
if (propsRef.current.params.externalId) {
4748
propsRef.current.startCheckingGigs(propsRef.current.params.externalId);
4849
} else {
49-
propsRef.current.getMyGigs();
50+
// propsRef.current.getMyGigs();
5051
}
5152
}, []);
5253

@@ -101,6 +102,7 @@ const MyGigs = ({
101102
{!checkingGigs && myGigs && myGigs.length == 0 && <Empty />}
102103
{!checkingGigs && myGigs && myGigs.length > 0 && (
103104
<JobListing
105+
gigStatus={gigStatus}
104106
jobs={myGigs}
105107
loadMore={loadMore}
106108
total={total}
@@ -133,6 +135,7 @@ const MyGigs = ({
133135
};
134136

135137
MyGigs.propTypes = {
138+
gigStatus: PT.string,
136139
myGigs: PT.arrayOf(PT.shape()),
137140
getMyGigs: PT.func,
138141
loadMore: PT.func,
@@ -148,6 +151,7 @@ MyGigs.propTypes = {
148151
};
149152

150153
const mapStateToProps = (state) => ({
154+
gigStatus: state.filter.gig.status,
151155
checkingGigs: state.myGigs.checkingGigs,
152156
myGigs: state.myGigs.myGigs,
153157
total: state.myGigs.total,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from "react";
2+
import PT from "prop-types";
3+
import _ from "lodash";
4+
import RadioButton from "../../../components/RadioButton";
5+
import * as utils from "../../../utils";
6+
7+
import "./styles.scss";
8+
9+
const GigsFilter = ({ gigStatus, gigsStatuses, updateGigFilter }) => {
10+
const bucketOptions = utils.createRadioOptions(gigsStatuses, gigStatus);
11+
12+
return (
13+
<div styleName="filter">
14+
<div styleName="buckets vertical-list">
15+
<RadioButton
16+
options={bucketOptions}
17+
onChange={(newBucketOptions) => {
18+
const filterChange = {
19+
status: utils.getSelectedRadioOption(newBucketOptions).label,
20+
};
21+
updateGigFilter(filterChange);
22+
}}
23+
/>
24+
<span></span>
25+
</div>
26+
</div>
27+
);
28+
};
29+
30+
GigsFilter.propTypes = {
31+
gigStatus: PT.string,
32+
gigsStatuses: PT.arrayOf(PT.string),
33+
updateGigFilter: PT.func,
34+
};
35+
36+
export default GigsFilter;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@import "styles/variables";
2+
@import "styles/mixins";
3+
4+
$filter-padding-x: 4 * $base-unit;
5+
$filter-padding-y: 3 * $base-unit;
6+
7+
.filter {
8+
padding: $filter-padding-y $filter-padding-x;
9+
font-size: $font-size-sm;
10+
}
11+
12+
.buckets {
13+
margin-bottom: 18px;
14+
15+
> h3 {
16+
margin-bottom: 15px;
17+
font-size: inherit;
18+
line-height: 19px;
19+
}
20+
}
21+
22+
.buckets {
23+
&.vertical-list {
24+
> div > div {
25+
margin: $base-unit 0;
26+
}
27+
}
28+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { useRef } from "react";
2+
import PT from "prop-types";
3+
import { useLocation } from "@reach/router";
4+
import { connect } from "react-redux";
5+
import GigsFilter from "./GigsFilter";
6+
import actions from "../../actions";
7+
import { updateQuery } from "../../utils/url";
8+
9+
const MyGigsFilter = ({ gigStatus, gigsStatuses, updateGigFilter }) => {
10+
const location = useLocation();
11+
const propsRef = useRef(null);
12+
propsRef.current = { location };
13+
14+
if (location.pathname === "/earn/my-gigs") {
15+
return (
16+
<GigsFilter
17+
gigStatus={gigStatus}
18+
gigsStatuses={gigsStatuses}
19+
updateGigFilter={(gigFilterChanged) => {
20+
updateGigFilter(gigFilterChanged);
21+
updateQuery(gigFilterChanged);
22+
}}
23+
/>
24+
);
25+
}
26+
27+
return null;
28+
};
29+
30+
MyGigsFilter.propTypes = {
31+
gigStatus: PT.string,
32+
gigsStatuses: PT.arrayOf(PT.string),
33+
updateGigFilter: PT.func,
34+
};
35+
36+
const mapStateToProps = (state) => ({
37+
state: state,
38+
gigStatus: state.filter.gig.status,
39+
gigsStatuses: state.lookup.gigsStatuses,
40+
});
41+
42+
const mapDispatchToProps = {
43+
updateGigFilter: actions.filter.updateGigFilter,
44+
updateGigQuery: actions.filter.updateGigQuery,
45+
};
46+
47+
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
48+
...ownProps,
49+
...stateProps,
50+
...dispatchProps,
51+
updateQuery: (change) =>
52+
dispatchProps.updateGigQuery(
53+
{
54+
...stateProps.state.filter.gig,
55+
...change,
56+
},
57+
change
58+
),
59+
});
60+
61+
export default connect(
62+
mapStateToProps,
63+
mapDispatchToProps,
64+
mergeProps
65+
)(MyGigsFilter);

0 commit comments

Comments
 (0)