Skip to content

Commit cfe0b83

Browse files
authored
Merge pull request #1610 from topcoder-platform/PM-803_wm-regression-fixes
PM-803 wm regression fixes
2 parents 356f008 + 190f97e commit cfe0b83

File tree

22 files changed

+269
-62
lines changed

22 files changed

+269
-62
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ workflows:
152152
context: org-global
153153
filters: &filters-dev
154154
branches:
155-
only: ["develop", "PM-690_asset-library-management"]
155+
only: ["develop", "PM-803_wm-regression-fixes"]
156156

157157
# Production builds are exectuted only on tagged commits to the
158158
# master branch.

src/actions/sidebar.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import {
88
LOAD_PROJECTS_PENDING,
99
LOAD_PROJECTS_SUCCESS,
1010
RESET_SIDEBAR_ACTIVE_PARAMS,
11-
UNLOAD_PROJECTS_SUCCESS
11+
UNLOAD_PROJECTS_SUCCESS,
12+
PROJECTS_PAGE_SIZE
1213
} from '../config/constants'
14+
import { checkAdmin } from '../util/tc'
1315
import _ from 'lodash'
1416

1517
/**
@@ -28,15 +30,15 @@ export function setActiveProject (projectId) {
2830
/**
2931
* Loads projects of the authenticated user
3032
*/
31-
export function loadProjects (filterProjectName = '', myProjects = true, paramFilters = {}) {
32-
return (dispatch) => {
33+
export function loadProjects (filterProjectName = '', paramFilters = {}) {
34+
return (dispatch, getState) => {
3335
dispatch({
3436
type: LOAD_PROJECTS_PENDING
3537
})
3638

3739
const filters = {
38-
status: 'active',
3940
sort: 'lastActivityAt desc',
41+
perPage: PROJECTS_PAGE_SIZE,
4042
...paramFilters
4143
}
4244
if (!_.isEmpty(filterProjectName)) {
@@ -47,21 +49,43 @@ export function loadProjects (filterProjectName = '', myProjects = true, paramFi
4749
}
4850
}
4951

50-
// filters['perPage'] = 20
51-
// filters['page'] = 1
52-
if (myProjects) {
52+
if (!checkAdmin(getState().auth.token)) {
5353
filters['memberOnly'] = true
5454
}
5555

56-
fetchMemberProjects(filters).then(projects => dispatch({
56+
// eslint-disable-next-line no-debugger
57+
const state = getState().sidebar
58+
fetchMemberProjects(filters).then(({ projects, pagination }) => dispatch({
5759
type: LOAD_PROJECTS_SUCCESS,
58-
projects
60+
projects: _.uniqBy((state.projects || []).concat(projects), 'id'),
61+
total: pagination.xTotal,
62+
page: pagination.xPage
5963
})).catch(() => dispatch({
6064
type: LOAD_PROJECTS_FAILURE
6165
}))
6266
}
6367
}
6468

69+
/**
70+
* Load more projects for the authenticated user
71+
*/
72+
export function loadMoreProjects (filterProjectName = '', paramFilters = {}) {
73+
return (dispatch, getState) => {
74+
const state = getState().sidebar
75+
76+
loadProjects(filterProjectName, _.assignIn({}, paramFilters, {
77+
perPage: PROJECTS_PAGE_SIZE,
78+
page: state.page + 1
79+
}))(dispatch, getState)
80+
}
81+
}
82+
83+
export function loadTaasProjects (filterProjectName = '', paramFilters = {}) {
84+
return loadProjects(filterProjectName, Object.assign({
85+
type: 'talent-as-a-service'
86+
}, paramFilters))
87+
}
88+
6589
/**
6690
* Unloads projects of the authenticated user
6791
*/

src/actions/users.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function loadAllUserProjects (isAdmin = true) {
2828
filters['memberOnly'] = true
2929
}
3030

31-
fetchMemberProjects(filters).then(projects => dispatch({
31+
fetchMemberProjects(filters).then(({ projects }) => dispatch({
3232
type: LOAD_ALL_USER_PROJECTS_SUCCESS,
3333
projects
3434
})).catch(() => dispatch({
@@ -66,7 +66,7 @@ export function searchUserProjects (isAdmin = true, keyword) {
6666
filters['memberOnly'] = true
6767
}
6868

69-
fetchMemberProjects(filters).then(projects => dispatch({
69+
fetchMemberProjects(filters).then(({ projects }) => dispatch({
7070
type: SEARCH_USER_PROJECTS_SUCCESS,
7171
projects
7272
})).catch(() => dispatch({

src/components/ChallengeEditor/ChallengeView/index.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { getResourceRoleByName } from '../../../util/tc'
2020
import { loadGroupDetails } from '../../../actions/challenges'
2121
import {
2222
REVIEW_TYPES,
23-
CONNECT_APP_URL,
2423
PHASE_PRODUCT_CHALLENGE_ID_FIELD,
2524
MULTI_ROUND_CHALLENGE_TEMPLATE_ID,
2625
DS_TRACK_ID
@@ -118,12 +117,7 @@ const ChallengeView = ({
118117
</div>
119118
{selectedMilestone &&
120119
<div className={styles.col}>
121-
<span><span className={styles.fieldTitle}>Milestone:</span> {selectedMilestone ? (
122-
<a href={`${CONNECT_APP_URL}/projects/${projectDetail.id}`} target='_blank'
123-
rel='noopener noreferrer'>
124-
{selectedMilestone.name}
125-
</a>
126-
) : ''}</span>
120+
<span><span className={styles.fieldTitle}>Milestone:</span> {selectedMilestone ? selectedMilestone.name : ''}</span>
127121
</div>
128122
}
129123
<div className={styles.col}>

src/components/ChallengeEditor/Milestone-Field/index.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import _ from 'lodash'
44
import Select from '../../Select'
55
import cn from 'classnames'
66
import styles from './Milestone-Field.module.scss'
7-
import { CONNECT_APP_URL } from '../../../config/constants'
8-
import PrimaryButton from '../../Buttons/PrimaryButton'
97

108
const MilestoneField = ({ milestones, onUpdateSelect, disabled, projectId, selectedMilestoneId }) => {
119
const options = milestones.map(type => ({ label: type.name, value: type.id }))
@@ -28,10 +26,6 @@ const MilestoneField = ({ milestones, onUpdateSelect, disabled, projectId, selec
2826
isDisabled={disabled}
2927
/>
3028
</div>
31-
<a className={cn(styles.field, styles.manageLink)} href={`${CONNECT_APP_URL}/projects/${projectId}`} target='_blank'
32-
rel='noopener noreferrer'>
33-
<PrimaryButton type='successDark' text='MANAGE MILESTONES' />
34-
</a>
3529
</div>
3630
</>
3731
)

src/components/ChallengesComponent/ProjectStatus/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react'
22
import PropTypes from 'prop-types'
33
import cn from 'classnames'
4-
import { PROJECT_STATUS } from '../../../config/constants'
4+
import { PROJECT_STATUSES } from '../../../config/constants'
55
import styles from './ProjectStatus.module.scss'
66

77
const ProjectStatus = ({ status }) => {
88
return (
99
<div className={cn(styles.container, styles[status])}>
10-
<div>{PROJECT_STATUS.find(item => item.value === status).label}</div>
10+
<div>{PROJECT_STATUSES.find(item => item.value === status).label}</div>
1111
</div>
1212
)
1313
}

src/components/ChallengesComponent/index.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import PropTypes from 'prop-types'
77
import { Helmet } from 'react-helmet'
88
import { Link } from 'react-router-dom'
99
import ProjectStatus from './ProjectStatus'
10-
import { PROJECT_ROLES, TYPEFORM_URL } from '../../config/constants'
10+
import { PROJECT_ROLES, TYPEFORM_URL, PROJECT_STATUS } from '../../config/constants'
1111
import { PrimaryButton, OutlineButton } from '../Buttons'
1212
import ChallengeList from './ChallengeList'
1313
import styles from './ChallengesComponent.module.scss'
@@ -50,7 +50,7 @@ const ChallengesComponent = ({
5050
}) => {
5151
const [loginUserRoleInProject, setLoginUserRoleInProject] = useState('')
5252
const isReadOnly = checkReadOnlyRoles(auth.token) || loginUserRoleInProject === PROJECT_ROLES.READ
53-
const isAdminOrCopilot = checkAdminOrCopilot(auth.token)
53+
const isAdminOrCopilot = checkAdminOrCopilot(auth.token, activeProject)
5454

5555
useEffect(() => {
5656
const loggedInUser = auth.user
@@ -101,11 +101,15 @@ const ChallengesComponent = ({
101101
target={'_blank'}
102102
/>
103103
)}
104-
<Link
105-
to={`/projects/${activeProject.id}/challenges/new`}
106-
>
107-
<PrimaryButton text={'Launch New'} type={'info'} />
108-
</Link>
104+
{activeProject.status === PROJECT_STATUS.ACTIVE ? (
105+
<Link
106+
to={`/projects/${activeProject.id}/challenges/new`}
107+
>
108+
<PrimaryButton text={'Launch New'} type={'info'} />
109+
</Link>
110+
) : (
111+
<PrimaryButton text={'Launch New'} type={'info'} disabled />
112+
)}
109113
</div>
110114
) : (
111115
<span />
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.loader {
2+
width: 100%;
3+
display: flex;
4+
justify-content: center;
5+
margin-bottom: 20px;
6+
7+
> * {
8+
width: auto;
9+
min-width: 130px;
10+
}
11+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { useEffect, useRef, useCallback } from 'react'
2+
import PropTypes from 'prop-types'
3+
4+
import styles from './InfiniteLoadTrigger.module.scss'
5+
import { OutlineButton } from '../Buttons'
6+
7+
const InfiniteLoadTrigger = ({ onLoadMore, rootMargin = '100px', threshold = 0.1 }) => {
8+
const triggerRef = useRef(null)
9+
10+
const observerCallback = useCallback(
11+
(entries) => {
12+
const [entry] = entries
13+
if (entry.isIntersecting) {
14+
onLoadMore()
15+
}
16+
},
17+
[onLoadMore]
18+
)
19+
20+
useEffect(() => {
21+
// eslint-disable-next-line no-undef
22+
const observer = new IntersectionObserver(observerCallback, {
23+
root: null, // Observe relative to viewport
24+
rootMargin,
25+
threshold
26+
})
27+
28+
if (triggerRef.current) {
29+
observer.observe(triggerRef.current)
30+
}
31+
32+
return () => {
33+
if (triggerRef.current) {
34+
observer.unobserve(triggerRef.current)
35+
}
36+
}
37+
}, [observerCallback, rootMargin, threshold])
38+
39+
return (
40+
<div ref={triggerRef} className={styles.loader}>
41+
<OutlineButton type='info' text='Load More' onClick={() => onLoadMore()} />
42+
</div>
43+
)
44+
}
45+
46+
InfiniteLoadTrigger.propTypes = {
47+
onLoadMore: PropTypes.func.isRequired,
48+
rootMargin: PropTypes.string,
49+
threshold: PropTypes.number
50+
}
51+
52+
export default InfiniteLoadTrigger

src/components/ProjectCard/ProjectCard.module.scss

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,13 @@
4444
overflow: hidden;
4545
white-space: nowrap;
4646
text-overflow: ellipsis;
47-
//display: flex;
48-
//justify-content: space-between;
49-
//align-items: center;
47+
display: flex;
48+
justify-content: space-between;
49+
align-items: center;
50+
51+
.status {
52+
opacity: 0.7;
53+
}
5054
}
5155

5256
.icon {

src/components/ProjectCard/index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@ import React from 'react'
22
import PT from 'prop-types'
33
import { Link } from 'react-router-dom'
44
import cn from 'classnames'
5+
import { find } from 'lodash'
6+
7+
import { PROJECT_STATUSES } from '../../config/constants'
58

69
import styles from './ProjectCard.module.scss'
710

8-
const ProjectCard = ({ projectName, projectId, selected, setActiveProject }) => {
11+
const ProjectCard = ({ projectName, projectStatus, projectId, selected, setActiveProject }) => {
912
return (
1013
<div className={styles.container}>
1114
<Link
1215
to={`/projects/${projectId}/challenges`}
1316
className={cn(styles.projectName, { [styles.selected]: selected })}
1417
onClick={() => setActiveProject(parseInt(projectId))}
1518
>
16-
<div className={styles.name}>{projectName}</div>
19+
<div className={styles.name}>
20+
<span>{projectName}</span>
21+
<span className={styles.status}>{find(PROJECT_STATUSES, { value: projectStatus }).label}</span>
22+
</div>
1723
</Link>
1824
</div>
1925
)
2026
}
2127

2228
ProjectCard.propTypes = {
29+
projectStatus: PT.string.isRequired,
2330
projectId: PT.number.isRequired,
2431
projectName: PT.string.isRequired,
2532
selected: PT.bool.isRequired,

0 commit comments

Comments
 (0)