Skip to content

Commit 92ac498

Browse files
committed
fix: issue #1046
1 parent 6754b03 commit 92ac498

File tree

3 files changed

+220
-79
lines changed

3 files changed

+220
-79
lines changed

src/components/ChallengesComponent/ChallengeCard/index.js

Lines changed: 141 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ import ChallengeStatus from '../ChallengeStatus'
1414
import ChallengeTag from '../ChallengeTag'
1515
import styles from './ChallengeCard.module.scss'
1616
import { getFormattedDuration, formatDate } from '../../../util/date'
17-
import { CHALLENGE_STATUS, COMMUNITY_APP_URL, DIRECT_PROJECT_URL, MESSAGE, ONLINE_REVIEW_URL } from '../../../config/constants'
17+
import { checkChallengeEditPermission } from '../../../util/tc'
18+
import {
19+
CHALLENGE_STATUS,
20+
COMMUNITY_APP_URL,
21+
DIRECT_PROJECT_URL,
22+
MESSAGE,
23+
ONLINE_REVIEW_URL
24+
} from '../../../config/constants'
1825
import ConfirmationModal from '../../Modal/ConfirmationModal'
1926
import AlertModal from '../../Modal/AlertModal'
2027
import Tooltip from '../../Tooltip'
@@ -28,6 +35,9 @@ const DRAFT_MSG = 'In Draft'
2835
const STALLED_TIME_LEFT_MSG = 'Challenge is currently on hold'
2936
const FF_TIME_LEFT_MSG = 'Winner is working on fixes'
3037

38+
const PERMISSION_DELETE_MESSAGE_ERROR =
39+
"You don't have permission to delete this challenge"
40+
3141
/**
3242
* Format the remaining time of a challenge phase
3343
* @param phase Challenge phase
@@ -56,7 +66,7 @@ const getTimeLeft = (phase, status) => {
5666
* @param c Challenge
5767
* @returns {{phaseMessage: string, endTime: {late, text}}}
5868
*/
59-
const getPhaseInfo = (c) => {
69+
const getPhaseInfo = c => {
6070
const { currentPhaseNames, status, startDate, phases } = c
6171
/* let checkPhases = (currentPhases && currentPhases.length > 0 ? currentPhases : allPhases)
6272
if (_.isEmpty(checkPhases)) checkPhases = []
@@ -114,25 +124,36 @@ const hoverComponents = (challenge, onUpdateLaunch, deleteModalLaunch) => {
114124
return challenge.legacyId || isTask ? (
115125
<div className={styles.linkGroup}>
116126
<div className={styles.linkGroupLeft}>
117-
<a className={styles.link} href={communityAppUrl} target='_blank'>View Challenge</a>
127+
<a className={styles.link} href={communityAppUrl} target='_blank'>
128+
View Challenge
129+
</a>
118130
{!isTask && (
119131
<div className={styles.linkGroupLeftBottom}>
120-
<a className={styles.link} href={directUrl} target='_blank'>Direct</a>
132+
<a className={styles.link} href={directUrl} target='_blank'>
133+
Direct
134+
</a>
121135
<span className={styles.linkDivider}>|</span>
122-
<a className={styles.link} href={orUrl} target='_blank'>OR</a>
136+
<a className={styles.link} href={orUrl} target='_blank'>
137+
OR
138+
</a>
123139
</div>
124140
)}
125141
</div>
126142
{challenge.status.toUpperCase() === CHALLENGE_STATUS.DRAFT && (
127-
<button className={styles.activateButton} onClick={() => onUpdateLaunch()}>
143+
<button
144+
className={styles.activateButton}
145+
onClick={() => onUpdateLaunch()}
146+
>
128147
<span>Activate</span>
129148
</button>
130149
)}
131150
</div>
132151
) : (
133152
<div className={styles.linkGroup}>
134153
<div className={styles.linkGroupLeft}>
135-
<a className={styles.link} href={communityAppUrl}>View Challenge</a>
154+
<a className={styles.link} href={communityAppUrl}>
155+
View Challenge
156+
</a>
136157
{!isTask && (
137158
<div className={styles.linkGroupLeftBottom}>
138159
<Tooltip content={MESSAGE.NO_LEGACY_CHALLENGE}>
@@ -145,36 +166,41 @@ const hoverComponents = (challenge, onUpdateLaunch, deleteModalLaunch) => {
145166
</div>
146167
)}
147168
</div>
148-
{
149-
challenge.status === 'Draft' && (
150-
<Tooltip content={MESSAGE.NO_LEGACY_CHALLENGE}>
151-
{/* Don't disable button for real inside tooltip, otherwise mouseEnter/Leave events work not good */}
152-
<button className={cn(styles.activateButton, styles.activateButtonDisabled)}>
153-
<span>Activate</span>
154-
</button>
155-
</Tooltip>
156-
)
157-
}
169+
{challenge.status === 'Draft' && (
170+
<Tooltip content={MESSAGE.NO_LEGACY_CHALLENGE}>
171+
{/* Don't disable button for real inside tooltip, otherwise mouseEnter/Leave events work not good */}
172+
<button
173+
className={cn(styles.activateButton, styles.activateButtonDisabled)}
174+
>
175+
<span>Activate</span>
176+
</button>
177+
</Tooltip>
178+
)}
158179
</div>
159180
)
160181
}
161182

