@@ -140,19 +140,21 @@ class ChallengeReviewerField extends Component {
140140 }
141141
142142 renderReviewerForm ( reviewer , index ) {
143- const { challenge, metadata } = this . props
143+ const { challenge, metadata, readOnly = false } = this . props
144144 const { scorecards = [ ] } = metadata
145145 const validationErrors = this . validateReviewer ( reviewer )
146146
147147 return (
148148 < div key = { `reviewer-${ index } ` } className = { styles . reviewerForm } >
149149 < div className = { styles . reviewerHeader } >
150150 < h4 > Reviewer { index + 1 } </ h4 >
151- < OutlineButton
152- text = 'Remove'
153- type = 'danger'
154- onClick = { ( ) => this . removeReviewer ( index ) }
155- />
151+ { ! readOnly && (
152+ < OutlineButton
153+ text = 'Remove'
154+ type = 'danger'
155+ onClick = { ( ) => this . removeReviewer ( index ) }
156+ />
157+ ) }
156158 </ div >
157159
158160 { validationErrors . length > 0 && (
@@ -166,129 +168,178 @@ class ChallengeReviewerField extends Component {
166168 < div className = { styles . formRow } >
167169 < div className = { styles . formGroup } >
168170 < label > Reviewer Type:</ label >
169- < select
170- value = { reviewer . isAIReviewer ? 'ai' : 'member' }
171- onChange = { ( e ) => {
172- const isAI = e . target . value === 'ai'
173- const { challenge, onUpdateReviewers } = this . props
174- const currentReviewers = challenge . reviewers || [ ]
175- const updatedReviewers = currentReviewers . slice ( )
176-
177- // Update both fields atomically to ensure XOR constraint is satisfied
178- // Maintain correct field order as expected by API schema
179- const currentReviewer = updatedReviewers [ index ]
180- updatedReviewers [ index ] = {
181- scorecardId : currentReviewer . scorecardId ,
182- isMemberReview : ! isAI ,
183- memberReviewerCount : currentReviewer . memberReviewerCount ,
184- phaseId : currentReviewer . phaseId ,
185- basePayment : currentReviewer . basePayment ,
186- incrementalPayment : currentReviewer . incrementalPayment ,
187- type : currentReviewer . type ,
188- isAIReviewer : isAI
189- }
190-
191- onUpdateReviewers ( { field : 'reviewers' , value : updatedReviewers } )
192- } }
193- >
194- < option value = 'member' > Member Reviewer</ option >
195- < option value = 'ai' > AI Reviewer</ option >
196- </ select >
171+ { readOnly ? (
172+ < span > { reviewer . isAIReviewer ? 'AI Reviewer' : 'Member Reviewer' } </ span >
173+ ) : (
174+ < select
175+ value = { reviewer . isAIReviewer ? 'ai' : 'member' }
176+ onChange = { ( e ) => {
177+ const isAI = e . target . value === 'ai'
178+ const { challenge, onUpdateReviewers } = this . props
179+ const currentReviewers = challenge . reviewers || [ ]
180+ const updatedReviewers = currentReviewers . slice ( )
181+
182+ // Update both fields atomically to ensure XOR constraint is satisfied
183+ // Maintain correct field order as expected by API schema
184+ const currentReviewer = updatedReviewers [ index ]
185+ updatedReviewers [ index ] = {
186+ scorecardId : currentReviewer . scorecardId ,
187+ isMemberReview : ! isAI ,
188+ memberReviewerCount : currentReviewer . memberReviewerCount ,
189+ phaseId : currentReviewer . phaseId ,
190+ basePayment : currentReviewer . basePayment ,
191+ incrementalPayment : currentReviewer . incrementalPayment ,
192+ type : currentReviewer . type ,
193+ isAIReviewer : isAI
194+ }
195+
196+ onUpdateReviewers ( { field : 'reviewers' , value : updatedReviewers } )
197+ } }
198+ >
199+ < option value = 'member' > Member Reviewer</ option >
200+ < option value = 'ai' > AI Reviewer</ option >
201+ </ select >
202+ ) }
197203 </ div >
198204
199205 < div className = { styles . formGroup } >
200206 < label > Scorecard:</ label >
201- < select
202- value = { reviewer . scorecardId }
203- onChange = { ( e ) => this . updateReviewer ( index , 'scorecardId' , e . target . value ) }
204- >
205- < option value = '' > Select Scorecard</ option >
206- { scorecards . map ( scorecard => (
207- < option key = { scorecard . id } value = { scorecard . id } >
208- { scorecard . name } - { scorecard . type } ({ scorecard . challengeTrack } ) v{ scorecard . version }
209- </ option >
210- ) ) }
211- </ select >
207+ { readOnly ? (
208+ < span >
209+ { ( ( ) => {
210+ const scorecard = scorecards . find ( s => s . id === reviewer . scorecardId )
211+ return scorecard ? `${ scorecard . name } - ${ scorecard . type } (${ scorecard . challengeTrack } ) v${ scorecard . version } ` : 'Not selected'
212+ } ) ( ) }
213+ </ span >
214+ ) : (
215+ < select
216+ value = { reviewer . scorecardId }
217+ onChange = { ( e ) => this . updateReviewer ( index , 'scorecardId' , e . target . value ) }
218+ >
219+ < option value = '' > Select Scorecard</ option >
220+ { scorecards . map ( scorecard => (
221+ < option key = { scorecard . id } value = { scorecard . id } >
222+ { scorecard . name } - { scorecard . type } ({ scorecard . challengeTrack } ) v{ scorecard . version }
223+ </ option >
224+ ) ) }
225+ </ select >
226+ ) }
212227 </ div >
213228
214229 < div className = { styles . formGroup } >
215230 < label > Phase:</ label >
216- < select
217- value = { reviewer . phaseId }
218- onChange = { ( e ) => this . updateReviewer ( index , 'phaseId' , e . target . value ) }
219- >
220- < option value = '' > Select Phase</ option >
221- { challenge . phases && challenge . phases
222- . filter ( phase =>
223- phase . name &&
224- phase . name . toLowerCase ( ) . includes ( 'review' )
225- )
226- . map ( phase => (
227- < option key = { phase . id } value = { phase . id } >
228- { phase . name || `Phase ${ phase . phaseId || phase . id } ` }
229- </ option >
230- ) ) }
231- </ select >
231+ { readOnly ? (
232+ < span >
233+ { ( ( ) => {
234+ const phase = challenge . phases && challenge . phases . find ( p => p . id === reviewer . phaseId )
235+ return phase ? ( phase . name || `Phase ${ phase . phaseId || phase . id } ` ) : 'Not selected'
236+ } ) ( ) }
237+ </ span >
238+ ) : (
239+ < select
240+ value = { reviewer . phaseId }
241+ onChange = { ( e ) => this . updateReviewer ( index , 'phaseId' , e . target . value ) }
242+ >
243+ < option value = '' > Select Phase</ option >
244+ { challenge . phases && challenge . phases
245+ . filter ( phase =>
246+ phase . name &&
247+ phase . name . toLowerCase ( ) . includes ( 'review' )
248+ )
249+ . map ( phase => (
250+ < option key = { phase . id } value = { phase . id } >
251+ { phase . name || `Phase ${ phase . phaseId || phase . id } ` }
252+ </ option >
253+ ) ) }
254+ </ select >
255+ ) }
232256 </ div >
233257 </ div >
234258
235259 { ! reviewer . isAIReviewer && (
236260 < div className = { styles . formRow } >
237261 < div className = { styles . formGroup } >
238262 < label > Number of Reviewers:</ label >
239- < input
240- type = 'number'
241- min = '1'
242- value = { reviewer . memberReviewerCount || 1 }
243- onChange = { ( e ) => this . updateReviewer ( index , 'memberReviewerCount' , parseInt ( e . target . value ) ) }
244- />
263+ { readOnly ? (
264+ < span > { reviewer . memberReviewerCount || 1 } </ span >
265+ ) : (
266+ < input
267+ type = 'number'
268+ min = '1'
269+ value = { reviewer . memberReviewerCount || 1 }
270+ onChange = { ( e ) => this . updateReviewer ( index , 'memberReviewerCount' , parseInt ( e . target . value ) ) }
271+ />
272+ ) }
245273 </ div >
246274
247275 < div className = { styles . formGroup } >
248276 < label > Base Payment ($):</ label >
249- < input
250- type = 'number'
251- min = '0'
252- step = '0.01'
253- value = { reviewer . basePayment || 0 }
254- onChange = { ( e ) => this . updateReviewer ( index , 'basePayment' , parseFloat ( e . target . value ) ) }
255- />
277+ { readOnly ? (
278+ < span > ${ reviewer . basePayment || 0 } </ span >
279+ ) : (
280+ < input
281+ type = 'number'
282+ min = '0'
283+ step = '0.01'
284+ value = { reviewer . basePayment || 0 }
285+ onChange = { ( e ) => this . updateReviewer ( index , 'basePayment' , parseFloat ( e . target . value ) ) }
286+ />
287+ ) }
256288 </ div >
257289
258290 < div className = { styles . formGroup } >
259291 < label > Incremental Payment ($):</ label >
260- < input
261- type = 'number'
262- min = '0'
263- step = '0.01'
264- value = { reviewer . incrementalPayment || 0 }
265- onChange = { ( e ) => this . updateReviewer ( index , 'incrementalPayment' , parseFloat ( e . target . value ) ) }
266- />
292+ { readOnly ? (
293+ < span > ${ reviewer . incrementalPayment || 0 } </ span >
294+ ) : (
295+ < input
296+ type = 'number'
297+ min = '0'
298+ step = '0.01'
299+ value = { reviewer . incrementalPayment || 0 }
300+ onChange = { ( e ) => this . updateReviewer ( index , 'incrementalPayment' , parseFloat ( e . target . value ) ) }
301+ />
302+ ) }
267303 </ div >
268304 </ div >
269305 ) }
270306
271307 < div className = { styles . formRow } >
272308 < div className = { styles . formGroup } >
273309 < label > Review Type:</ label >
274- < select
275- value = { reviewer . type || REVIEW_OPPORTUNITY_TYPES . REGULAR_REVIEW }
276- onChange = { ( e ) => this . updateReviewer ( index , 'type' , e . target . value ) }
277- >
278- < option value = { REVIEW_OPPORTUNITY_TYPES . REGULAR_REVIEW } > Regular Review</ option >
279- < option value = { REVIEW_OPPORTUNITY_TYPES . COMPONENT_DEV_REVIEW } > Component Dev Review</ option >
280- < option value = { REVIEW_OPPORTUNITY_TYPES . SPEC_REVIEW } > Spec Review</ option >
281- < option value = { REVIEW_OPPORTUNITY_TYPES . ITERATIVE_REVIEW } > Iterative Review</ option >
282- < option value = { REVIEW_OPPORTUNITY_TYPES . SCENARIOS_REVIEW } > Scenarios Review</ option >
283- </ select >
310+ { readOnly ? (
311+ < span >
312+ { ( ( ) => {
313+ const typeMap = {
314+ [ REVIEW_OPPORTUNITY_TYPES . REGULAR_REVIEW ] : 'Regular Review' ,
315+ [ REVIEW_OPPORTUNITY_TYPES . COMPONENT_DEV_REVIEW ] : 'Component Dev Review' ,
316+ [ REVIEW_OPPORTUNITY_TYPES . SPEC_REVIEW ] : 'Spec Review' ,
317+ [ REVIEW_OPPORTUNITY_TYPES . ITERATIVE_REVIEW ] : 'Iterative Review' ,
318+ [ REVIEW_OPPORTUNITY_TYPES . SCENARIOS_REVIEW ] : 'Scenarios Review'
319+ }
320+ return typeMap [ reviewer . type ] || 'Regular Review'
321+ } ) ( ) }
322+ </ span >
323+ ) : (
324+ < select
325+ value = { reviewer . type || REVIEW_OPPORTUNITY_TYPES . REGULAR_REVIEW }
326+ onChange = { ( e ) => this . updateReviewer ( index , 'type' , e . target . value ) }
327+ >
328+ < option value = { REVIEW_OPPORTUNITY_TYPES . REGULAR_REVIEW } > Regular Review</ option >
329+ < option value = { REVIEW_OPPORTUNITY_TYPES . COMPONENT_DEV_REVIEW } > Component Dev Review</ option >
330+ < option value = { REVIEW_OPPORTUNITY_TYPES . SPEC_REVIEW } > Spec Review</ option >
331+ < option value = { REVIEW_OPPORTUNITY_TYPES . ITERATIVE_REVIEW } > Iterative Review</ option >
332+ < option value = { REVIEW_OPPORTUNITY_TYPES . SCENARIOS_REVIEW } > Scenarios Review</ option >
333+ </ select >
334+ ) }
284335 </ div >
285336 </ div >
286337 </ div >
287338 )
288339 }
289340
290341 render ( ) {
291- const { challenge, metadata, isLoading } = this . props
342+ const { challenge, metadata, isLoading, readOnly = false } = this . props
292343 const { error } = this . state
293344 const { scorecards = [ ] , defaultReviewers = [ ] } = metadata
294345 const reviewers = challenge . reviewers || [ ]
@@ -327,11 +378,13 @@ class ChallengeReviewerField extends Component {
327378 < label > Review Configuration :</ label >
328379 </ div >
329380 < div className = { cn ( styles . field , styles . col2 ) } >
330- < div className = { styles . description } >
331- Configure how this challenge will be reviewed. You can add multiple reviewers including AI and member reviewers.
332- </ div >
381+ { ! readOnly && (
382+ < div className = { styles . description } >
383+ Configure how this challenge will be reviewed. You can add multiple reviewers including AI and member reviewers.
384+ </ div >
385+ ) }
333386
334- { reviewers . length === 0 && (
387+ { ! readOnly && reviewers . length === 0 && (
335388 < div className = { styles . noReviewers } >
336389 < p > No reviewers configured. Click "Add Reviewer" to get started.</ p >
337390 { this . findDefaultReviewer ( ) && (
@@ -347,6 +400,12 @@ class ChallengeReviewerField extends Component {
347400 </ div >
348401 ) }
349402
403+ { readOnly && reviewers . length === 0 && (
404+ < div className = { styles . noReviewers } >
405+ < p > No reviewers configured for this challenge.</ p >
406+ </ div >
407+ ) }
408+
350409 { reviewers . map ( ( reviewer , index ) =>
351410 this . renderReviewerForm ( reviewer , index )
352411 ) }
@@ -373,13 +432,15 @@ class ChallengeReviewerField extends Component {
373432 </ div >
374433 ) }
375434
376- < div className = { styles . addButton } >
377- < PrimaryButton
378- text = 'Add Reviewer'
379- type = 'info'
380- onClick = { this . addReviewer }
381- />
382- </ div >
435+ { ! readOnly && (
436+ < div className = { styles . addButton } >
437+ < PrimaryButton
438+ text = 'Add Reviewer'
439+ type = 'info'
440+ onClick = { this . addReviewer }
441+ />
442+ </ div >
443+ ) }
383444 </ div >
384445 </ div >
385446 </ >
@@ -395,6 +456,7 @@ ChallengeReviewerField.propTypes = {
395456 defaultReviewers : PropTypes . array
396457 } ) ,
397458 isLoading : PropTypes . bool ,
459+ readOnly : PropTypes . bool ,
398460 loadScorecards : PropTypes . func . isRequired ,
399461 loadDefaultReviewers : PropTypes . func . isRequired
400462}
0 commit comments