Skip to content

Commit

Permalink
feat: show all enterprise budgets regardless of plan and route correctly
Browse files Browse the repository at this point in the history
fix: full page refresh issue when clicking 'Budgets', added test coverage and fixed lint issues

feat: show all enterprise budgets regardless of plan and route correctly

fix: moved LCM routes to separate file

fix: resolve spacing and repetitions issue

fix: resolve spacing and repetitions issue

fix: improved test coverage

refactor: improve code efficiency and readability

fix: incorporate adam's suggestions and nits

fix: incorporated adam's feedback

fix: incorporate adam's feedback
  • Loading branch information
jajjibhai008 committed Sep 27, 2023
1 parent e12f09a commit 7874784
Show file tree
Hide file tree
Showing 17 changed files with 769 additions and 265 deletions.
8 changes: 3 additions & 5 deletions src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { SubscriptionManagementPage } from '../subscriptions';
import { PlotlyAnalyticsPage } from '../PlotlyAnalytics';
import { ROUTE_NAMES } from './data/constants';
import BulkEnrollmentResultsDownloadPage from '../BulkEnrollmentResultsDownloadPage';
import LearnerCreditManagement from '../learner-credit-management';
import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
import ContentHighlights from '../ContentHighlights';
import LearnerCreditManagementRoutes from '../learner-credit-management';

