Skip to content

Commit 9b17d61

Browse files
committed
Fixed metadata , phase issue, ConvertDollarToInteger
1 parent e05594d commit 9b17d61

File tree

2 files changed

+58
-41
lines changed
  • src/components/ChallengeEditor

2 files changed

+58
-41
lines changed

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

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import PropTypes from 'prop-types'
33
import { connect } from 'react-redux'
44
import cn from 'classnames'
55
import { PrimaryButton, OutlineButton } from '../../Buttons'
6-
import { REVIEW_OPPORTUNITY_TYPES } from '../../../config/constants'
6+
import { REVIEW_OPPORTUNITY_TYPES, VALIDATION_VALUE_TYPE } from '../../../config/constants'
77
import { loadScorecards, loadDefaultReviewers } from '../../../actions/challenges'
88
import styles from './ChallengeReviewer-Field.module.scss'
9+
import { convertDollarToInteger, validateValue } from '../../../util/input-check'
910

1011
class ChallengeReviewerField extends Component {
1112
constructor (props) {
@@ -69,11 +70,11 @@ class ChallengeReviewerField extends Component {
6970
scorecardId: (defaultReviewer && defaultReviewer.scorecardId) || '',
7071
isMemberReview: true,
7172
memberReviewerCount: (defaultReviewer && defaultReviewer.memberReviewerCount) || 1,
72-
phaseId: (defaultReviewer && defaultReviewer.phaseId) || (firstPhase ? firstPhase.id : ''),
73-
basePayment: (defaultReviewer && defaultReviewer.basePayment) || 0,
73+
phaseId: (defaultReviewer && defaultReviewer.phaseId) || (firstPhase ? (firstPhase.id || firstPhase.phaseId) : ''),
74+
basePayment: (defaultReviewer && defaultReviewer.basePayment) || '0',
7475
incrementalPayment: (defaultReviewer && defaultReviewer.incrementalPayment) || 0,
7576
type: (defaultReviewer && defaultReviewer.opportunityType) || REVIEW_OPPORTUNITY_TYPES.REGULAR_REVIEW,
76-
isAIReviewer: false
77+
isAIReviewer: Boolean((defaultReviewer && defaultReviewer.isAIReviewer) || false)
7778
}
7879

7980
const updatedReviewers = currentReviewers.concat([newReviewer])
@@ -98,7 +99,7 @@ class ChallengeReviewerField extends Component {
9899
}
99100

100101
findDefaultReviewer () {
101-
const { challenge, metadata } = this.props
102+
const { challenge, metadata = {} } = this.props
102103
const { defaultReviewers = [] } = metadata
103104

104105
if (!challenge || !challenge.trackId || !challenge.typeId) {
@@ -113,6 +114,10 @@ class ChallengeReviewerField extends Component {
113114
validateReviewer (reviewer) {
114115
const errors = []
115116

117+
if (typeof reviewer.isAIReviewer !== 'boolean') {
118+
errors.push('Reviewer type must be specified')
119+
}
120+
116121
if (!reviewer.scorecardId) {
117122
errors.push('Scorecard is required')
118123
}
@@ -121,11 +126,13 @@ class ChallengeReviewerField extends Component {
121126
errors.push('Phase is required')
122127
}
123128

124-
if (!reviewer.isAIReviewer && (!reviewer.memberReviewerCount || reviewer.memberReviewerCount < 1)) {
125-
errors.push('Number of reviewers must be at least 1')
129+
const memberCount = parseInt(reviewer.memberReviewerCount) || 1
130+
if (!reviewer.isAIReviewer && (memberCount < 1 || !Number.isInteger(memberCount))) {
131+
errors.push('Number of reviewers must be a positive integer')
126132
}
127133

128-
if (!reviewer.isAIReviewer && (!reviewer.basePayment || reviewer.basePayment < 0)) {
134+
const basePayment = convertDollarToInteger(reviewer.basePayment, '')
135+
if (!reviewer.isAIReviewer && (basePayment < 0)) {
129136
errors.push('Base payment must be non-negative')
130137
}
131138

@@ -140,14 +147,14 @@ class ChallengeReviewerField extends Component {
140147
}
141148

142149
renderReviewerForm (reviewer, index) {
143-
const { challenge, metadata, readOnly = false } = this.props
150+
const { challenge, metadata = {}, readOnly = false } = this.props
144151
const { scorecards = [] } = metadata
145152
const validationErrors = this.validateReviewer(reviewer)
146153

147154
return (
148155
<div key={`reviewer-${index}`} className={styles.reviewerForm}>
149156
<div className={styles.reviewerHeader}>
150-
<h4>Reviewer {index + 1}</h4>
157+
{index > 0 && <h4>Reviewer {index + 1}</h4>}
151158
{!readOnly && (
152159
<OutlineButton
153160
text='Remove'
@@ -160,7 +167,7 @@ class ChallengeReviewerField extends Component {
160167
{validationErrors.length > 0 && (
161168
<div className={styles.validationErrors}>
162169
{validationErrors.map((error, i) => (
163-
<div key={i} className={styles.validationError}>{error}</div>
170+
<div key={`error-${index}-${i}-${error}`} className={styles.validationError}>{error}</div>
164171
))}
165172
</div>
166173
)}
@@ -185,12 +192,12 @@ class ChallengeReviewerField extends Component {
185192
updatedReviewers[index] = {
186193
scorecardId: currentReviewer.scorecardId,
187194
isMemberReview: !isAI,
188-
memberReviewerCount: currentReviewer.memberReviewerCount,
195+
memberReviewerCount: currentReviewer.memberReviewerCount || 1,
189196
phaseId: currentReviewer.phaseId,
190-
basePayment: currentReviewer.basePayment,
191-
incrementalPayment: currentReviewer.incrementalPayment,
197+
basePayment: currentReviewer.basePayment || '0',
198+
incrementalPayment: currentReviewer.incrementalPayment || 0,
192199
type: currentReviewer.type,
193-
isAIReviewer: isAI
200+
isAIReviewer: Boolean(isAI)
194201
}
195202

196203
onUpdateReviewers({ field: 'reviewers', value: updatedReviewers })
@@ -231,7 +238,9 @@ class ChallengeReviewerField extends Component {
231238
{readOnly ? (
232239
<span>
233240
{(() => {
234-
const phase = challenge.phases && challenge.phases.find(p => p.id === reviewer.phaseId)
241+
const phase = challenge.phases && challenge.phases.find(p =>
242+
(p.id === reviewer.phaseId) || (p.phaseId === reviewer.phaseId)
243+
)
235244
return phase ? (phase.name || `Phase ${phase.phaseId || phase.id}`) : 'Not selected'
236245
})()}
237246
</span>
@@ -242,12 +251,13 @@ class ChallengeReviewerField extends Component {
242251
>
243252
<option value=''>Select Phase</option>
244253
{challenge.phases && challenge.phases
245-
.filter(phase =>
246-
phase.name &&
247-
phase.name.toLowerCase().includes('review')
248-
)
254+
.filter(phase => {
255+
const isReviewPhase = phase.name && phase.name.toLowerCase().includes('review')
256+
const isCurrentlySelected = (phase.id === reviewer.phaseId) || (phase.phaseId === reviewer.phaseId)
257+
return isReviewPhase || isCurrentlySelected
258+
})
249259
.map(phase => (
250-
<option key={phase.id} value={phase.id}>
260+
<option key={phase.id || phase.phaseId} value={phase.phaseId || phase.id}>
251261
{phase.name || `Phase ${phase.phaseId || phase.id}`}
252262
</option>
253263
))}
@@ -267,22 +277,29 @@ class ChallengeReviewerField extends Component {
267277
type='number'
268278
min='1'
269279
value={reviewer.memberReviewerCount || 1}
270-
onChange={(e) => this.updateReviewer(index, 'memberReviewerCount', parseInt(e.target.value))}
280+
onChange={(e) => {
281+
const validatedValue = validateValue(e.target.value, VALIDATION_VALUE_TYPE.INTEGER)
282+
const parsedValue = parseInt(validatedValue) || 1
283+
this.updateReviewer(index, 'memberReviewerCount', Math.max(1, parsedValue))
284+
}}
271285
/>
272286
)}
273287
</div>
274288

275289
<div className={styles.formGroup}>
276290
<label>Base Payment ($):</label>
277291
{readOnly ? (
278-
<span>${reviewer.basePayment || 0}</span>
292+
<span>${reviewer.basePayment || '0'}</span>
279293
) : (
280294
<input
281295
type='number'
282296
min='0'
283297
step='0.01'
284-
value={reviewer.basePayment || 0}
285-
onChange={(e) => this.updateReviewer(index, 'basePayment', parseFloat(e.target.value))}
298+
value={reviewer.basePayment || '0'}
299+
onChange={(e) => {
300+
const validatedValue = validateValue(e.target.value, VALIDATION_VALUE_TYPE.INTEGER)
301+
this.updateReviewer(index, 'basePayment', validatedValue)
302+
}}
286303
/>
287304
)}
288305
</div>
@@ -339,7 +356,7 @@ class ChallengeReviewerField extends Component {
339356
}
340357

341358
render () {
342-
const { challenge, metadata, isLoading, readOnly = false } = this.props
359+
const { challenge, metadata = {}, isLoading, readOnly = false } = this.props
343360
const { error } = this.state
344361
const { scorecards = [], defaultReviewers = [] } = metadata
345362
const reviewers = challenge.reviewers || []
@@ -384,7 +401,7 @@ class ChallengeReviewerField extends Component {
384401
</div>
385402
)}
386403

387-
{!readOnly && reviewers.length === 0 && (
404+
{!readOnly && reviewers && reviewers.length === 0 && (
388405
<div className={styles.noReviewers}>
389406
<p>No reviewers configured. Click "Add Reviewer" to get started.</p>
390407
{this.findDefaultReviewer() && (
@@ -400,34 +417,34 @@ class ChallengeReviewerField extends Component {
400417
</div>
401418
)}
402419

403-
{readOnly && reviewers.length === 0 && (
420+
{readOnly && reviewers && reviewers.length === 0 && (
404421
<div className={styles.noReviewers}>
405422
<p>No reviewers configured for this challenge.</p>
406423
</div>
407424
)}
408425

409-
{reviewers.map((reviewer, index) =>
426+
{reviewers && reviewers.map((reviewer, index) =>
410427
this.renderReviewerForm(reviewer, index)
411428
)}
412429

413-
{reviewers.length > 0 && (
430+
{reviewers && reviewers.length > 0 && (
414431
<div className={styles.summary}>
415432
<h4>Review Summary</h4>
416433
<div className={styles.summaryRow}>
417434
<span>Total Member Reviewers:</span>
418-
<span>{reviewers.filter(r => !r.isAIReviewer).reduce((sum, r) => sum + (r.memberReviewerCount || 0), 0)}</span>
435+
<span>{reviewers.filter(r => Boolean(r.isAIReviewer) === false).reduce((sum, r) => sum + (parseInt(r.memberReviewerCount) || 0), 0)}</span>
419436
</div>
420437
<div className={styles.summaryRow}>
421438
<span>Total AI Reviewers:</span>
422-
<span>{reviewers.filter(r => r.isAIReviewer).length}</span>
439+
<span>{reviewers.filter(r => Boolean(r.isAIReviewer) === true).length}</span>
423440
</div>
424441
<div className={styles.summaryRow}>
425442
<span>Total Review Cost:</span>
426-
<span>${reviewers.filter(r => !r.isAIReviewer).reduce((sum, r) => {
427-
const base = r.basePayment || 0
428-
const count = r.memberReviewerCount || 1
443+
<span>${reviewers.filter(r => Boolean(r.isAIReviewer) === false).reduce((sum, r) => {
444+
const base = convertDollarToInteger(r.basePayment, '')
445+
const count = parseInt(r.memberReviewerCount) || 1
429446
return sum + (base * count)
430-
}, 0).toFixed(2)}</span>
447+
}, 0)}</span>
431448
</div>
432449
</div>
433450
)}
@@ -462,7 +479,7 @@ ChallengeReviewerField.propTypes = {
462479
}
463480

464481
const mapStateToProps = (state) => ({
465-
metadata: state.challenges.metadata,
482+
metadata: state.challenges.metadata || {},
466483
isLoading: state.challenges.isLoading
467484
})
468485

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ const ChallengeTotalField = ({ challenge }) => {
1515
}
1616

1717
let reviewerTotal = 0
18-
if (challenge.reviewers) {
18+
if (challenge.reviewers && Array.isArray(challenge.reviewers)) {
1919
reviewerTotal = challenge.reviewers
20-
.filter(r => !r.isAIReviewer)
20+
.filter(r => Boolean(r.isAIReviewer) === false)
2121
.reduce((sum, r) => {
22-
const base = r.basePayment || 0
23-
const count = r.memberReviewerCount || 1
22+
const base = convertDollarToInteger(r.basePayment, '')
23+
const count = parseInt(r.memberReviewerCount) || 1
2424
return sum + (base * count)
2525
}, 0)
2626
}

0 commit comments

Comments
 (0)