Skip to content

Commit

Permalink
OBSERVER SHOULD BE ABLE TO VIEW CHALLENGES IN A PROJECT
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgasper committed Dec 24, 2022
1 parent 4921b33 commit 75c4a63
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 27 deletions.
7 changes: 4 additions & 3 deletions src/components/ChallengeEditor/ChallengeViewTabs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import LegacyLinks from '../../LegacyLinks'
import ForumLink from '../../ForumLink'
import Registrants from '../Registrants'
import Submissions from '../Submissions'
import { checkAdmin, getResourceRoleByName } from '../../../util/tc'
import { checkAdmin, checkReadOnlyRoles, getResourceRoleByName } from '../../../util/tc'
import { CHALLENGE_STATUS, MESSAGE } from '../../../config/constants'
import Tooltip from '../../Tooltip'
import CancelDropDown from '../Cancel-Dropdown'
Expand Down Expand Up @@ -88,14 +88,15 @@ const ChallengeViewTabs = ({
const isDraft = challenge.status.toUpperCase() === CHALLENGE_STATUS.DRAFT
const isSelfServiceCopilot = challenge.legacy.selfServiceCopilot === loggedInUser.handle
const isAdmin = checkAdmin(token)
const isReadOnly = checkReadOnlyRoles(token)
const canApprove = (isSelfServiceCopilot || enableEdit) && isDraft && isSelfService
const hasBillingAccount = _.get(projectDetail, 'billingAccountId') !== null
// only challenges that have a billing account can be launched AND
// if this isn't self-service, permit launching if the challenge is draft
// OR if this isn't a non-self-service draft, permit launching if:
// a) the current user is either the self-service copilot or is an admin AND
// b) the challenge is approved
const canLaunch = hasBillingAccount &&
const canLaunch = hasBillingAccount && !isReadOnly &&
((!isSelfService && isDraft) ||
((isSelfServiceCopilot || enableEdit || isAdmin) &&
challenge.status.toUpperCase() === CHALLENGE_STATUS.APPROVED))
Expand Down Expand Up @@ -127,7 +128,7 @@ const ChallengeViewTabs = ({
styles.actionButtonsRight
)}
>
{(isDraft || challenge.status === 'New') && !isSelfService &&
{(isDraft || challenge.status === 'New') && !isReadOnly && !isSelfService &&
(<div className={styles['cancel-button']}><CancelDropDown challenge={challenge} onSelectMenu={cancelChallenge} /></div>)}
{canLaunch && (
<div className={styles.button}>
Expand Down
16 changes: 11 additions & 5 deletions src/components/ChallengesComponent/ChallengeCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import styles from './ChallengeCard.module.scss'
import { formatDate } from '../../../util/date'
import { CHALLENGE_STATUS, COMMUNITY_APP_URL, DIRECT_PROJECT_URL, MESSAGE, ONLINE_REVIEW_URL } from '../../../config/constants'
import ConfirmationModal from '../../Modal/ConfirmationModal'
import { checkChallengeEditPermission } from '../../../util/tc'
import { checkChallengeEditPermission, checkReadOnlyRoles } from '../../../util/tc'
import AlertModal from '../../Modal/AlertModal'
import Tooltip from '../../Tooltip'

Expand Down Expand Up @@ -208,6 +208,7 @@ class ChallengeCard extends React.Component {
: `Do you want to delete "${challenge.name}"?`
const orUrl = `${ONLINE_REVIEW_URL}/review/actions/ViewProjectDetails?pid=${challenge.legacyId}`
const communityAppUrl = `${COMMUNITY_APP_URL}/challenges/${challenge.id}`
const isReadOnly = checkReadOnlyRoles(this.props.auth.token)

return (
<div className={styles.item}>
Expand Down Expand Up @@ -277,9 +278,13 @@ class ChallengeCard extends React.Component {
<div className={styles.col3}>
{renderStatus(challenge.status.toUpperCase(), getStatusText)}
</div>
<div className={styles.col6}>
{(disableHover ? <Link className={styles.link} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/edit`}>Edit</Link> : hoverComponents(challenge, this.onUpdateLaunch, this.deleteModalLaunch))}
</div>
{
!isReadOnly && (
<div className={styles.col6}>
{(disableHover ? <Link className={styles.link} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/edit`}>Edit</Link> : hoverComponents(challenge, this.onUpdateLaunch, this.deleteModalLaunch))}
</div>
)
}
<div className={styles.col6}>
<a className={styles.link} href={orUrl} target='_blank'>OR</a>
</div>
Expand All @@ -306,7 +311,8 @@ ChallengeCard.propTypes = {
isBillingAccountExpired: PropTypes.bool,
disableHover: PropTypes.bool,
getStatusText: PropTypes.func,
challengeTypes: PropTypes.arrayOf(PropTypes.shape())
challengeTypes: PropTypes.arrayOf(PropTypes.shape()),
auth: PropTypes.object.isRequired
}

export default withRouter(ChallengeCard)
1 change: 1 addition & 0 deletions src/components/ChallengesComponent/ChallengeList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ class ChallengeList extends Component {
disableHover
getStatusText={this.getStatusTextFunc(selfService)}
challengeTypes={challengeTypes}
auth={this.props.auth}
/>
</li>
)
Expand Down
5 changes: 4 additions & 1 deletion src/components/ChallengesComponent/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { PrimaryButton } from '../Buttons'
import ChallengeList from './ChallengeList'
import styles from './ChallengesComponent.module.scss'
import xss from 'xss'
import { checkReadOnlyRoles } from '../../util/tc'

const ChallengesComponent = ({
challenges,
Expand Down Expand Up @@ -42,6 +43,8 @@ const ChallengesComponent = ({
auth,
challengeTypes
}) => {
const isReadOnly = checkReadOnlyRoles(auth.token)

return (
<div>
<Helmet title={activeProject ? activeProject.name : ''} />
Expand All @@ -67,7 +70,7 @@ const ChallengesComponent = ({
</span>
)}
</div>
{activeProject && activeProject.id ? (
{activeProject && activeProject.id && !isReadOnly ? (
<Link
className={styles.buttonLaunchNew}
to={`/projects/${activeProject.id}/challenges/new`}
Expand Down
7 changes: 6 additions & 1 deletion src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,12 @@ export const ALLOWED_USER_ROLES = [
'administrator',
'connect admin',
'connect manager',
'connect copilot'
'connect copilot',
'topcoder user'
]

export const READ_ONLY_ROLES = [
'topcoder user'
]

export const ADMIN_ROLES = [
Expand Down
44 changes: 28 additions & 16 deletions src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getFreshToken, decodeToken } from 'tc-auth-lib'
import { saveToken } from './actions/auth'
import { loadChallengeDetails } from './actions/challenges'
import { connect } from 'react-redux'
import { checkAllowedRoles } from './util/tc'
import { checkAllowedRoles, checkReadOnlyRoles } from './util/tc'
import { setCookie, removeCookie, isBetaMode } from './util/cookie'
import IdleTimer from 'react-idle-timer'
import modalStyles from './styles/modal.module.scss'
Expand All @@ -36,9 +36,11 @@ class RedirectToChallenge extends React.Component {
}

componentWillReceiveProps (nextProps) {
const { token } = nextProps
const isReadOnly = checkReadOnlyRoles(token)
const projectId = _.get(nextProps.challengeDetails, 'projectId')
const challengeId = _.get(nextProps.challengeDetails, 'id')
if (projectId && challengeId) {
if (projectId && challengeId && isReadOnly) {
console.log('Redircting to full URL')
this.props.history.replace(`/projects/${projectId}/challenges/${challengeId}/view`)
}
Expand All @@ -61,7 +63,8 @@ RedirectToChallenge.propTypes = {
loadChallengeDetails: PropTypes.func,
challengeDetails: PropTypes.object,
match: PropTypes.object,
history: PropTypes.object
history: PropTypes.object,
token: PropTypes.string
}

const ConnectRedirectToChallenge = connect(mapStateToProps, mapDispatchToProps)(RedirectToChallenge)
Expand Down Expand Up @@ -128,6 +131,7 @@ class Routes extends React.Component {
}

const isAllowed = checkAllowedRoles(_.get(decodeToken(this.props.token), 'roles'))
const isReadOnly = checkReadOnlyRoles(this.props.token)
const modal = (<ConfirmationModal
theme={theme}
title='Session Timeout'
Expand Down Expand Up @@ -176,26 +180,34 @@ class Routes extends React.Component {
<Tab />
)()}
/>
<Route exact path='/users'
render={() => renderApp(
<Users />,
<TopBarContainer />,
<Tab />
)()}
/>
{
!isReadOnly && (
<Route exact path='/users'
render={() => renderApp(
<Users />,
<TopBarContainer />,
<Tab />
)()}
/>
)
}
<Route exact path='/self-service'
render={() => renderApp(
<Challenges selfService />,
<TopBarContainer />,
<Tab selfService />
)()}
/>
<Route exact path='/projects/:projectId/challenges/new'
render={({ match }) => renderApp(
<ChallengeEditor />,
<TopBarContainer />,
<Tab projectId={match.params.projectId} menu={'New Challenge'} />
)()} />
{
!isReadOnly && (
<Route exact path='/projects/:projectId/challenges/new'
render={({ match }) => renderApp(
<ChallengeEditor />,
<TopBarContainer />,
<Tab projectId={match.params.projectId} menu={'New Challenge'} />
)()} />
)
}
<Route exact path='/challenges/:challengeId' component={ConnectRedirectToChallenge} />
<Route
path='/projects/:projectId/challenges/:challengeId'
Expand Down
12 changes: 11 additions & 1 deletion src/util/tc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
CHALLENGE_TRACKS,
ALLOWED_USER_ROLES,
ADMIN_ROLES,
SUBMITTER_ROLE_UUID
SUBMITTER_ROLE_UUID,
READ_ONLY_ROLES
} from '../config/constants'
import _ from 'lodash'
import { decodeToken } from 'tc-auth-lib'
Expand Down Expand Up @@ -149,6 +150,15 @@ export const getDomainTypes = (trackId) => {
export const checkAllowedRoles = roles =>
roles.some(val => ALLOWED_USER_ROLES.indexOf(val.toLowerCase()) > -1)

/**
* Checks if read only role is present in allowed roles
* @param token
*/
export const checkReadOnlyRoles = token => {
const roles = _.get(decodeToken(token), 'roles')
return roles.some(val => READ_ONLY_ROLES.indexOf(val.toLowerCase()) > -1)
}

/**
* Checks if token has any of the admin roles
* @param token
Expand Down

0 comments on commit 75c4a63

Please sign in to comment.