diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index 78d22f57c7..5eb4f2d678 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -17,46 +17,55 @@ import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
*
* @returns Budget card component(s).
*/
-const BudgetCard = ({
- budget,
- enterpriseUUID,
- enterpriseSlug,
-}) => {
+const BudgetCard = ({ original }) => {
+ const {
+ aggregates,
+ end,
+ enterpriseSlug,
+ enterpriseUUID,
+ id,
+ isAssignable,
+ isRetired,
+ name,
+ source,
+ start,
+ } = original;
+
const {
isLoading: isLoadingSubsidySummaryAnalyticsApi,
subsidySummary: subsidySummaryAnalyticsApi,
- } = useSubsidySummaryAnalyticsApi(enterpriseUUID, budget.id, budget.source);
+ } = useSubsidySummaryAnalyticsApi(enterpriseUUID, id, source);
// Subsidy Access Policies will always have a single budget, so we can render a single card
// without relying on `useSubsidySummaryAnalyticsApi`.
- if (budget.source === BUDGET_TYPES.policy) {
+ if (source === BUDGET_TYPES.policy) {
return (
);
}
// Enterprise Offers (ecommerce) will always have a single budget, so we can render a single card.
- if (budget.source === BUDGET_TYPES.ecommerce) {
+ if (source === BUDGET_TYPES.ecommerce) {
return (
);
@@ -74,8 +83,8 @@ const BudgetCard = ({
isLoading={isLoadingSubsidySummaryAnalyticsApi}
key={subBudget.subsidyAccessPolicyUuid}
id={subBudget.subsidyAccessPolicyUuid}
- start={budget.start}
- end={budget.end}
+ start={start}
+ end={end}
available={subBudget.remainingFunds}
spent={subBudget.redeemedFunds}
displayName={subBudget.subsidyAccessPolicyDisplayName}
@@ -85,7 +94,7 @@ const BudgetCard = ({
};
BudgetCard.propTypes = {
- budget: PropTypes.shape({
+ original: PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
name: PropTypes.string.isRequired,
start: PropTypes.string.isRequired,
@@ -98,9 +107,10 @@ BudgetCard.propTypes = {
}),
isAssignable: PropTypes.bool,
isRetired: PropTypes.bool,
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+ status: PropTypes.string,
}).isRequired,
- enterpriseUUID: PropTypes.string.isRequired,
- enterpriseSlug: PropTypes.string.isRequired,
};
export default BudgetCard;
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index 2477b74281..0af1fe06a5 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -1,13 +1,17 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import {
- Stack,
+ DataTable,
+ CardView,
+ TextFilter,
+ CheckboxFilter,
Row,
Col,
} from '@edx/paragon';
+import groupBy from 'lodash/groupBy';
import BudgetCard from './BudgetCard';
-import { orderBudgets } from './data/utils';
+import { getBudgetStatus, orderBudgets } from './data/utils';
const MultipleBudgetsPicker = ({
budgets,
@@ -16,27 +20,71 @@ const MultipleBudgetsPicker = ({
enableLearnerPortal,
}) => {
const orderedBudgets = orderBudgets(budgets);
+
+ const rows = useMemo(
+ () => orderedBudgets.map(budget => {
+ const budgetLabel = getBudgetStatus({
+ startDateStr: budget.start,
+ endDateStr: budget.end,
+ isBudgetRetired: budget.isRetired,
+ });
+
+ return ({
+ ...budget,
+ status: budgetLabel.status,
+ enterpriseUUID,
+ enterpriseSlug,
+ enableLearnerPortal,
+ });
+ }),
+ [orderedBudgets, enterpriseUUID, enterpriseSlug, enableLearnerPortal],
+ );
+
+ const budgetLabels = orderedBudgets.map(budget => (
+ getBudgetStatus({
+ startDateStr: budget.start,
+ endDateStr: budget.end,
+ isBudgetRetired: budget.isRetired,
+ })
+ ));
+ const budgetLabelsByStatus = groupBy(budgetLabels, 'status');
+ const reducedChoices = Object.keys(budgetLabelsByStatus).map(budgetLabel => ({
+ name: budgetLabel,
+ number: budgetLabelsByStatus[budgetLabel].length,
+ value: budgetLabel,
+ }));
+
return (
-
-
+ <>
+
Budgets
-
-
-
- {orderedBudgets.map(budget => (
-
- ))}
-
-
-
-
+
+
+
+
+
+ >
);
};
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
index 16a2b1cc7a..f27e0f0c77 100644
--- a/src/components/learner-credit-management/SubBudgetCard.jsx
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -6,7 +6,6 @@ import classNames from 'classnames';
import {
Card,
Button,
- Row,
Col,
Badge,
Stack,
@@ -123,8 +122,8 @@ const BaseSubBudgetCard = ({
title={
Balance
}
muted
>
-
-
+
+
Available
{isFetchingBudgets ? : formatPrice(available)}
@@ -144,7 +143,7 @@ const BaseSubBudgetCard = ({
{isFetchingBudgets ? : formatPrice(spent)}
-
+
);
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index 80ec4dc7db..43687867db 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -90,18 +90,20 @@ describe('', () => {
});
it('displays correctly for a scheduled Enterprise Offers (ecommerce)', () => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '3022-01-01',
end: '3023-01-01',
source: BUDGET_TYPES.ecommerce,
+ aggregates: mockBudgetAggregates,
};
- const mockBudgetAggregates = {
- total: 5000,
- spent: 200,
- available: 4800,
- };
+
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
subsidySummary: {
@@ -116,9 +118,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -130,17 +130,18 @@ describe('', () => {
});
it('displays correctly for a scheduled Subsidy (enterprise-subsidy)', () => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '3022-01-01',
end: '4023-01-01',
source: BUDGET_TYPES.subsidy,
- };
- const mockBudgetAggregates = {
- total: 5000,
- spent: 200,
- available: 4800,
+ aggregates: mockBudgetAggregates,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -167,9 +168,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -202,6 +201,8 @@ describe('', () => {
spent: mockBudgetAggregates.spent,
},
isAssignable: isAssignableBudget,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -209,9 +210,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -223,17 +222,20 @@ describe('', () => {
});
it('displays correctly for an expired Enterprise Offers (ecommerce)', () => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '2022-01-01',
end: '2023-01-01',
source: BUDGET_TYPES.ecommerce,
- };
- const mockBudgetAggregates = {
- total: 5000,
- spent: 200,
- available: 4800,
+ aggregates: mockBudgetAggregates,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -249,9 +251,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -268,17 +268,20 @@ describe('', () => {
});
it('displays correctly for an expired Subsidy (enterprise-subsidy)', () => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '2022-01-01',
end: '2023-01-01',
source: BUDGET_TYPES.subsidy,
- };
- const mockBudgetAggregates = {
- total: 5000,
- spent: 200,
- available: 4800,
+ aggregates: mockBudgetAggregates,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -305,9 +308,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -345,6 +346,8 @@ describe('', () => {
spent: mockBudgetAggregates.spent,
},
isAssignable: isAssignableBudget,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -352,9 +355,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -371,17 +372,20 @@ describe('', () => {
});
it('displays correctly for a current Enterprise Offers (ecommerce)', () => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '2022-01-01',
end: '3022-01-01',
source: BUDGET_TYPES.ecommerce,
- };
- const mockBudgetAggregates = {
- total: 5000,
- spent: 200,
- available: 4800,
+ aggregates: mockBudgetAggregates,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -397,9 +401,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -423,17 +425,20 @@ describe('', () => {
});
it('displays correctly for a current Subsidy (enterprise-subsidy)', () => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '2022-01-01',
end: '3023-01-01',
source: BUDGET_TYPES.subsidy,
- };
- const mockBudgetAggregates = {
- total: 5000,
- spent: 200,
- available: 4800,
+ aggregates: mockBudgetAggregates,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -460,9 +465,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
@@ -507,6 +510,8 @@ describe('', () => {
spent: mockBudgetAggregates.spent,
},
isAssignable: isAssignableBudget,
+ enterpriseSlug,
+ enterpriseUUID,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
@@ -514,9 +519,7 @@ describe('', () => {
});
render();
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
diff --git a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
index f81d9afeb5..041114312b 100644
--- a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
+++ b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
@@ -10,6 +10,8 @@ import {
} from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
import MultipleBudgetsPage from '../MultipleBudgetsPage';
import { queryClient } from '../../test/testUtils';
@@ -61,9 +63,11 @@ const MultipleBudgetsPageWrapper = ({
}) => (
-
-
-
+
+
+
+
+
);