@@ -51,6 +51,7 @@ const CHALLENGE_BILLING_LOCK_STATUSES = new Set([
5151 ChallengeStatusEnum . APPROVED ,
5252 ChallengeStatusEnum . ACTIVE ,
5353] ) ;
54+
5455const CHALLENGE_APPROVAL_STATUS = {
5556 PENDING_APPROVAL : "PENDING_APPROVAL" ,
5657 APPROVED : "APPROVED" ,
@@ -61,6 +62,8 @@ const CHALLENGE_APPROVAL_ACTION_STATUSES = new Set([
6162 CHALLENGE_APPROVAL_STATUS . REJECTED ,
6263] ) ;
6364
65+ const DEFAULT_ESTIMATED_SUBMISSIONS_COUNT = 2 ;
66+
6467// Provide aliases for friendlier sortBy query params
6568const sortByAliases = {
6669 updated : constants . validChallengeParams . Updated ,
@@ -199,17 +202,93 @@ function getChallengePrizeSetMemberPaymentAmount(challenge) {
199202 } , 0 ) ;
200203}
201204
205+ /**
206+ * Reads the first-place placement prize used by reviewer cost estimates.
207+ *
208+ * @param {object } challenge Challenge model or response object.
209+ * @returns {number|undefined } First-place USD placement prize amount, or undefined when unavailable.
210+ */
211+ function getFirstPlacePrizeValue ( challenge ) {
212+ const prizeSets = _ . get ( challenge , "prizeSets" ) ;
213+
214+ if ( ! Array . isArray ( prizeSets ) ) {
215+ return undefined ;
216+ }
217+
218+ const placementPrizeSet = _ . find (
219+ prizeSets ,
220+ ( prizeSet ) => _ . toString ( _ . get ( prizeSet , "type" ) ) . toUpperCase ( ) === PrizeSetTypeEnum . PLACEMENT ,
221+ ) ;
222+ const firstPrize = _ . get ( placementPrizeSet , "prizes[0]" ) ;
223+
224+ if ( _ . toString ( _ . get ( firstPrize , "type" ) ) . toUpperCase ( ) !== constants . prizeTypes . USD ) {
225+ return undefined ;
226+ }
227+
228+ const prizeValue = _ . toNumber ( _ . get ( firstPrize , "value" ) ) ;
229+
230+ return Number . isFinite ( prizeValue ) ? prizeValue : undefined ;
231+ }
232+
233+ /**
234+ * Calculates the estimated member-review payment amount for billing locks.
235+ *
236+ * The work app shows review cost using a two-submission estimate, so draft
237+ * budget locks need the same fixed and coefficient-based reviewer math.
238+ *
239+ * @param {object } challenge Challenge model or response object.
240+ * @returns {number } Estimated member-review payment amount before markup.
241+ */
242+ function getEstimatedReviewerPaymentAmount ( challenge ) {
243+ const reviewers = _ . get ( challenge , "reviewers" ) ;
244+
245+ if ( ! Array . isArray ( reviewers ) ) {
246+ return 0 ;
247+ }
248+
249+ const firstPlacePrizeValue = getFirstPlacePrizeValue ( challenge ) ;
250+
251+ if ( _ . isNil ( firstPlacePrizeValue ) ) {
252+ return 0 ;
253+ }
254+
255+ return reviewers . reduce ( ( total , reviewer ) => {
256+ if ( _ . get ( reviewer , "isMemberReview" ) === false ) {
257+ return total ;
258+ }
259+
260+ const fixedAmount = _ . toNumber ( _ . get ( reviewer , "fixedAmount" ) ) ;
261+ const baseCoefficient = _ . toNumber ( _ . get ( reviewer , "baseCoefficient" ) ) ;
262+ const incrementalCoefficient = _ . toNumber ( _ . get ( reviewer , "incrementalCoefficient" ) ) ;
263+ const memberReviewerCount = Math . max (
264+ 1 ,
265+ Math . trunc ( _ . toNumber ( _ . get ( reviewer , "memberReviewerCount" ) ) || 1 ) ,
266+ ) ;
267+ const reviewerPayment =
268+ ( Number . isFinite ( fixedAmount ) ? fixedAmount : 0 ) +
269+ ( ( Number . isFinite ( baseCoefficient ) ? baseCoefficient : 0 ) +
270+ ( Number . isFinite ( incrementalCoefficient ) ? incrementalCoefficient : 0 ) *
271+ DEFAULT_ESTIMATED_SUBMISSIONS_COUNT ) *
272+ firstPlacePrizeValue ;
273+
274+ return total + reviewerPayment * memberReviewerCount ;
275+ } , 0 ) ;
276+ }
277+
202278/**
203279 * Reads the currently persisted challenge member-payment total.
204280 *
205281 * @param {object } challenge Challenge model or response object.
206- * @returns {number|undefined } Total USD member-payment amount before markup.
282+ * @returns {number|undefined } Total USD member-payment amount before markup,
283+ * including estimated member-review cost when prize sets are loaded.
207284 */
208285function getChallengeMemberPaymentAmount ( challenge ) {
209286 const prizeSetMemberPaymentAmount = getChallengePrizeSetMemberPaymentAmount ( challenge ) ;
210287
211288 if ( ! _ . isNil ( prizeSetMemberPaymentAmount ) ) {
212- return prizeSetMemberPaymentAmount ;
289+ return Number (
290+ ( prizeSetMemberPaymentAmount + getEstimatedReviewerPaymentAmount ( challenge ) ) . toFixed ( 2 ) ,
291+ ) ;
213292 }
214293
215294 const totalPrizes = _ . get (
0 commit comments