162-
const renderStatus = (status) => {
183+
const renderStatus = status => {
163184
switch (status) {
164185
case CHALLENGE_STATUS.ACTIVE:
165186
case CHALLENGE_STATUS.NEW:
166187
case CHALLENGE_STATUS.DRAFT:
167188
case CHALLENGE_STATUS.COMPLETED:
168-
return (<ChallengeStatus status={status} />)
189+
return <ChallengeStatus status={status} />
169190
default:
170-
return (<span className={styles.statusText}>{status}</span>)
191+
return <span className={styles.statusText}>{status}</span>
171192
}
172193
}
173194

174-
const renderLastUpdated = (challenge) => {
195+
const renderLastUpdated = challenge => {
175196
return (
176-
<Link className={cn(styles.col2, styles.lastUpdated)} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}>
177-
<div className={styles.lastUpdatedAt}>{formatDate(challenge.updated)}</div>
197+
<Link
198+
className={cn(styles.col2, styles.lastUpdated)}
199+
to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}
200+
>
201+
<div className={styles.lastUpdatedAt}>
202+
{formatDate(challenge.updated)}
203+
</div>
178204
<div className={styles.lastUpdatedBy}>{challenge.updatedBy}</div>
179205
</Link>
180206
)
@@ -187,7 +213,9 @@ class ChallengeCard extends React.Component {
187213
isConfirm: false,
188214
isLaunch: false,
189215
isDeleteLaunch: false,
190-
isSaving: false
216+
isSaving: false,
217+
isCheckChalengePermission: false,
218+
hasEditChallengePermission: false
191219
}
192220
this.onUpdateConfirm = this.onUpdateConfirm.bind(this)
193221
this.onUpdateLaunch = this.onUpdateLaunch.bind(this)
@@ -208,8 +236,19 @@ class ChallengeCard extends React.Component {
208236
}
209237

210238
deleteModalLaunch () {
239+
const { challenge } = this.props
211240
if (!this.state.isDeleteLaunch) {
212-
this.setState({ isDeleteLaunch: true })
241+
checkChallengeEditPermission(challenge.id).then(hasPermission => {
242+
this.setState({
243+
isCheckChalengePermission: false,
244+
hasEditChallengePermission: hasPermission
245+
})
246+
})
247+
this.setState({
248+
isDeleteLaunch: true,
249+
isCheckChalengePermission: true,
250+
hasEditChallengePermission: false
251+
})
213252
}
214253
}
215254

@@ -232,9 +271,17 @@ class ChallengeCard extends React.Component {
232271
}
233272
// call action to update the challenge with a new status
234273
await partiallyUpdateChallengeDetails(challenge.id, payload)
235-
this.setState({ isLaunch: true, isConfirm: challenge.id, isSaving: false })
274+
this.setState({
275+
isLaunch: true,
276+
isConfirm: challenge.id,
277+
isSaving: false
278+
})
236279
} catch (e) {
237-
const error = _.get(e, 'response.data.message', 'Unable to activate the challenge')
280+
const error = _.get(
281+
e,
282+
'response.data.message',
283+
'Unable to activate the challenge'
284+
)
238285
this.setState({ isSaving: false, error })
239286
}
240287
}
@@ -248,31 +295,49 @@ class ChallengeCard extends React.Component {
248295
this.setState({ isSaving: false })
249296
this.resetModal()
250297
} catch (e) {
251-
const error = _.get(e, 'response.data.message', 'Unable to Delete the challenge')
298+
const error = _.get(
299+
e,
300+
'response.data.message',
301+
'Unable to Delete the challenge'
302+
)
252303
this.setState({ isSaving: false, error })
253304
}
254305
}
255306