const EnterpriseAppRoutes = ({
baseUrl,
Expand Down Expand Up @@ -98,10 +98,8 @@ const EnterpriseAppRoutes = ({
/>

{canManageLearnerCredit && (
<Route
exact
path={`${baseUrl}/admin/${ROUTE_NAMES.learnerCredit}`}
component={LearnerCreditManagement}
<LearnerCreditManagementRoutes
baseUrl={baseUrl}
/>
)}

Expand Down
11 changes: 11 additions & 0 deletions src/components/EnterpriseApp/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,14 @@ export const ROUTE_NAMES = {
subscriptionManagement: 'subscriptions',
contentHighlights: 'content-highlights',
};

export const BUDGET_STATUSES = {
active: 'Active',
expired: 'Expired',
upcoming: 'Upcoming',
};

export const BUDGET_TYPES = {
ecommerce: 'ecommerce',
subsidy: 'subsidy',
};
65 changes: 32 additions & 33 deletions src/components/EnterpriseSubsidiesContext/data/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { camelCaseObject } from '@edx/frontend-platform/utils';
import EcommerceApiService from '../../../data/services/EcommerceApiService';
import LicenseManagerApiService from '../../../data/services/LicenseManagerAPIService';
import SubsidyApiService from '../../../data/services/EnterpriseSubsidyApiService';
import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';

export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen, enterpriseId }) => {
const [offers, setOffers] = useState([]);
Expand All @@ -25,42 +26,40 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen,
try {
const [enterpriseSubsidyResponse, ecommerceApiResponse] = await Promise.all([
SubsidyApiService.getSubsidyByCustomerUUID(enterpriseId, { subsidyType: 'learner_credit' }),
EcommerceApiService.fetchEnterpriseOffers({
isCurrent: true,
}),
EcommerceApiService.fetchEnterpriseOffers(),
]);

// If there are no subsidies in enterprise, fall back to the e-commerce API.
let { results } = camelCaseObject(enterpriseSubsidyResponse.data);
let source = 'subsidyApi';
// We have to consider both type of offers active and inactive.

if (results.length === 0) {
results = camelCaseObject(ecommerceApiResponse.data.results);
source = 'ecommerceApi';
}
let activeSubsidyFound = false;
if (results.length !== 0) {
let subsidy = results[0];
const offerData = [];
let activeSubsidyData = {};
for (let i = 0; i < results.length; i++) {
subsidy = results[i];
activeSubsidyFound = source === 'ecommerceApi'
? subsidy.isCurrent
: subsidy.isActive;
if (activeSubsidyFound === true) {
activeSubsidyData = {
id: subsidy.uuid || subsidy.id,
name: subsidy.title || subsidy.displayName,
start: subsidy.activeDatetime || subsidy.startDatetime,
end: subsidy.expirationDatetime || subsidy.endDatetime,
isCurrent: activeSubsidyFound,
};
offerData.push(activeSubsidyData);
setCanManageLearnerCredit(true);
}
}
setOffers(offerData);
const enterpriseSubsidyResults = camelCaseObject(enterpriseSubsidyResponse.data).results;
const ecommerceOffersResults = camelCaseObject(ecommerceApiResponse.data.results);

const offerData = [];

enterpriseSubsidyResults.forEach((result) => {
offerData.push({
source: BUDGET_TYPES.subsidy,
id: result.uuid,
name: result.title,
start: result.activeDatetime,
end: result.expirationDatetime,
isCurrent: result.isActive,
});
});

ecommerceOffersResults.forEach((result) => {
offerData.push({
source: BUDGET_TYPES.ecommerce,
id: (result.id).toString(),
name: result.displayName,
start: result.startDatetime,
end: result.endDatetime,
isCurrent: result.isCurrent,
});
});
setOffers(offerData);
if (offerData.length > 0) {
setCanManageLearnerCredit(true);
}
} catch (error) {
logError(error);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import {
screen,
render,
} from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import { IntlProvider } from '@edx/frontend-platform/i18n';
import { MemoryRouter } from 'react-router-dom';
import BudgetDetailPage from '../../../learner-credit-management/BudgetDetailPage';
import { useOfferSummary, useOfferRedemptions } from '../../../learner-credit-management/data/hooks';
import { EXEC_ED_OFFER_TYPE } from '../../../learner-credit-management/data/constants';
import { EnterpriseSubsidiesContext } from '../..';

jest.mock('../../../learner-credit-management/data/hooks');

useOfferSummary.mockReturnValue({
isLoading: false,
offerSummary: null,
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
offerRedemptions: {
itemCount: 0,
pageCount: 0,
results: [],
},
fetchOfferRedemptions: jest.fn(),
});

const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
const enterpriseId = 'test-enterprise';
const enterpriseUUID = '1234';
const initialStore = {
portalConfiguration: {
enterpriseId,
enterpriseSlug: enterpriseId,

},
};
const store = getMockStore({ ...initialStore });

const mockEnterpriseOfferId = '123';

const mockOfferDisplayName = 'Test Enterprise Offer';
const mockOfferSummary = {
totalFunds: 5000,
redeemedFunds: 200,
remainingFunds: 4800,
percentUtilized: 0.04,
offerType: EXEC_ED_OFFER_TYPE,
};

const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};

const BudgetDetailPageWrapper = ({
enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
...rest
}) => (
<MemoryRouter initialEntries={['/test-enterprise/admin/learner-credit/1234']}>

<Provider store={store}>
<IntlProvider locale="en">
<EnterpriseSubsidiesContext.Provider value={enterpriseSubsidiesContextValue}>
<BudgetDetailPage {...rest} />
</EnterpriseSubsidiesContext.Provider>
</IntlProvider>
</Provider>
</MemoryRouter>
);

describe('<BudgetDetailPage />', () => {
describe('with enterprise offer', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('displays table on clicking view budget', async () => {
const mockOffer = {
id: mockEnterpriseOfferId,
name: mockOfferDisplayName,
start: '2022-01-01',
end: '2023-01-01',
};
useOfferSummary.mockReturnValue({
isLoading: false,
offerSummary: mockOfferSummary,
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
offerRedemptions: {
itemCount: 0,
pageCount: 0,
results: [],
},
fetchOfferRedemptions: jest.fn(),
});
render(<BudgetDetailPageWrapper
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseId}
offer={mockOffer}
/>);
expect(screen.getByText('Learner Credit Management'));
expect(screen.getByText('Overview'));
expect(screen.getByText('No results found'));
});

it('displays loading message while loading data', async () => {
useOfferSummary.mockReturnValue({
isLoading: true,
offerSummary: null,
});
useOfferRedemptions.mockReturnValue({
isLoading: true,
offerRedemptions: {
itemCount: 0,
pageCount: 0,
results: [],
},
fetchOfferRedemptions: jest.fn(),
});

render(<BudgetDetailPageWrapper
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseId}
/>);

expect(screen.getByText('loading'));
});
});
});
Loading

0 comments on commit 7874784

Please sign in to comment.