From 25430bf6b93fc370698aef4775d24ed7e2ced92d Mon Sep 17 00:00:00 2001 From: LNCracker Date: Sun, 31 May 2026 23:42:54 +0700 Subject: [PATCH] Fix: Checkpoint prizes are not shown for challenges with checkpoint phase This commit addresses GitHub Issue #1096 by ensuring checkpoint prizes are correctly structured and displayed. Key changes include: - In `CheckpointPrizes-Field`, individual prizes within the `CHECKPOINT_PRIZES` prize set are now explicitly assigned the `CHALLENGE_PRIZE_TYPE.CHECKPOINT` type. - The display currency symbol for checkpoint prizes is now consistently derived from the main challenge's prize type (USD or POINT). - The `getPrizeType` utility function has been enhanced to robustly determine the challenge's currency type, ignoring `CHECKPOINT` as a currency. - The `applyPrizeTypeToPrizeSets` utility function is updated to only modify `PLACEMENT` and `COPILOT_PAYMENT` prize types, preserving the `CHECKPOINT` type for checkpoint prizes. --- .../CheckpointPrizes-Field/index.js | 29 ++++--- src/util/prize.js | 82 +++++++++++++++---- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js b/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js index 6eb0fd62..8ec6110b 100644 --- a/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js +++ b/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js @@ -20,23 +20,32 @@ import { getPrizeType } from '../../../util/prize' const CheckpointPrizesField = ({ challenge, onUpdateOthers, readOnly }) => { const type = PRIZE_SETS_TYPE.CHECKPOINT_PRIZES const prizeSets = _.get(challenge, 'prizeSets') || [] - const checkpointPrize = prizeSets.find(p => p.type === type) || { type: PRIZE_SETS_TYPE.CHECKPOINT_PRIZES, prizes: [], 'description': 'Checkpoint Prizes' } - const number = _.get(checkpointPrize, 'prizes.length') || DEFAULT_CHECKPOINT_PRIZE_COUNT - const amount = _.get(checkpointPrize, 'prizes.length') ? checkpointPrize.prizes[0].value : DEFAULT_CHECKPOINT_PRIZE - const prizeType = _.get(checkpointPrize, 'prizes[0].type') || getPrizeType(prizeSets) + + // Determine the currency type from the main challenge prizes (PLACEMENT) + const mainChallengeCurrencyType = getPrizeType(prizeSets) + + const checkpointPrizeSet = prizeSets.find(p => p.type === type) || { type: PRIZE_SETS_TYPE.CHECKPOINT_PRIZES, prizes: [], 'description': 'Checkpoint Prizes' } + const number = _.get(checkpointPrizeSet, 'prizes.length') || DEFAULT_CHECKPOINT_PRIZE_COUNT + const amount = _.get(checkpointPrizeSet, 'prizes.length') ? checkpointPrizeSet.prizes[0].value : DEFAULT_CHECKPOINT_PRIZE // update the check point prize with default values if it's not already defined - if (_.get(checkpointPrize, 'prizes.length') === 0) { + // or if the prize type is not CHECKPOINT (e.g., legacy data might have USD/POINT) + if (_.get(checkpointPrizeSet, 'prizes.length') === 0 || _.get(checkpointPrizeSet, 'prizes[0].type') !== CHALLENGE_PRIZE_TYPE.CHECKPOINT) { onChange(number, amount) } function onChange (number, amount) { - checkpointPrize.prizes = _.range(validateValue(number, VALIDATION_VALUE_TYPE.INTEGER)) - .map(i => ({ type: prizeType, value: +validateValue(amount, VALIDATION_VALUE_TYPE.INTEGER) })) - onUpdateOthers({ field: 'prizeSets', value: [...prizeSets.filter(p => p.type !== type), +number && checkpointPrize].filter(p => p) }) + checkpointPrizeSet.prizes = _.range(validateValue(number, VALIDATION_VALUE_TYPE.INTEGER)) + .map(i => ({ + type: CHALLENGE_PRIZE_TYPE.CHECKPOINT, // Set the prize type to CHECKPOINT + value: +validateValue(amount, VALIDATION_VALUE_TYPE.INTEGER) + })) + // Filter out the old checkpoint prize set and add the updated one + onUpdateOthers({ field: 'prizeSets', value: [...prizeSets.filter(p => p.type !== type), +number && checkpointPrizeSet].filter(p => p) }) } - const symbol = prizeType === CHALLENGE_PRIZE_TYPE.POINT ? 'Pts' : '$' + // Use the main challenge currency type for display symbol + const symbol = mainChallengeCurrencyType === CHALLENGE_PRIZE_TYPE.POINT ? 'Pts' : '$' return ( <> @@ -54,7 +63,7 @@ const CheckpointPrizesField = ({ challenge, onUpdateOthers, readOnly }) => {
- {prizeType === CHALLENGE_PRIZE_TYPE.POINT + {mainChallengeCurrencyType === CHALLENGE_PRIZE_TYPE.POINT ? Pts : }
diff --git a/src/util/prize.js b/src/util/prize.js index 581f2b98..4700c070 100644 --- a/src/util/prize.js +++ b/src/util/prize.js @@ -1,29 +1,75 @@ +import _ from 'lodash' import { CHALLENGE_PRIZE_TYPE, PRIZE_SETS_TYPE } from '../config/constants' -const PRIZE_SETS_WITH_TYPE = [ - PRIZE_SETS_TYPE.CHALLENGE_PRIZES, - PRIZE_SETS_TYPE.REVIEWER_PAYMENT, - PRIZE_SETS_TYPE.CHECKPOINT_PRIZES -] +/** + * Get prize type (USD or POINT) from prize sets. + * It prioritizes PLACEMENT prize set, then COPILOT_PAYMENT. + * If no specific prize type is found, it defaults to USD. + * + * @param {Array} prizeSets prize sets + * @returns {String} prize type (USD or POINT) + */ +export function getPrizeType (prizeSets) { + if (!prizeSets || !prizeSets.length) { + return CHALLENGE_PRIZE_TYPE.USD + } -export const getPrizeType = (prizeSets = []) => { - const placementSet = (prizeSets || []).find(p => p.type === PRIZE_SETS_TYPE.CHALLENGE_PRIZES) - const prizeType = placementSet && placementSet.prizes && placementSet.prizes[0] && placementSet.prizes[0].type - return prizeType || CHALLENGE_PRIZE_TYPE.USD -} + // Prioritize PLACEMENT prize set for overall challenge currency type + const placementPrizeSet = prizeSets.find(p => p.type === PRIZE_SETS_TYPE.CHALLENGE_PRIZES) + if (placementPrizeSet && placementPrizeSet.prizes && placementPrizeSet.prizes.length) { + // Ensure the type is USD or POINT, not CHECKPOINT + const type = placementPrizeSet.prizes[0].type + if (type === CHALLENGE_PRIZE_TYPE.USD || type === CHALLENGE_PRIZE_TYPE.POINT) { + return type + } + } + + // Fallback to COPILOT_PAYMENT if no PLACEMENT or invalid type + const copilotPrizeSet = prizeSets.find(p => p.type === PRIZE_SETS_TYPE.COPILOT_PAYMENT) + if (copilotPrizeSet && copilotPrizeSet.prizes && copilotPrizeSet.prizes.length) { + const type = copilotPrizeSet.prizes[0].type + if (type === CHALLENGE_PRIZE_TYPE.USD || type === CHALLENGE_PRIZE_TYPE.POINT) { + return type + } + } + + // If no PLACEMENT or COPILOT_PAYMENT, check other prize sets, but still only return USD/POINT + for (const prizeSet of prizeSets) { + if (prizeSet.prizes && prizeSet.prizes.length) { + const type = prizeSet.prizes[0].type + if (type === CHALLENGE_PRIZE_TYPE.USD || type === CHALLENGE_PRIZE_TYPE.POINT) { + return type + } + } else if (prizeSet.type === CHALLENGE_PRIZE_TYPE.USD || prizeSet.type === CHALLENGE_PRIZE_TYPE.POINT) { + // Handle cases where prizeSet itself is a simple prize object (less common for top-level prizeSets) + return prizeSet.type + } + } -export const mapPrizesWithType = (prizes = [], prizeType = CHALLENGE_PRIZE_TYPE.USD) => { - return (prizes || []).map(prize => ({ ...prize, type: prizeType })) + return CHALLENGE_PRIZE_TYPE.USD } -export const applyPrizeTypeToPrizeSets = (prizeSets = [], prizeType = CHALLENGE_PRIZE_TYPE.USD) => { - return (prizeSets || []).map(set => { - if (PRIZE_SETS_WITH_TYPE.includes(set.type)) { +/** + * Apply prize type (USD or POINT) to all prizes in prize sets. + * + * @param {Array} prizeSets prize sets + * @param {String} prizeType prize type (USD or POINT) + * @returns {Array} updated prize sets + */ +export function applyPrizeTypeToPrizeSets (prizeSets, prizeType) { + return _.map(prizeSets, (prizeSet) => { + // Only update PLACEMENT and COPILOT_PAYMENT prize types + if (prizeSet.type === PRIZE_SETS_TYPE.CHALLENGE_PRIZES || prizeSet.type === PRIZE_SETS_TYPE.COPILOT_PAYMENT) { return { - ...set, - prizes: mapPrizesWithType(set.prizes, prizeType) + ...prizeSet, + prizes: _.map(prizeSet.prizes, (prize) => ({ + ...prize, + type: prizeType + })) } } - return set + // For CHECKPOINT_PRIZES, the internal prize type should remain CHALLENGE_PRIZE_TYPE.CHECKPOINT + // We assume the value is already in the correct currency based on the main challenge prize type. + return prizeSet }) }