diff --git a/src/components/EnterpriseApp/data/constants.js b/src/components/EnterpriseApp/data/constants.js index 5c881fefab..7bacfbd457 100644 --- a/src/components/EnterpriseApp/data/constants.js +++ b/src/components/EnterpriseApp/data/constants.js @@ -13,3 +13,9 @@ export const ROUTE_NAMES = { subscriptionManagement: 'subscriptions', contentHighlights: 'content-highlights', }; + +export const BUDGET_STATUSES = { + active: 'Active', + expired: 'Expired', + upcoming: 'Upcoming', +}; diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js index 243d6063b8..fc91edb874 100644 --- a/src/components/EnterpriseSubsidiesContext/data/hooks.js +++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js @@ -43,10 +43,10 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen, const { isActive } = subsidy; // Always check isActive for enterprise subsidies const isCurrent = isActive; // You can adjust this based on your specific requirements const activeSubsidyData = { - id: subsidy.uuid || subsidy.id, - name: subsidy.title || subsidy.displayName, - start: subsidy.activeDatetime || subsidy.startDatetime, - end: subsidy.expirationDatetime || subsidy.endDatetime, + id: subsidy.uuid, + name: subsidy.title, + start: subsidy.activeDatetime, + end: subsidy.expirationDatetime, isCurrent, source, }; @@ -61,10 +61,10 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen, const source = 'ecommerceApi'; const { isCurrent } = subsidy; const activeSubsidyData = { - id: subsidy.uuid || subsidy.id, - name: subsidy.title || subsidy.displayName, - start: subsidy.activeDatetime || subsidy.startDatetime, - end: subsidy.expirationDatetime || subsidy.endDatetime, + id: subsidy.id, + name: subsidy.displayName, + start: subsidy.startDatetime, + end: subsidy.endDatetime, isCurrent, source, }; diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx index 09a5c8cdf5..11e563e606 100644 --- a/src/components/learner-credit-management/BudgetCard-V2.jsx +++ b/src/components/learner-credit-management/BudgetCard-V2.jsx @@ -1,18 +1,44 @@ +/* eslint-disable */ import React from 'react'; import PropTypes from 'prop-types'; -import dayjs from 'dayjs'; import { Stack, + Col, + Card, + Skeleton, } from '@edx/paragon'; import { useOfferSummary } from './data/hooks'; import SubBudgetCard from './Budgetcard-V3'; +const LoadingCards = () => ( + + + + +
+ +
+
+
+ + + + +
+ +
+
+
+ +
+); const BudgetCard = ({ offer, enterpriseUUID, enterpriseSlug, offerType, + displayName, }) => { const { start, @@ -24,37 +50,35 @@ const BudgetCard = ({ offerSummary, } = useOfferSummary(enterpriseUUID, offer); - const formattedStartDate = dayjs(start).format('MMMM D, YYYY'); - const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY'); - return ( - <> -

Budgets

- {!isLoadingOfferSummary - && offerType === 'ecommerceApi' - ? ( - - ) - : offerSummary?.budgetsSumary.map((budget) => ( - - ))} - +

Budgets

+ {isLoadingOfferSummary ? ( + + ) : offerType === 'ecommerceApi' ? ( + + ) : ( + offerSummary?.budgetsSummary.map((budget) => ( + + )) + )}
); }; @@ -69,6 +93,7 @@ BudgetCard.propTypes = { enterpriseUUID: PropTypes.string.isRequired, enterpriseSlug: PropTypes.string.isRequired, offerType: PropTypes.string.isRequired, + displayName: PropTypes.string, }; export default BudgetCard; diff --git a/src/components/learner-credit-management/Budgetcard-V3.jsx b/src/components/learner-credit-management/Budgetcard-V3.jsx index 97c275b4e7..5f981acc9b 100644 --- a/src/components/learner-credit-management/Budgetcard-V3.jsx +++ b/src/components/learner-credit-management/Budgetcard-V3.jsx @@ -1,99 +1,103 @@ +/* eslint-disable */ import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import dayjs from 'dayjs'; import { - Card, - Button, - Stack, - Row, - Col, + Card, + Button, + Stack, + Row, + Col, } from '@edx/paragon'; -import { ROUTE_NAMES } from '../EnterpriseApp/data/constants'; +import { BUDGET_STATUSES, ROUTE_NAMES } from '../EnterpriseApp/data/constants'; +import { getBudgetStatus } from './data/utils'; const SubBudgetCard = ({ - id, - start, - end, - available, - spent, - enterpriseSlug, + id, + start, + end, + available, + spent, + displayName, + enterpriseSlug, }) => { - const formattedStartDate = dayjs(start).format('MMMM D, YYYY'); - const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY'); + const formattedStartDate = dayjs(start).format('MMMM D, YYYY'); + const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY'); + const budgetStatus = getBudgetStatus(start, end); - const renderActions = (id) => ( - - ); - - const renderCardHeader = (budgetType, id) => { - const subtitle = ( -
- - {formattedStartDate} - {formattedExpirationDate} - -
+ const renderActions = (id) => ( + ); - return ( - - {renderActions(id)} - + const renderCardHeader = (budgetType, id) => { + const subtitle = ( +
+ + {formattedStartDate} - {formattedExpirationDate} + +
+ ); + + return ( + + {budgetStatus !== BUDGET_STATUSES.upcoming && renderActions(id)} + )} - /> - ); - }; + /> + ); + }; - const renderCardSection = (available, spent) => ( - - - - Available - {available} - - - Spent - {spent} - - - - ); + const renderCardSection = (available, spent) => ( + + + + Available + {available} + + + Spent + {spent} + + + + ); - return ( - - - - {renderCardHeader('Overview', id)} - {renderCardSection(available, spent)} - - - - ); + return ( + + + + {renderCardHeader(displayName || 'Overview', id)} + {budgetStatus !== BUDGET_STATUSES.upcoming && renderCardSection(available, spent)} + + + + ); }; SubBudgetCard.propTypes = { - enterpriseSlug: PropTypes.string.isRequired, - id: PropTypes.string, - start: PropTypes.string, - end: PropTypes.string, - spent: PropTypes.number, - available: PropTypes.number, - + enterpriseSlug: PropTypes.string.isRequired, + id: PropTypes.string, + start: PropTypes.string, + end: PropTypes.string, + spent: PropTypes.number, + available: PropTypes.number, + displayName: PropTypes.string, }; export default SubBudgetCard; diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx index 9dc6a227c1..8bedc5dd5e 100644 --- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx +++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx @@ -25,6 +25,7 @@ const MultipleBudgetsPicker = ({ enterpriseSlug={enterpriseSlug} enableLearnerPortal={enableLearnerPortal} offerType={offer.source} + displayName={offer.name} /> ))} diff --git a/src/components/learner-credit-management/data/hooks.js b/src/components/learner-credit-management/data/hooks.js index 5e107e9428..31577f36a7 100644 --- a/src/components/learner-credit-management/data/hooks.js +++ b/src/components/learner-credit-management/data/hooks.js @@ -74,7 +74,7 @@ const applyFiltersToOptions = (filters, options) => { } }; -export const useOfferRedemptions = (enterpriseUUID, offerId= null, budgetId = null) => { +export const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => { const shouldTrackFetchEvents = useRef(false); const [isLoading, setIsLoading] = useState(true); const [offerRedemptions, setOfferRedemptions] = useState({ diff --git a/src/components/learner-credit-management/data/tests/hooks.test.js b/src/components/learner-credit-management/data/tests/hooks.test.js index f70f533d4d..5459167674 100644 --- a/src/components/learner-credit-management/data/tests/hooks.test.js +++ b/src/components/learner-credit-management/data/tests/hooks.test.js @@ -73,7 +73,7 @@ describe('useOfferSummary', () => { remainingFunds: 4800, percentUtilized: 0.04, offerId: 1, - budgetsSumary: [], + budgetsSummary: [], offerType: undefined, }; expect(result.current).toEqual({ diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js index b24abe27d4..f39d9f33a9 100644 --- a/src/components/learner-credit-management/data/tests/utils.test.js +++ b/src/components/learner-credit-management/data/tests/utils.test.js @@ -23,7 +23,7 @@ describe('transformOfferSummary', () => { remainingFunds: 0.0, percentUtilized: 1.0, offerType: EXEC_ED_OFFER_TYPE, - budgetsSumary: [], + budgetsSummary: [], offerId: undefined, }); }); @@ -36,7 +36,7 @@ describe('transformOfferSummary', () => { percentOfOfferSpent: null, offerType: 'Site', offerId: '123', - budgetsSumary: [], + budgetsSummary: [], }; expect(transformOfferSummary(offerSummary)).toEqual({ @@ -48,7 +48,7 @@ describe('transformOfferSummary', () => { redeemedFundsExecEd: undefined, redeemedFundsOcm: undefined, offerId: '123', - budgetsSumary: [], + budgetsSummary: [], }); }); }); diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js index 1c7a52c459..fc193744c2 100644 --- a/src/components/learner-credit-management/data/utils.js +++ b/src/components/learner-credit-management/data/utils.js @@ -3,6 +3,7 @@ import { LOW_REMAINING_BALANCE_PERCENT_THRESHOLD, NO_BALANCE_REMAINING_DOLLAR_THRESHOLD, } from './constants'; +import { BUDGET_STATUSES } from '../../EnterpriseApp/data/constants'; /** * Transforms offer summary from API for display in the UI, guarding * against bad data (e.g., accounting for refunds). @@ -12,23 +13,22 @@ import { */ export const transformOfferSummary = (offerSummary) => { if (!offerSummary) { return null; } - let budgetsSumary = [] - + const budgetsSummary = []; if (offerSummary?.budgets) { - const budgets = offerSummary?.budgets + const budgets = offerSummary?.budgets; for (let i = 0; i < budgets.length; i++) { - let redeemedFunds = budgets[i].amountOfPolicySpent && parseFloat(budgets[i].amountOfPolicySpent); - let remainingFunds = budgets[i].remainingBalance && parseFloat(budgets[i].remainingBalance); + const redeemedFunds = budgets[i].amountOfPolicySpent && parseFloat(budgets[i].amountOfPolicySpent); + const remainingFunds = budgets[i].remainingBalance && parseFloat(budgets[i].remainingBalance); // Create an object with key-value pairs const budgetEntry = { redeemedFunds, remainingFunds, - ...budgets[i] + ...budgets[i], }; - budgetsSumary.push(budgetEntry); + budgetsSummary.push(budgetEntry); } } - + const totalFunds = offerSummary.maxDiscount && parseFloat(offerSummary.maxDiscount); let redeemedFunds = offerSummary.amountOfOfferSpent && parseFloat(offerSummary.amountOfOfferSpent); let redeemedFundsOcm = offerSummary.amountOfferSpentOcm && parseFloat(offerSummary.amountOfferSpentOcm); @@ -64,7 +64,7 @@ export const transformOfferSummary = (offerSummary) => { percentUtilized, offerType, offerId, - budgetsSumary, + budgetsSummary, }; }; @@ -110,7 +110,20 @@ export const getProgressBarVariant = ({ percentUtilized, remainingFunds }) => { return variant; }; -// Utility function to check if the ID is a UUID -export const isUUID = (id) => { - return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id); -} +// Utility function to check if the ID is a UUID +export const isUUID = (id) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id); + +// Utility function to check the budget status +export const getBudgetStatus = (startDateStr, endDateStr) => { + const currentDate = new Date(); + const startDate = new Date(startDateStr); + const endDate = new Date(endDateStr); + + if (currentDate < startDate) { + return BUDGET_STATUSES.upcoming; + } + if (currentDate >= startDate && currentDate <= endDate) { + return BUDGET_STATUSES.active; + } + return BUDGET_STATUSES.expired; +}; diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx index dcff64ce3e..bd445a8676 100644 --- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx +++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx @@ -81,7 +81,7 @@ describe('', () => { remainingFunds: 4800, percentUtilized: 0.04, offerType: 'Site', - budgetsSumary: [ + budgetsSummary: [ { id: 123, start: '2022-01-01',