Skip to content

Commit 03bcfff

Browse files
authored
Merge pull request #7094 from topcoder-platform/PM-1188
PM-1188 copilot opportunities on challenge feed
2 parents 9f432e5 + f1e3d3e commit 03bcfff

File tree

23 files changed

+885
-181
lines changed

23 files changed

+885
-181
lines changed

__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/__snapshots__/index.jsx.snap

Lines changed: 0 additions & 62 deletions
This file was deleted.

__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx

Lines changed: 0 additions & 51 deletions
This file was deleted.

__tests__/shared/components/challenge-listing/__snapshots__/index.jsx.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ exports[`Matches shallow shapshot 1 shapshot 1 1`] = `
3838
auth={Object {}}
3939
challenges={Array []}
4040
communityName={null}
41+
copilotOpportunities={Array []}
4142
expandTag={null}
4243
expandedTags={Array []}
4344
expanding={false}
4445
filterState={Object {}}
4546
loadMoreActive={null}
4647
loadMoreAll={null}
48+
loadMoreCopilotOpportunities={null}
4749
loadMoreMy={null}
4850
loadMoreMyPast={null}
4951
loadMoreOnGoing={null}
@@ -103,12 +105,14 @@ exports[`Matches shallow shapshot 2 shapshot 2 1`] = `
103105
auth={Object {}}
104106
challenges={Array []}
105107
communityName={null}
108+
copilotOpportunities={Array []}
106109
expandTag={null}
107110
expandedTags={Array []}
108111
expanding={false}
109112
filterState={Object {}}
110113
loadMoreActive={null}
111114
loadMoreAll={null}
115+
loadMoreCopilotOpportunities={null}
112116
loadMoreMy={null}
113117
loadMoreMyPast={null}
114118
loadMoreOnGoing={null}

