diff --git a/src/components/learner-credit-management/assignment-modal/AssignmentModalContent.jsx b/src/components/learner-credit-management/assignment-modal/AssignmentModalContent.jsx index 0408dabd73..0c0df99fa5 100644 --- a/src/components/learner-credit-management/assignment-modal/AssignmentModalContent.jsx +++ b/src/components/learner-credit-management/assignment-modal/AssignmentModalContent.jsx @@ -27,7 +27,7 @@ const AssignmentModalContent = ({ const [emailAddressesInputValue, setEmailAddressesInputValue] = useState(''); const [assignmentAllocationMetadata, setAssignmentAllocationMetadata] = useState({}); const intl = useIntl(); - // TODO: as part of fixed price, this would need to extract the contentPrice from courseRun + // TODO: as part of fixed price, this would need to extract the contentPrice from courseRun, ENT-9394 const { contentPrice } = course.normalizedMetadata; const handleEmailAddressInputChange = (e) => { const inputValue = e.target.value; diff --git a/src/components/learner-credit-management/assignment-modal/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/assignment-modal/NewAssignmentModalButton.jsx index a0b5c970c4..2e365037c5 100644 --- a/src/components/learner-credit-management/assignment-modal/NewAssignmentModalButton.jsx +++ b/src/components/learner-credit-management/assignment-modal/NewAssignmentModalButton.jsx @@ -68,7 +68,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => { isSubsidyActive, isAssignable, aggregates, - contentPriceCents: course.normalizedMetadata.contentPrice * 100, + contentPriceCents: assignmentRun?.contentPrice ? assignmentRun.contentPrice * 100 : 0, parentContentKey: null, contentKey: course.key, courseUuid: course.uuid, @@ -143,7 +143,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => { }; const handleAllocateContentAssignments = () => { const payload = snakeCaseObject({ - contentPriceCents: course.normalizedMetadata.contentPrice * 100, // Convert to USD cents + contentPriceCents: assignmentRun.contentPrice * 100, // Convert to USD cents contentKey: assignmentRun.key, learnerEmails, }); @@ -203,7 +203,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => { parentContentKey: course.key, totalAllocatedLearners: learnerEmails.length, errorStatus: httpErrorStatus, - + errorReason, response: err, }, ); diff --git a/src/components/learner-credit-management/cards/data/useCourseCardMetadata.jsx b/src/components/learner-credit-management/cards/data/useCourseCardMetadata.jsx index beb8516beb..c17fe90e41 100644 --- a/src/components/learner-credit-management/cards/data/useCourseCardMetadata.jsx +++ b/src/components/learner-credit-management/cards/data/useCourseCardMetadata.jsx @@ -4,6 +4,7 @@ import cardFallbackImg from '@edx/brand/paragon/images/card-imagecap-fallback.pn import { defineMessages, useIntl } from '@edx/frontend-platform/i18n'; import { + EMPTY_CONTENT_PRICE_VALUE, EXEC_ED_COURSE_TYPE, formatPrice, getAssignableCourseRuns, @@ -22,18 +23,18 @@ const messages = defineMessages({ }); const getContentPriceDisplay = ({ courseRuns }) => { - const flatContentPrice = courseRuns.flatMap(run => run?.contentPrice); + const flatContentPrice = courseRuns.flatMap(run => run.contentPrice || EMPTY_CONTENT_PRICE_VALUE); // Find the max and min prices + if (!flatContentPrice.length) { + return formatPrice(EMPTY_CONTENT_PRICE_VALUE); + } const maxPrice = Math.max(...flatContentPrice); const minPrice = Math.min(...flatContentPrice); // Heuristic for displaying the price as a range or a singular price based on runs - if (flatContentPrice.length > 1 && maxPrice !== minPrice) { + if (maxPrice !== minPrice) { return `${formatPrice(minPrice)} - ${formatPrice(maxPrice)}`; } - if (flatContentPrice.length === 1) { - return formatPrice(flatContentPrice[0]); - } - return 'N/A'; + return formatPrice(flatContentPrice[0]); }; const useCourseCardMetadata = ({ diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js index 1f9724a373..ff4b0b4654 100644 --- a/src/components/learner-credit-management/data/constants.js +++ b/src/components/learner-credit-management/data/constants.js @@ -53,6 +53,7 @@ export const BUDGET_DETAIL_TAB_LABELS = { [BUDGET_DETAIL_MEMBERS_TAB]: 'Members', }; +// TODO: i18n'tify this // Card text for used in useCourseCardMetadata export const CARD_TEXT = { BADGE: { @@ -104,9 +105,12 @@ export const DAYS_UNTIL_ASSIGNMENT_ALLOCATION_EXPIRATION = 90; // Maximum days allowed from enrollment for a refund on assignments related to policies export const MAX_ALLOWABLE_REFUND_THRESHOLD_DAYS = 14; -// Start date threshold to default to today days, sets start date to today if course start date is beyond this value +// When the start date is before this number of days before today, display the alternate start date (fixed to today). export const START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS = 14; +// Default empty content_price value +export const EMPTY_CONTENT_PRICE_VALUE = 0; + // Query Key factory for the learner credit management module, intended to be used with `@tanstack/react-query`. // Inspired by https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories. export const learnerCreditManagementQueryKeys = { @@ -123,4 +127,4 @@ export const learnerCreditManagementQueryKeys = { }; // Route to learner credit -export const LEARNER_CREDIT_ROUTE = '/:enterpriseSlug/admin/:enterpriseAppPage/:budgetId/:activeTabKey'; +export const LEARNER_CREDIT_ROUTE = '/:enterpriseSlug/admin/:enterpriseAppPage/:budgetId/:activeTabKey?'; diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js index 74c1ad6be7..50fc3a55ff 100644 --- a/src/components/learner-credit-management/data/utils.js +++ b/src/components/learner-credit-management/data/utils.js @@ -559,8 +559,6 @@ export const isLmsBudget = ( /** * Determines if the course has already started. Mostly used around text formatting for tense * - * This should also help with reducing 'flaky' tests. - * * @param start * @returns {boolean} */ @@ -577,10 +575,10 @@ export const hasCourseStarted = (start) => dayjs(start).isBefore(dayjs()); * @returns {*} */ export const getAssignableCourseRuns = ({ courseRuns, subsidyExpirationDatetime, isLateRedemptionAllowed }) => { - const clonedCourseRuns = courseRuns.map(a => ({ - ...a, - enrollBy: dayjs.unix(a.enrollBy).toISOString(), - upgradeDeadline: dayjs.unix(a.upgradeDeadline).toISOString(), + const clonedCourseRuns = courseRuns.map(courseRun => ({ + ...courseRun, + enrollBy: dayjs.unix(courseRun.enrollBy).toISOString(), + upgradeDeadline: dayjs.unix(courseRun.upgradeDeadline).toISOString(), })); const assignableCourseRunsFilter = ({ enrollBy, isActive }) => { const enrollByDateThreshold = dayjs(enrollBy).isBefore( @@ -618,8 +616,10 @@ const isWithinMinimumStartDateThreshold = ({ start }) => dayjs(start).isBefore(d * * For cases where a start date does not exist, just return today's date. * - * @param start - * @param format + * @param {string} - start + * @param {string} - pacingType + * @param {string} - end + * @param {number} - weeksToComplete * @returns {string} */ export const getNormalizedStartDate = ({