256307
render () {
257-
const { isLaunch, isConfirm, isSaving, isDeleteLaunch } = this.state
258-
const { challenge, shouldShowCurrentPhase, reloadChallengeList } = this.props
308+
const { isLaunch, isConfirm, isSaving, isDeleteLaunch,
309+
isCheckChalengePermission,
310+
hasEditChallengePermission
311+
} = this.state
312+
const {
313+
challenge,
314+
shouldShowCurrentPhase,
315+
reloadChallengeList
316+
} = this.props
259317
const { phaseMessage, endTime } = getPhaseInfo(challenge)
318+
const deleteMessage = isCheckChalengePermission
319+
? 'Checking permissions...'
320+
: `Do you want to delete "${challenge.name}"?`
321+
260322
return (
261323
<div className={styles.item}>
262-
{
263-
isDeleteLaunch && !isConfirm && (
264-
<ConfirmationModal
265-
title='Confirm Delete'
266-
message={`Do you want to delete "${challenge.name}"?`}
267-
theme={theme}
268-
isProcessing={isSaving}
269-
errorMessage={this.state.error}
270-
onCancel={this.resetModal}
271-
onConfirm={this.onDeleteChallenge}
272-
/>
273-
)
274-
}
275-
{ isLaunch && !isConfirm && (
324+
{isDeleteLaunch && !isConfirm && (
325+
<ConfirmationModal
326+
title='Confirm Delete'
327+
message={deleteMessage}
328+
theme={theme}
329+
isProcessing={isSaving}
330+
disableConfirmButton={!hasEditChallengePermission}
331+
errorMessage={
332+
!isCheckChalengePermission && !hasEditChallengePermission
333+
? PERMISSION_DELETE_MESSAGE_ERROR
334+
: this.state.error
335+
}
336+
onCancel={this.resetModal}
337+
onConfirm={this.onDeleteChallenge}
338+
/>
339+
)}
340+
{isLaunch && !isConfirm && (
276341
<ConfirmationModal
277342
title='Confirm Launch'
278343
message={`Do you want to launch "${challenge.name}"?`}
@@ -282,9 +347,8 @@ class ChallengeCard extends React.Component {
282347
onCancel={this.resetModal}
283348
onConfirm={this.onLaunchChallenge}
284349
/>
285-
)
286-
}
287-
{ isLaunch && isConfirm && (
350+
)}
351+
{isLaunch && isConfirm && (
288352
<AlertModal
289353
title='Success'
290354
message={`Challenge "${challenge.name}" is activated successfuly`}
@@ -295,25 +359,45 @@ class ChallengeCard extends React.Component {
295359
okLink={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}
296360
onClose={this.resetModal}
297361
/>
298-
) }
362+
)}
299363

300-
<Link className={styles.col1} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}>
364+
<Link
365+
className={styles.col1}
366+
to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}
367+
>
301368
<div className={styles.name}>
302369
<span className={styles.block}>{challenge.name}</span>
303-
<ChallengeTag track={challenge.trackId} challengeType={challenge.type} />
304-
<span className={styles.createdAt}>{`Created by ${challenge.createdBy} at ${formatDate(challenge.created)}`}</span>
370+
<ChallengeTag
371+
track={challenge.trackId}
372+
challengeType={challenge.type}
373+
/>
374+
<span className={styles.createdAt}>{`Created by ${
375+
challenge.createdBy
376+
} at ${formatDate(challenge.created)}`}</span>
305377
</div>
306378
</Link>
307379
{renderLastUpdated(challenge)}
308-
<Link className={styles.col2} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}>
380+
<Link
381+
className={styles.col2}
382+
to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}
383+
>
309384
{renderStatus(challenge.status.toUpperCase())}
310385
</Link>
311-
{shouldShowCurrentPhase && (<Link className={styles.col3} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}>
312-
<span className={styles.block}>{phaseMessage}</span>
313-
<span className='block light-text'>{endTime}</span>
314-
</Link>)}
386+
{shouldShowCurrentPhase && (
387+
<Link
388+
className={styles.col3}
389+
to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}
390+
>
391+
<span className={styles.block}>{phaseMessage}</span>
392+
<span className='block light-text'>{endTime}</span>
393+
</Link>
394+
)}
315395
<div className={cn(styles.col4, styles.editingContainer)}>
316-
{hoverComponents(challenge, this.onUpdateLaunch, this.deleteModalLaunch)}
396+
{hoverComponents(
397+
challenge,
398+
this.onUpdateLaunch,
399+
this.deleteModalLaunch
400+
)}
317401
</div>
318402
<div className={cn(styles.col4, styles.iconsContainer)}>
319403
<div className={styles.faIconContainer}>

src/components/Modal/ConfirmationModal.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import styles from './ConfirmationModal.module.scss'
66
import OutlineButton from '../Buttons/OutlineButton'
77
import PrimaryButton from '../Buttons/PrimaryButton'
88

9-
const ConfirmationModal = ({ title, message, errorMessage, theme, isProcessing, onCancel, onConfirm }) => (
9+
const ConfirmationModal = ({ title, message, errorMessage, theme, isProcessing, onCancel, onConfirm, disableConfirmButton }) => (
1010
<Modal theme={theme} onCancel={onCancel}>
1111
<div className={styles.contentContainer}>
1212
<div className={styles.title}>{title}</div>
@@ -23,6 +23,7 @@ const ConfirmationModal = ({ title, message, errorMessage, theme, isProcessing,
2323
<div className={styles.button}>
2424
<PrimaryButton
2525
text={isProcessing ? 'Processing...' : 'Confirm'}
26+
disabled={disableConfirmButton}
2627
type={'info'}
2728
onClick={onConfirm}
2829
/>
@@ -39,6 +40,7 @@ ConfirmationModal.propTypes = {
3940
errorMessage: PropTypes.string,
4041
theme: PropTypes.shape(),
4142
isProcessing: PropTypes.bool,
43+
disableConfirmButton: PropTypes.bool,
4244
onCancel: PropTypes.func,
4345
onConfirm: PropTypes.func
4446
}

0 commit comments

Comments
 (0)