config/backup-default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ module.exports = {
102102
/* This is the same value as above, but it is used by topcoder-react-lib,
103103
* as a more verbose name for the param. */
104104
COMMUNITY_APP: 'https://community-app.topcoder-dev.com',
105+
COPILOTS_URL: 'https://copilots.topcoder-dev.com',
105106
CHALLENGES_URL: 'https://www.topcoder-dev.com/challenges',
106107
TCO_OPEN_URL: 'https://www.topcoder-dev.com/community/member-programs/topcoder-open',
107108
ARENA: 'https://arena.topcoder-dev.com',

config/default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ module.exports = {
104104
* as a more verbose name for the param. */
105105
COMMUNITY_APP: 'https://community-app.topcoder-dev.com',
106106
CHALLENGES_URL: 'https://www.topcoder-dev.com/challenges',
107+
COPILOTS_URL: 'https://copilots.topcoder-dev.com',
107108
TCO_OPEN_URL: 'https://www.topcoder-dev.com/community/member-programs/topcoder-open',
108109
ARENA: 'https://arena.topcoder-dev.com',
109110
AUTH: 'https://accounts-auth0.topcoder-dev.com',

config/production.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = {
2626
* as a more verbose name for the param. */
2727
COMMUNITY_APP: 'https://community-app.topcoder.com',
2828
CHALLENGES_URL: 'https://www.topcoder.com/challenges',
29+
COPILOTS_URL: 'https://copilots.topcoder.com',
2930
TCO_OPEN_URL: 'https://www.topcoder.com/community/member-programs/topcoder-open',
3031

3132
AUTH: 'https://accounts-auth0.topcoder.com',

src/shared/actions/challenge-listing/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { processSRM } from 'utils/tc';
1010
import { errors, services } from 'topcoder-react-lib';
1111
import { BUCKETS } from 'utils/challenge-listing/buckets';
1212
import SORT from 'utils/challenge-listing/sort';
13+
import getCopilotOpportunities from '../../services/copilotOpportunities';
1314

1415
const { fireErrorMessage } = errors;
1516
const { getService } = services.challenge;
@@ -25,6 +26,8 @@ const PAGE_SIZE = 10;
2526
*/
2627
const REVIEW_OPPORTUNITY_PAGE_SIZE = 1000;
2728

29+
const COPILOT_OPPORTUNITY_PAGE_SIZE = 20;
30+
2831
/**
2932
* Private. Loads from the backend all challenges matching some conditions.
3033
* @param {Function} getter Given params object of shape { limit, offset }
@@ -496,6 +499,21 @@ function getReviewOpportunitiesDone(uuid, page, tokenV3) {
496499
});
497500
}
498501

502+
/**
503+
* Action to get a list of currently open Copilot Opportunities using V5 API
504+
* @param {String} uuid Unique identifier for init/done instance from shortid module
505+
* @param {Number} page Page of copilot opportunities to fetch (1-based)
506+
* @return {Promise<{uuid: string, loaded: object}>} Action result
507+
*/
508+
function getCopilotOpportunitiesDone(uuid, page) {
509+
return getCopilotOpportunities(page, COPILOT_OPPORTUNITY_PAGE_SIZE)
510+
.then(loaded => ({ uuid, loaded }))
511+
.catch((error) => {
512+
fireErrorMessage('Error Getting Copilot Opportunities', error.content || error);
513+
return Promise.reject(error);
514+
});
515+
}
516+
499517
/**
500518
* Payload creator for the action that inits the loading of SRMs.
501519
* @param {String} uuid
@@ -610,6 +628,9 @@ export default createActions({
610628
GET_REVIEW_OPPORTUNITIES_INIT: (uuid, page) => ({ uuid, page }),
611629
GET_REVIEW_OPPORTUNITIES_DONE: getReviewOpportunitiesDone,
612630

631+
GET_COPILOT_OPPORTUNITIES_INIT: (uuid, page) => ({ uuid, page }),
632+
GET_COPILOT_OPPORTUNITIES_DONE: getCopilotOpportunitiesDone,
633+
613634
GET_SRMS_INIT: getSrmsInit,
614635
GET_SRMS_DONE: getSrmsDone,
615636

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Component for rendering a Copilot Opportunity Card which will serve as a row in the list.
3+
*/
4+
import _ from 'lodash';
5+
import { config } from 'topcoder-react-utils';
6+
import moment from 'moment';
7+
import React, { useMemo } from 'react';
8+
import PT from 'prop-types';
9+
10+
import Tags from '../Tags';
11+
12+
import './style.scss';
13+
14+
const PROJECT_TYPE_LABELS = {
15+
dev: 'Development',
16+
ai: 'AI (Artificial Intelligence)',
17+
design: 'Design',
18+
datascience: 'DataScience',
19+
qa: 'Quality Assurance',
20+
};
21+
22+
function CopilotOpportunityCard({
23+
opportunity,
24+
}) {
25+
const skills = useMemo(() => _.uniq((opportunity.skills || []).map(skill => skill.name)), [
26+
opportunity.skills,
27+
]);
28+
const start = moment(opportunity.startDate);
29+
30+
return (
31+
<div styleName="copilotOpportunityCard">
32+
<div styleName="left-panel">
33+
34+
<div styleName="challenge-details">
35+
<a
36+
href={`${config.URL.COPILOTS_URL}/opportunity/${opportunity.id}`}
37+
target="_blank"
38+
rel="noopener noreferrer"
39+
>
40+
{opportunity.project.name}
41+
</a>
42+
43+
<div styleName="details-footer">
44+
<span styleName="date">
45+
Starts {start.format('MMM DD')}
46+
</span>
47+
{ skills.length > 0
48+
&& (
49+
<Tags
50+
skills={skills}
51+
/>
52+
) }
53+
</div>
54+
</div>
55+
</div>
56+
57+
<div styleName="right-panel">
58+
<div styleName="type">
59+
<span>{PROJECT_TYPE_LABELS[opportunity.type]}</span>
60+
</div>
61+
<div styleName={`status ${opportunity.status === 'completed' ? 'completed' : ''}`}>
62+
<span>{opportunity.status}</span>
63+
</div>
64+
<div styleName="numHours">
65+
<span>{opportunity.numHoursPerWeek} hours/week</span>
66+
</div>
67+
</div>
68+
</div>
69+
);
70+
}
71+
72+
CopilotOpportunityCard.propTypes = {
73+
opportunity: PT.shape().isRequired,
74+
};
75+
76+
export default CopilotOpportunityCard;

0 commit comments

Comments
 (0)