diff --git a/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.js b/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.js
index e35559c971..5e765cf351 100644
--- a/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.js
+++ b/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.js
@@ -20,7 +20,7 @@ export default function useIsAssignmentsOnlyLearner() {
},
},
} = useBrowseAndRequest();
- const { data: { couponCodeAssignments } } = useCouponCodes();
+ const { data: { couponCodeRedemptionCount } } = useCouponCodes();
const { data: { hasCurrentEnterpriseOffers } } = useEnterpriseOffers();
const { data: redeemableLearnerCreditPolicies } = useRedeemablePolicies();
@@ -29,7 +29,7 @@ export default function useIsAssignmentsOnlyLearner() {
subscriptionLicense,
licenseRequests,
couponCodeRequests,
- couponCodesCount: couponCodeAssignments.length,
+ couponCodesCount: couponCodeRedemptionCount,
redeemableLearnerCreditPolicies,
hasCurrentEnterpriseOffers,
});
diff --git a/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.test.jsx b/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.test.jsx
index 7bab04b023..f9a496c61d 100644
--- a/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.test.jsx
+++ b/src/components/app/data/hooks/useIsAssignmentsOnlyLearner.test.jsx
@@ -385,9 +385,7 @@ describe('useIsAssignmentsOnlyLearner', () => {
},
},
hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
+ subscriptionPlan: undefined,
subscriptionLicense: undefined,
licenseRequests: [],
couponCodesCount: 1,
@@ -475,7 +473,10 @@ describe('useIsAssignmentsOnlyLearner', () => {
});
useCouponCodes.mockReturnValue({
data: {
- couponCodeAssignments: new Array(couponCodesCount).fill('test-coupon-code-assignments'),
+ couponCodeAssignments: new Array(couponCodesCount).fill({
+ redemptionsRemaining: 1,
+ }),
+ couponCodeRedemptionCount: couponCodesCount,
},
});
useEnterpriseOffers.mockReturnValue({
diff --git a/src/components/app/data/services/subsidies/couponCodes.js b/src/components/app/data/services/subsidies/couponCodes.js
index c66864dd5c..3a4ef855fc 100644
--- a/src/components/app/data/services/subsidies/couponCodes.js
+++ b/src/components/app/data/services/subsidies/couponCodes.js
@@ -3,6 +3,7 @@ import { logError } from '@edx/frontend-platform/logging';
import { fetchPaginatedData } from '../utils';
import { hasValidStartExpirationDates } from '../../../../../utils/common';
+import { findCouponCodeRedemptionCount } from '../../../../enterprise-user-subsidy/coupons';
// Coupon Codes
@@ -68,8 +69,10 @@ export async function fetchCouponCodes(enterpriseUuid) {
fetchCouponsOverview(enterpriseUuid),
fetchCouponCodeAssignments(enterpriseUuid),
]);
+ const couponCodeRedemptionCount = findCouponCodeRedemptionCount(results[1]);
return {
couponsOverview: results[0],
couponCodeAssignments: results[1],
+ couponCodeRedemptionCount,
};
}
diff --git a/src/components/app/data/services/subsidies/couponCodes.test.js b/src/components/app/data/services/subsidies/couponCodes.test.js
index 4db6921591..5ca0865124 100644
--- a/src/components/app/data/services/subsidies/couponCodes.test.js
+++ b/src/components/app/data/services/subsidies/couponCodes.test.js
@@ -81,7 +81,10 @@ describe('fetchCouponCodes', () => {
const COUPON_CODE_ASSIGNMENTS_URL = getCouponCodeAssignmentsUrl(enterpriseId);
const COUPONS_OVERVIEW_URL = getCouponsOverviewUrl(enterpriseId);
const couponCodeAssignmentsResponse = {
- results: [{ id: 123 }],
+ results: [
+ { code: 123, redemptionsRemaining: 1 },
+ { code: 456, redemptionsRemaining: 2 },
+ ],
};
const couponsOverviewResponse = {
results: [{ id: 123 }],
@@ -90,10 +93,14 @@ describe('fetchCouponCodes', () => {
axiosMock.onGet(COUPONS_OVERVIEW_URL).reply(200, couponsOverviewResponse);
const result = await fetchCouponCodes(enterpriseId);
- const expectedCouponsCodeAssignmentsResponse = [{ ...couponCodeAssignmentsResponse.results[0], available: false }];
+ const expectedCouponsCodeAssignmentsResponse = couponCodeAssignmentsResponse.results.map((assignment) => ({
+ ...assignment,
+ available: false,
+ }));
expect(result).toEqual({
couponCodeAssignments: expectedCouponsCodeAssignmentsResponse,
couponsOverview: couponsOverviewResponse.results,
+ couponCodeRedemptionCount: 3,
});
});
});
diff --git a/src/components/course/data/courseLoader.js b/src/components/course/data/courseLoader.js
index 28a032e0b1..40a2a120fe 100644
--- a/src/components/course/data/courseLoader.js
+++ b/src/components/course/data/courseLoader.js
@@ -90,7 +90,11 @@ export default function makeCourseLoader(queryClient) {
const redeemableLearnerCreditPolicies = subsidyResponses[0];
const { customerAgreement, subscriptionPlan, subscriptionLicense } = subsidyResponses[1];
const { hasCurrentEnterpriseOffers, currentEnterpriseOffers } = subsidyResponses[2];
- const { couponCodeAssignments, couponsOverview } = subsidyResponses[3];
+ const {
+ couponCodeAssignments,
+ couponCodeRedemptionCount,
+ couponsOverview,
+ } = subsidyResponses[3];
const licenseRequests = subsidyResponses[4];
const couponCodeRequests = subsidyResponses[5];
const browseAndRequestConfiguration = subsidyResponses[6];
@@ -99,7 +103,7 @@ export default function makeCourseLoader(queryClient) {
subscriptionLicense,
licenseRequests,
couponCodeRequests,
- couponCodesCount: couponCodeAssignments.length,
+ couponCodesCount: couponCodeRedemptionCount,
redeemableLearnerCreditPolicies,
hasCurrentEnterpriseOffers,
});
diff --git a/src/components/course/enrollment/components/ToEcomBasketPage.jsx b/src/components/course/enrollment/components/ToEcomBasketPage.jsx
index b9b2ecd8e0..2759634bf4 100644
--- a/src/components/course/enrollment/components/ToEcomBasketPage.jsx
+++ b/src/components/course/enrollment/components/ToEcomBasketPage.jsx
@@ -19,7 +19,7 @@ import { useCouponCodes, useCourseMetadata, useEnterpriseCourseEnrollments } fro
*/
const ToEcomBasketPage = ({ enrollLabel, enrollmentUrl, courseRunPrice }) => {
const { userSubsidyApplicableToCourse } = useUserSubsidyApplicableToCourse();
- const { data: { couponCodeAssignments } } = useCouponCodes();
+ const { data: { couponCodeRedemptionCount } } = useCouponCodes();
const [isModalOpen, setIsModalOpen] = useState(false);
const {
data: {
@@ -57,7 +57,7 @@ const ToEcomBasketPage = ({ enrollLabel, enrollmentUrl, courseRunPrice }) => {
enrollmentUrl={enrollmentUrl}
courseRunPrice={courseRunPrice}
userSubsidyApplicableToCourse={userSubsidyApplicableToCourse}
- couponCodesCount={couponCodeAssignments.length}
+ couponCodesCount={couponCodeRedemptionCount}
onEnroll={handleEnroll}
/>
>
diff --git a/src/components/dashboard/main-content/course-enrollments/course-cards/UpgradeCourseButton.jsx b/src/components/dashboard/main-content/course-enrollments/course-cards/UpgradeCourseButton.jsx
index 0b82dd5de6..e40fff4b5d 100644
--- a/src/components/dashboard/main-content/course-enrollments/course-cards/UpgradeCourseButton.jsx
+++ b/src/components/dashboard/main-content/course-enrollments/course-cards/UpgradeCourseButton.jsx
@@ -18,7 +18,7 @@ const UpgradeCourseButton = ({
const [isModalOpen, setIsModalOpen] = useState(false);
const { data: enterpriseCustomer } = useEnterpriseCustomer();
- const { data: { couponCodeAssignments } } = useCouponCodes();
+ const { data: { couponCodeRedemptionCount } } = useCouponCodes();
const {
subsidyForCourse,
couponUpgradeUrl,
@@ -57,7 +57,7 @@ const UpgradeCourseButton = ({
enrollmentUrl={couponUpgradeUrl}
courseRunPrice={courseRunPrice}
userSubsidyApplicableToCourse={subsidyForCourse}
- couponCodesCount={couponCodeAssignments.length}
+ couponCodesCount={couponCodeRedemptionCount}
onEnroll={handleEnroll}
/>
>
diff --git a/src/components/enterprise-subsidy-requests/data/hooks.js b/src/components/enterprise-subsidy-requests/data/hooks.js
deleted file mode 100644
index f6d14f152d..0000000000
--- a/src/components/enterprise-subsidy-requests/data/hooks.js
+++ /dev/null
@@ -1,155 +0,0 @@
-import {
- useState, useEffect, useCallback, useContext,
-} from 'react';
-import { logError } from '@edx/frontend-platform/logging';
-import { camelCaseObject } from '@edx/frontend-platform/utils';
-import { AppContext } from '@edx/frontend-platform/react';
-
-import {
- fetchSubsidyRequestConfiguration,
- fetchLicenseRequests,
- fetchCouponCodeRequests,
-} from './service';
-import { getErrorResponseStatusCode } from '../../../utils/common';
-import { SUBSIDY_REQUEST_STATE, SUBSIDY_TYPE } from '../../../constants';
-
-export const useSubsidyRequestConfiguration = (enterpriseUUID) => {
- const [subsidyRequestConfiguration, setSubsidyRequestConfiguration] = useState();
- const [isLoading, setIsLoading] = useState(true);
-
- useEffect(() => {
- const fetchCustomerConfiguration = async () => {
- try {
- const response = await fetchSubsidyRequestConfiguration(enterpriseUUID);
- const config = camelCaseObject(response.data);
- setSubsidyRequestConfiguration(config);
- } catch (error) {
- const httpErrorStatus = getErrorResponseStatusCode(error);
- if (httpErrorStatus === 404) {
- // Customer configuration does not exist, subsidy requests are turned off.
- setSubsidyRequestConfiguration(null);
- } else {
- logError(error);
- }
- } finally {
- setIsLoading(false);
- }
- };
-
- fetchCustomerConfiguration(enterpriseUUID);
- }, [enterpriseUUID]);
-
- return { subsidyRequestConfiguration, isLoading };
-};
-
-/**
- * @param {{
- * enterpriseCustomerUuid: string,
- * subsidyRequestsEnabled: boolean,
- * subsidyType: string
- * }} subsidyRequestConfiguration The subsidy request configuration for the customer
- * @returns {Object} { couponCodeRequests, licenseRequests, isLoading }
- */
-export const useSubsidyRequests = (subsidyRequestConfiguration) => {
- const [licenseRequests, setLicenseRequests] = useState([]);
- const [couponCodeRequests, setCouponCodeRequests] = useState([]);
- const [isLoading, setIsLoading] = useState(false);
- const { authenticatedUser: { email: userEmail } } = useContext(AppContext);
-
- const fetchSubsidyRequests = useCallback(async (subsidyType) => {
- setIsLoading(true);
- try {
- const { enterpriseCustomerUuid: enterpriseUUID } = subsidyRequestConfiguration;
-
- const options = {
- enterpriseUUID,
- userEmail,
- state: SUBSIDY_REQUEST_STATE.REQUESTED,
- };
-
- if (subsidyType === SUBSIDY_TYPE.COUPON) {
- const { data: { results } } = await fetchCouponCodeRequests(options);
- const requests = camelCaseObject(results);
- setCouponCodeRequests(requests);
- } if (subsidyType === SUBSIDY_TYPE.LICENSE) {
- const { data: { results } } = await fetchLicenseRequests(options);
- const requests = camelCaseObject(results);
- setLicenseRequests(requests);
- }
- } catch (error) {
- logError(error);
- } finally {
- setIsLoading(false);
- }
- }, [subsidyRequestConfiguration, userEmail]);
-
- const loadSubsidyRequests = useCallback(() => {
- if (subsidyRequestConfiguration?.subsidyRequestsEnabled) {
- const { subsidyType } = subsidyRequestConfiguration;
- if (subsidyType) {
- fetchSubsidyRequests(subsidyType);
- }
- }
- }, [fetchSubsidyRequests, subsidyRequestConfiguration]);
-
- useEffect(() => {
- loadSubsidyRequests();
- }, [loadSubsidyRequests]);
-
- return {
- couponCodeRequests,
- licenseRequests,
- isLoading,
- refreshSubsidyRequests: loadSubsidyRequests,
- };
-};
-
-export const useCatalogsForSubsidyRequests = ({
- subsidyRequestConfiguration,
- isLoadingSubsidyRequestConfiguration,
- customerAgreementConfig,
- couponsOverview,
-}) => {
- const [catalogs, setCatalogs] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
-
- useEffect(() => {
- const getCatalogs = async () => {
- if (subsidyRequestConfiguration.subsidyType === SUBSIDY_TYPE.COUPON) {
- const catalogsFromCoupons = couponsOverview
- .filter(coupon => !!coupon.available)
- .map(coupon => coupon.enterpriseCatalogUuid);
-
- setCatalogs([...new Set(catalogsFromCoupons)]);
- }
-
- if (subsidyRequestConfiguration.subsidyType === SUBSIDY_TYPE.LICENSE) {
- const catalogsFromSubscriptions = customerAgreementConfig.subscriptions
- .filter(subscription => subscription.daysUntilExpirationIncludingRenewals > 0)
- .map(subscription => subscription.enterpriseCatalogUuid);
-
- setCatalogs([...new Set(catalogsFromSubscriptions)]);
- }
-
- setIsLoading(false);
- };
-
- if (!isLoadingSubsidyRequestConfiguration) {
- if (subsidyRequestConfiguration?.subsidyRequestsEnabled) {
- getCatalogs();
- return;
- }
- setIsLoading(false);
- }
- }, [
- customerAgreementConfig,
- isLoadingSubsidyRequestConfiguration,
- subsidyRequestConfiguration,
- couponsOverview,
- ]);
-
- return {
- catalogs,
- isLoading,
- };
-};
diff --git a/src/components/enterprise-subsidy-requests/data/index.js b/src/components/enterprise-subsidy-requests/data/index.js
index db0bacf0dd..f78beabc33 100644
--- a/src/components/enterprise-subsidy-requests/data/index.js
+++ b/src/components/enterprise-subsidy-requests/data/index.js
@@ -1,2 +1 @@
-export * from './hooks';
export * from './service';
diff --git a/src/components/enterprise-subsidy-requests/data/service.js b/src/components/enterprise-subsidy-requests/data/service.js
index d8114e11bd..41698b176c 100644
--- a/src/components/enterprise-subsidy-requests/data/service.js
+++ b/src/components/enterprise-subsidy-requests/data/service.js
@@ -1,43 +1,6 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform/config';
-import { SUBSIDY_REQUEST_STATE } from '../../../constants';
-
-export function fetchSubsidyRequestConfiguration(enterpriseUUID) {
- const url = `${getConfig().ENTERPRISE_ACCESS_BASE_URL}/api/v1/customer-configurations/${enterpriseUUID}/`;
- return getAuthenticatedHttpClient().get(url);
-}
-
-export function fetchLicenseRequests({
- enterpriseUUID,
- userEmail,
- state = SUBSIDY_REQUEST_STATE.REQUESTED,
-}) {
- const queryParams = new URLSearchParams({
- enterprise_customer_uuid: enterpriseUUID,
- user__email: userEmail,
- state,
- });
- const config = getConfig();
- const url = `${config.ENTERPRISE_ACCESS_BASE_URL}/api/v1/license-requests/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-}
-
-export function fetchCouponCodeRequests({
- enterpriseUUID,
- userEmail,
- state = SUBSIDY_REQUEST_STATE.REQUESTED,
-}) {
- const queryParams = new URLSearchParams({
- enterprise_customer_uuid: enterpriseUUID,
- user__email: userEmail,
- state,
- });
- const config = getConfig();
- const url = `${config.ENTERPRISE_ACCESS_BASE_URL}/api/v1/coupon-code-requests/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-}
-
export function postCouponCodeRequest(enterpriseUUID, courseID) {
const options = {
enterprise_customer_uuid: enterpriseUUID,
diff --git a/src/components/enterprise-subsidy-requests/data/tests/hooks.test.jsx b/src/components/enterprise-subsidy-requests/data/tests/hooks.test.jsx
deleted file mode 100644
index 99328955d4..0000000000
--- a/src/components/enterprise-subsidy-requests/data/tests/hooks.test.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-import { renderHook } from '@testing-library/react-hooks';
-import * as logger from '@edx/frontend-platform/logging';
-import { AppContext } from '@edx/frontend-platform/react';
-import {
- useCatalogsForSubsidyRequests,
- useSubsidyRequestConfiguration,
- useSubsidyRequests,
-} from '../hooks';
-import * as service from '../service';
-import { SUBSIDY_REQUEST_STATE, SUBSIDY_TYPE } from '../../../../constants';
-
-const mockEmail = 'edx@example.com';
-const mockEnterpriseUUID = 'enterprise-uuid';
-
-jest.mock('../service');
-jest.mock('../../../enterprise-user-subsidy/coupons/data/service');
-
-const wrapper = ({ children }) => (
-
- {children}
-
-);
-
-describe('useSubsidyRequestConfiguration', () => {
- afterEach(() => jest.clearAllMocks());
-
- it('should fetch subsidy request configuration for the given enterprise', async () => {
- service.fetchSubsidyRequestConfiguration.mockResolvedValue({
- data: {
- subsidy_requests_enabled: true,
- subsidy_type: SUBSIDY_TYPE.COUPON,
- },
- });
- const { result, waitForNextUpdate } = renderHook(() => useSubsidyRequestConfiguration(mockEnterpriseUUID));
- await waitForNextUpdate();
- expect(result.current.subsidyRequestConfiguration).toEqual({
- subsidyRequestsEnabled: true,
- subsidyType: SUBSIDY_TYPE.COUPON,
- });
- });
-
- it('sets subsidyRequestConfiguration to null if customer configuration does not exist', async () => {
- const error = new Error('Something went wrong.');
- error.customAttributes = {
- httpErrorStatus: 404,
- };
- service.fetchSubsidyRequestConfiguration.mockRejectedValue(error);
- const { result, waitForNextUpdate } = renderHook(() => useSubsidyRequestConfiguration(mockEnterpriseUUID));
- await waitForNextUpdate();
- expect(result.current.subsidyRequestConfiguration).toEqual(null);
- });
-
- it('handles any errors', async () => {
- const error = new Error('Something went wrong.');
- service.fetchSubsidyRequestConfiguration.mockRejectedValue(error);
- const { result, waitForNextUpdate } = renderHook(() => useSubsidyRequestConfiguration(mockEnterpriseUUID));
- await waitForNextUpdate();
- expect(result.current.subsidyRequestConfiguration).toEqual(undefined);
- expect(logger.logError).toHaveBeenCalledWith(error);
- });
-});
-
-describe('useSubsidyRequests', () => {
- afterEach(() => jest.clearAllMocks());
-
- it.each([null, {
- subsidyRequestsEnabled: false,
- }, {
- subsidyRequestsEnabled: true,
- subsidyType: undefined,
- }])('should not do anything if subsidy requests are disabled', async (subsidyRequestsConfiguration) => {
- renderHook(() => useSubsidyRequests(subsidyRequestsConfiguration), { wrapper });
- expect(service.fetchCouponCodeRequests).not.toHaveBeenCalled();
- expect(service.fetchLicenseRequests).not.toHaveBeenCalled();
- });
-
- it('should fetch coupon code requests', async () => {
- service.fetchCouponCodeRequests.mockResolvedValue({
- data: {
- results: [
- {
- lms_user_id: 1,
- enterprise_customer_uuid: mockEnterpriseUUID,
- },
- ],
- },
- });
-
- const args = {
- subsidyRequestsEnabled: true,
- subsidyType: SUBSIDY_TYPE.COUPON,
- enterpriseCustomerUuid: mockEnterpriseUUID,
- };
- const { result, waitForNextUpdate } = renderHook(() => useSubsidyRequests(args), { wrapper });
- await waitForNextUpdate();
- expect(service.fetchCouponCodeRequests).toHaveBeenCalledWith({
- enterpriseUUID: mockEnterpriseUUID,
- userEmail: mockEmail,
- state: SUBSIDY_REQUEST_STATE.REQUESTED,
- });
- expect(service.fetchLicenseRequests).not.toHaveBeenCalled();
-
- expect(result.current.couponCodeRequests).toEqual(
- [
- {
- lmsUserId: 1,
- enterpriseCustomerUuid: mockEnterpriseUUID,
- },
- ],
- );
- });
-
- it('should fetch coupon code requests', async () => {
- service.fetchCouponCodeRequests.mockResolvedValue({
- data: {
- results: [
- {
- lms_user_id: 1,
- enterprise_customer_uuid: mockEnterpriseUUID,
- },
- ],
- },
- });
-
- const args = {
- subsidyRequestsEnabled: true,
- subsidyType: SUBSIDY_TYPE.COUPON,
- enterpriseCustomerUuid: mockEnterpriseUUID,
- };
- const { result, waitForNextUpdate } = renderHook(() => useSubsidyRequests(args), { wrapper });
- await waitForNextUpdate();
-
- expect(service.fetchCouponCodeRequests).toHaveBeenCalledWith({
- enterpriseUUID: mockEnterpriseUUID,
- userEmail: mockEmail,
- state: SUBSIDY_REQUEST_STATE.REQUESTED,
- });
- expect(service.fetchLicenseRequests).not.toHaveBeenCalled();
-
- expect(result.current.couponCodeRequests).toEqual(
- [
- {
- lmsUserId: 1,
- enterpriseCustomerUuid: mockEnterpriseUUID,
- },
- ],
- );
-
- expect(result.current.licenseRequests).toEqual([]);
- });
-
- it('should fetch license requests', async () => {
- service.fetchLicenseRequests.mockResolvedValue({
- data: {
- results: [
- {
- lms_user_id: 1,
- enterprise_customer_uuid: mockEnterpriseUUID,
- },
- ],
- },
- });
-
- const args = {
- subsidyRequestsEnabled: true,
- subsidyType: SUBSIDY_TYPE.LICENSE,
- enterpriseCustomerUuid: mockEnterpriseUUID,
- };
- const { result, waitForNextUpdate } = renderHook(() => useSubsidyRequests(args), { wrapper });
- await waitForNextUpdate();
-
- expect(service.fetchCouponCodeRequests).not.toHaveBeenCalled();
- expect(service.fetchLicenseRequests).toHaveBeenCalledWith({
- enterpriseUUID: mockEnterpriseUUID,
- userEmail: mockEmail,
- state: SUBSIDY_REQUEST_STATE.REQUESTED,
- });
-
- expect(result.current.licenseRequests).toEqual(
- [
- {
- lmsUserId: 1,
- enterpriseCustomerUuid: mockEnterpriseUUID,
- },
- ],
- );
-
- expect(result.current.couponCodeRequests).toEqual([]);
- });
-});
-
-describe('useCatalogsForSubsidyRequests', () => {
- afterEach(() => jest.clearAllMocks());
-
- it('sets isLoading to false if there is no subsidy request configuration', () => {
- const args = {
- subsidyRequestConfiguration: null,
- isLoadingSubsidyRequestConfiguration: false,
- customerAgreementConfig: null,
- };
- const { result } = renderHook(() => useCatalogsForSubsidyRequests(args));
-
- expect(result.current.isLoading).toBe(false);
- });
-
- it('fetches coupons overview and sets catalogs correctly if configured subsidy type is coupons', () => {
- const mockCatalogUUIDs = ['uuid1', 'uuid2'];
- const subsidyRequestConfiguration = {
- subsidyType: SUBSIDY_TYPE.COUPON,
- subsidyRequestsEnabled: true,
- };
- const args = {
- subsidyRequestConfiguration,
- isLoadingSubsidyRequestConfiguration: false,
- customerAgreementConfig: null,
- couponsOverview: mockCatalogUUIDs.map(uuid => ({
- enterpriseCatalogUuid: uuid,
- available: true,
- })),
- };
- const { result } = renderHook(() => useCatalogsForSubsidyRequests(args));
-
- expect(result.current.isLoading).toBe(false);
- expect([...result.current.catalogs]).toEqual(mockCatalogUUIDs);
- });
-
- it('does nothing if subsidy requests are not enabled', async () => {
- const args = {
- subsidyRequestConfiguration: {
- subsidyType: SUBSIDY_TYPE.COUPON,
- subsidyRequestsEnabled: false,
- },
- isLoadingSubsidyRequestConfiguration: false,
- customerAgreementConfig: null,
- };
- const { result } = renderHook(() => useCatalogsForSubsidyRequests(args));
-
- expect(result.current.isLoading).toBe(false);
- expect([...result.current.catalogs]).toEqual([]);
- });
-
- it('sets catalogs from subscription plans correctly if configured subsidy type is licenses', async () => {
- const mockCatalogUUIDs = ['uuid1', 'uuid2'];
- const subsidyRequestConfiguration = {
- subsidyType: SUBSIDY_TYPE.LICENSE,
- subsidyRequestsEnabled: true,
- };
- const args = {
- subsidyRequestConfiguration,
- isLoadingSubsidyRequestConfiguration: false,
- customerAgreementConfig: {
- subscriptions: mockCatalogUUIDs.map(uuid => ({
- enterpriseCatalogUuid: uuid,
- daysUntilExpirationIncludingRenewals: 123,
- })),
- },
- };
- const { result } = renderHook(() => useCatalogsForSubsidyRequests(args));
-
- expect(result.current.isLoading).toBe(false);
- expect([...result.current.catalogs]).toEqual(mockCatalogUUIDs);
- });
-});
diff --git a/src/components/enterprise-subsidy-requests/data/tests/service.test.js b/src/components/enterprise-subsidy-requests/data/tests/service.test.js
index b8352001fd..9c31e44bcb 100644
--- a/src/components/enterprise-subsidy-requests/data/tests/service.test.js
+++ b/src/components/enterprise-subsidy-requests/data/tests/service.test.js
@@ -4,68 +4,55 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { mergeConfig } from '@edx/frontend-platform/config';
import {
- fetchSubsidyRequestConfiguration,
- fetchCouponCodeRequests,
- fetchLicenseRequests,
+ postLicenseRequest,
+ postCouponCodeRequest,
} from '../service';
-import { SUBSIDY_REQUEST_STATE } from '../../../../constants';
-
jest.mock('@edx/frontend-platform/auth');
const axiosMock = new MockAdapter(axios);
getAuthenticatedHttpClient.mockReturnValue(axios);
axiosMock.onAny().reply(200);
axios.get = jest.fn();
+axios.post = jest.fn();
const enterpriseAccessBaseUrl = `${process.env.ENTERPRISE_ACCESS_BASE_URL}`;
const mockEnterpriseUUID = 'test-enterprise-id';
-const mockEmail = 'edx@example.com';
+const mockCourseId = 'test-course-id';
-describe('fetchSubsidyRequestConfiguration', () => {
+describe('postCouponCodeRequest', () => {
beforeEach(() => {
+ jest.clearAllMocks();
mergeConfig({
ENTERPRISE_ACCESS_BASE_URL: enterpriseAccessBaseUrl,
});
});
- it('fetches subsidy request configuration for the given enterprise', () => {
- fetchSubsidyRequestConfiguration(mockEnterpriseUUID);
- expect(axios.get).toBeCalledWith(`${enterpriseAccessBaseUrl}/api/v1/customer-configurations/${mockEnterpriseUUID}/`);
+ it('posts coupon code request for the given enterprise and course', () => {
+ postCouponCodeRequest(mockEnterpriseUUID, mockCourseId);
+ const options = {
+ enterprise_customer_uuid: mockEnterpriseUUID,
+ course_id: mockCourseId,
+ };
+ expect(axios.post).toHaveBeenCalledTimes(1);
+ expect(axios.post).toHaveBeenCalledWith(`${enterpriseAccessBaseUrl}/api/v1/coupon-code-requests/`, options);
});
});
-describe('fetchLicenseRequests', () => {
- it('fetches license requests', () => {
- fetchLicenseRequests({
- enterpriseUUID: mockEnterpriseUUID,
- userEmail: mockEmail,
- state: SUBSIDY_REQUEST_STATE.DECLINED,
- });
- const queryParams = new URLSearchParams({
- enterprise_customer_uuid: mockEnterpriseUUID,
- user__email: mockEmail,
- state: SUBSIDY_REQUEST_STATE.DECLINED,
+describe('postLicenseRequest', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mergeConfig({
+ ENTERPRISE_ACCESS_BASE_URL: enterpriseAccessBaseUrl,
});
- expect(axios.get).toBeCalledWith(
- `${enterpriseAccessBaseUrl}/api/v1/license-requests/?${queryParams.toString()}`,
- );
});
-});
-describe('fetchCouponCodeRequests', () => {
- it('fetches coupon code requests', () => {
- fetchCouponCodeRequests({
- enterpriseUUID: mockEnterpriseUUID,
- userEmail: mockEmail,
- state: SUBSIDY_REQUEST_STATE.REQUESTED,
- });
- const queryParams = new URLSearchParams({
+ it('posts license request for the given enterprise and course', () => {
+ postLicenseRequest(mockEnterpriseUUID, mockCourseId);
+ const options = {
enterprise_customer_uuid: mockEnterpriseUUID,
- user__email: mockEmail,
- state: SUBSIDY_REQUEST_STATE.REQUESTED,
- });
- expect(axios.get).toBeCalledWith(
- `${enterpriseAccessBaseUrl}/api/v1/coupon-code-requests/?${queryParams.toString()}`,
- );
+ course_id: mockCourseId,
+ };
+ expect(axios.post).toHaveBeenCalledTimes(1);
+ expect(axios.post).toHaveBeenCalledWith(`${enterpriseAccessBaseUrl}/api/v1/license-requests/`, options);
});
});
diff --git a/src/components/enterprise-user-subsidy/UserSubsidy.jsx b/src/components/enterprise-user-subsidy/UserSubsidy.jsx
deleted file mode 100644
index 401d240f94..0000000000
--- a/src/components/enterprise-user-subsidy/UserSubsidy.jsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import React, {
- createContext, useContext, useMemo,
-} from 'react';
-import PropTypes from 'prop-types';
-import { AppContext } from '@edx/frontend-platform/react';
-import { Container } from '@openedx/paragon';
-
-import { LoadingSpinner } from '../loading-spinner';
-import {
- useCouponCodes,
- useSubscriptions,
- useRedeemableLearnerCreditPolicies,
-} from './data/hooks';
-import { useEnterpriseOffers } from './enterprise-offers/data/hooks';
-import { LOADING_SCREEN_READER_TEXT } from './data/constants';
-import { useEnterpriseCustomer } from '../app/data';
-
-export const UserSubsidyContext = createContext();
-
-const UserSubsidy = ({ children }) => {
- const { authenticatedUser } = useContext(AppContext);
- const { userId } = authenticatedUser;
- const { data: enterpriseCustomer } = useEnterpriseCustomer();
-
- // Subscriptions
- const {
- customerAgreementConfig,
- subscriptionPlan,
- subscriptionLicense,
- isLoading: isLoadingSubscriptions,
- showExpirationNotifications,
- } = useSubscriptions({ enterpriseCustomer, authenticatedUser });
-
- // Subsidy Access Policies
- const {
- data: redeemableLearnerCreditPolicies,
- isLoading: isLoadingRedeemablePolicies,
- } = useRedeemableLearnerCreditPolicies(enterpriseCustomer.uuid, userId);
-
- // Coupon Codes
- const [couponCodes, isLoadingCouponCodes] = useCouponCodes(enterpriseCustomer.uuid);
-
- // Enterprise Offers
- const {
- enterpriseOffers,
- currentEnterpriseOffers,
- hasCurrentEnterpriseOffers,
- canEnrollWithEnterpriseOffers,
- hasLowEnterpriseOffersBalance,
- hasNoEnterpriseOffersBalance,
- isLoading: isLoadingEnterpriseOffers,
- } = useEnterpriseOffers({
- enterpriseId: enterpriseCustomer.uuid,
- enableLearnerPortalOffers: enterpriseCustomer.enableLearnerPortalOffers,
- customerAgreementConfig,
- });
-
- const isLoadingSubsidies = useMemo(
- () => {
- const loadingStates = [
- isLoadingSubscriptions,
- isLoadingCouponCodes,
- isLoadingEnterpriseOffers,
- isLoadingRedeemablePolicies,
- ];
- return loadingStates.includes(true);
- },
- [isLoadingSubscriptions, isLoadingCouponCodes, isLoadingEnterpriseOffers, isLoadingRedeemablePolicies],
- );
-
- const contextValue = useMemo(
- () => {
- if (isLoadingSubsidies) {
- return {};
- }
- return {
- subscriptionLicense,
- subscriptionPlan,
- couponCodes,
- enterpriseOffers,
- currentEnterpriseOffers,
- hasCurrentEnterpriseOffers,
- canEnrollWithEnterpriseOffers,
- hasLowEnterpriseOffersBalance,
- hasNoEnterpriseOffersBalance,
- showExpirationNotifications,
- customerAgreementConfig,
- redeemableLearnerCreditPolicies,
- };
- },
- [
- isLoadingSubsidies,
- subscriptionLicense,
- subscriptionPlan,
- couponCodes,
- enterpriseOffers,
- currentEnterpriseOffers,
- hasCurrentEnterpriseOffers,
- canEnrollWithEnterpriseOffers,
- hasLowEnterpriseOffersBalance,
- hasNoEnterpriseOffersBalance,
- showExpirationNotifications,
- customerAgreementConfig,
- redeemableLearnerCreditPolicies,
- ],
- );
-
- if (isLoadingSubsidies) {
- return (
-
-
-
- );
- }
- return (
- <>
- {/* Render the children so the rest of the page shows */}
-
- {children}
-
- >
- );
-};
-
-UserSubsidy.propTypes = {
- children: PropTypes.node.isRequired,
-};
-
-export default UserSubsidy;
diff --git a/src/components/enterprise-user-subsidy/coupons/data/__mocks__/OfferAssignments.json b/src/components/enterprise-user-subsidy/coupons/data/__mocks__/OfferAssignments.json
deleted file mode 100644
index 5a7cf2843c..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/__mocks__/OfferAssignments.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "count": 4,
- "num_pages": 1,
- "current_page": 1,
- "results": [
- {
- "usage_type": "Percentage",
- "benefit_value": 100,
- "redemptions_remaining": 4,
- "code": "EPKAUH7MZ7WALXZV",
- "catalog": "9014df44-e8eb-41c0-ab39-fb9a508ac716",
- "coupon_start_date": "2019-07-14T00:00:00Z",
- "coupon_end_date": "2019-07-31T00:00:00Z"
- },
- {
- "usage_type": "Percentage",
- "benefit_value": 100,
- "redemptions_remaining": 1,
- "code": "XUFEXU3MXSWBYMLY",
- "catalog": "97207126-7f9b-43c7-ab6d-c77260069126",
- "coupon_start_date": "2019-04-30T00:00:00Z",
- "coupon_end_date": "2020-04-30T00:00:00Z"
- },
- {
- "usage_type": "Percentage",
- "benefit_value": 50,
- "redemptions_remaining": 1,
- "code": "FUT4VBONG3BA7R76",
- "catalog": "88fce376-946e-419e-967b-ea6e68a44d23",
- "coupon_start_date": "2019-07-15T00:00:00Z",
- "coupon_end_date": "2019-07-30T00:00:00Z"
- },
- {
- "usage_type": "Absolute",
- "benefit_value": 10,
- "redemptions_remaining": 1,
- "code": "2YL4L3QW32SZGVCY",
- "catalog": "88fce376-946e-419e-967b-ea6e68a44d23",
- "coupon_start_date": "2018-11-01T00:00:00Z",
- "coupon_end_date": "2019-11-30T00:00:00Z"
- }
- ],
- "next": null,
- "start": 0,
- "previous": null
-}
diff --git a/src/components/enterprise-user-subsidy/coupons/data/actions.js b/src/components/enterprise-user-subsidy/coupons/data/actions.js
deleted file mode 100644
index 02906c9a5a..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/actions.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { camelCaseObject } from '@edx/frontend-platform';
-
-import {
- FETCH_COUPON_CODES_REQUEST,
- FETCH_COUPON_CODES_SUCCESS,
- FETCH_COUPON_CODES_FAILURE,
-} from './constants';
-
-import findCouponCodeRedemptionCount from './utils';
-import * as service from './service';
-import { hasValidStartExpirationDates } from '../../../../utils/common';
-
-const fetchCouponCodesRequest = () => ({
- type: FETCH_COUPON_CODES_REQUEST,
-});
-
-const fetchCouponCodesSuccess = data => ({
- type: FETCH_COUPON_CODES_SUCCESS,
- payload: {
- couponCodes: data.results,
- couponCodesCount: findCouponCodeRedemptionCount(data.results),
- },
-});
-
-const fetchCouponCodesFailure = error => ({
- type: FETCH_COUPON_CODES_FAILURE,
- payload: {
- error,
- },
-});
-
-export const fetchCouponCodeAssignments = (queryOptions, dispatch) => {
- dispatch(fetchCouponCodesRequest());
-
- return service.fetchCouponCodeAssignments(queryOptions)
- .then((response) => {
- const formattedResponse = camelCaseObject(response.data);
- const transformedResults = formattedResponse.results.map((couponCode) => ({
- ...couponCode,
- available: hasValidStartExpirationDates({
- startDate: couponCode.couponStartDate,
- endDate: couponCode.couponEndDate,
- }),
- }));
- dispatch(fetchCouponCodesSuccess(camelCaseObject({
- ...formattedResponse,
- results: transformedResults,
- })));
- })
- .catch((error) => {
- dispatch(fetchCouponCodesFailure(error));
- });
-};
diff --git a/src/components/enterprise-user-subsidy/coupons/data/constants.js b/src/components/enterprise-user-subsidy/coupons/data/constants.js
deleted file mode 100644
index 368a02a64c..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const FETCH_COUPON_CODES_REQUEST = 'FETCH_COUPON_CODES_REQUEST';
-export const FETCH_COUPON_CODES_SUCCESS = 'FETCH_COUPON_CODES_SUCCESS';
-export const FETCH_COUPON_CODES_FAILURE = 'FETCH_COUPON_CODES_FAILURE';
diff --git a/src/components/enterprise-user-subsidy/coupons/data/index.js b/src/components/enterprise-user-subsidy/coupons/data/index.js
new file mode 100644
index 0000000000..04bca77e0d
--- /dev/null
+++ b/src/components/enterprise-user-subsidy/coupons/data/index.js
@@ -0,0 +1 @@
+export * from './utils';
diff --git a/src/components/enterprise-user-subsidy/coupons/data/reducer.js b/src/components/enterprise-user-subsidy/coupons/data/reducer.js
deleted file mode 100644
index 7517ab4d2d..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/reducer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import {
- FETCH_COUPON_CODES_REQUEST,
- FETCH_COUPON_CODES_SUCCESS,
- FETCH_COUPON_CODES_FAILURE,
-} from './constants';
-
-export const initialCouponCodesState = {
- loading: false,
- couponCodes: [],
- couponCodesCount: 0,
- couponsOverview: [],
- error: null,
-};
-
-const couponCodesReducer = (state, action) => {
- switch (action.type) {
- case FETCH_COUPON_CODES_REQUEST:
- return {
- ...state,
- loading: true,
- error: null,
- };
- case FETCH_COUPON_CODES_SUCCESS:
- return {
- ...state,
- loading: false,
- error: null,
- couponCodes: action.payload.couponCodes,
- couponCodesCount: action.payload.couponCodesCount,
- };
- case FETCH_COUPON_CODES_FAILURE:
- return {
- ...state,
- loading: false,
- error: action.payload.error,
- };
- default:
- return state;
- }
-};
-
-export default couponCodesReducer;
diff --git a/src/components/enterprise-user-subsidy/coupons/data/service.js b/src/components/enterprise-user-subsidy/coupons/data/service.js
deleted file mode 100644
index e419cfb89b..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/service.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { getConfig } from '@edx/frontend-platform/config';
-
-const fetchCouponCodeAssignments = (options) => {
- const queryParams = new URLSearchParams(options);
- const config = getConfig();
- const url = `${config.ECOMMERCE_BASE_URL}/api/v2/enterprise/offer_assignment_summary/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-};
-
-const fetchCouponsOverview = ({ enterpriseId, options = {} }) => {
- const queryParams = new URLSearchParams({
- page: 1,
- page_size: 100,
- ...options,
- });
- const config = getConfig();
- const url = `${config.ECOMMERCE_BASE_URL}/api/v2/enterprise/coupons/${enterpriseId}/overview/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-};
-
-export { fetchCouponCodeAssignments, fetchCouponsOverview };
diff --git a/src/components/enterprise-user-subsidy/coupons/data/tests/actions.test.js b/src/components/enterprise-user-subsidy/coupons/data/tests/actions.test.js
deleted file mode 100644
index 59b43a679a..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/tests/actions.test.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import {
- FETCH_COUPON_CODES_REQUEST,
- FETCH_COUPON_CODES_SUCCESS,
- FETCH_COUPON_CODES_FAILURE,
-} from '../constants';
-import {
- fetchCouponCodeAssignments,
-} from '../actions';
-import * as service from '../service';
-import { hasValidStartExpirationDates } from '../../../../../utils/common';
-
-jest.mock('../service');
-jest.mock('../../../../../utils/common', () => ({
- ...jest.requireActual('../../../../../utils/common'),
- hasValidStartExpirationDates: jest.fn(),
-}));
-
-describe('fetchCouponCodeAssignments action', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it.each([
- { couponStartDate: '2020-10-20', couponEndDate: '2021-10-20', expectedAvailability: true },
- { couponStartDate: '2022-06-12', couponEndDate: '2023-06-12', expectedAvailability: false },
- ])('fetch coupon codes success (%s)', ({
- couponStartDate,
- couponEndDate,
- expectedAvailability,
- }) => {
- hasValidStartExpirationDates.mockReturnValue(expectedAvailability);
- const expectedAction = [
- { type: FETCH_COUPON_CODES_REQUEST },
- {
- type: FETCH_COUPON_CODES_SUCCESS,
- payload: {
- couponCodes: [{
- fooBar: 'foo',
- redemptionsRemaining: 2,
- available: expectedAvailability,
- couponStartDate,
- couponEndDate,
- }],
- couponCodesCount: 2,
- },
- },
- ];
-
- service.fetchCouponCodeAssignments.mockImplementation((
- () => Promise.resolve({
- data: {
- results: [{
- foo_bar: 'foo',
- redemptions_remaining: 2,
- coupon_start_date: couponStartDate,
- coupon_end_date: couponEndDate,
- }],
- count: 2,
- },
- })
- ));
- const dispatchSpy = jest.fn();
- return fetchCouponCodeAssignments(null, dispatchSpy)
- .then(() => {
- expect(dispatchSpy).toHaveBeenNthCalledWith(1, expectedAction[0]);
- expect(dispatchSpy).toHaveBeenNthCalledWith(2, expectedAction[1]);
- });
- });
-
- it('fetch coupon codes failure', () => {
- const expectedAction = [
- { type: FETCH_COUPON_CODES_REQUEST },
- {
- type: FETCH_COUPON_CODES_FAILURE,
- payload: {
- error: Error,
- },
- },
- ];
-
- service.fetchCouponCodeAssignments.mockImplementation((
- () => Promise.reject(Error)
- ));
- const dispatchSpy = jest.fn();
- return fetchCouponCodeAssignments(null, dispatchSpy)
- .then(() => {
- expect(dispatchSpy).toHaveBeenNthCalledWith(1, expectedAction[0]);
- expect(dispatchSpy).toHaveBeenNthCalledWith(2, expectedAction[1]);
- });
- });
-});
diff --git a/src/components/enterprise-user-subsidy/coupons/data/tests/reducer.test.js b/src/components/enterprise-user-subsidy/coupons/data/tests/reducer.test.js
deleted file mode 100644
index 114fbc9429..0000000000
--- a/src/components/enterprise-user-subsidy/coupons/data/tests/reducer.test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import couponCodesReducer from '../reducer';
-import {
- FETCH_COUPON_CODES_REQUEST,
- FETCH_COUPON_CODES_SUCCESS,
- FETCH_COUPON_CODES_FAILURE,
-} from '../constants';
-
-const initialState = {
- loading: false,
- couponCodes: [],
- couponCodesCount: 0,
- error: null,
-};
-
-describe('couponCodesReducer', () => {
- it('should return the initial state', () => {
- expect(couponCodesReducer(initialState, {})).toEqual(initialState);
- });
-
- it('should handle FETCH_COUPON_CODES_REQUEST', () => {
- const expected = {
- ...initialState,
- loading: true,
- error: null,
- };
- expect(couponCodesReducer(initialState, {
- type: FETCH_COUPON_CODES_REQUEST,
- })).toEqual(expected);
- });
-
- it('should handle FETCH_COUPON_CODES_SUCCESS', () => {
- const expected = {
- loading: false,
- couponCodes: ['some data'],
- couponCodesCount: 4,
- error: null,
- };
- expect(couponCodesReducer(initialState, {
- type: FETCH_COUPON_CODES_SUCCESS,
- payload: {
- couponCodes: ['some data'],
- couponCodesCount: 4,
- },
- })).toEqual(expected);
- });
-
- it('should handle FETCH_COUPON_CODES_FAILURE', () => {
- const expected = {
- ...initialState,
- loading: false,
- error: Error,
- };
- expect(couponCodesReducer(initialState, {
- type: FETCH_COUPON_CODES_FAILURE,
- payload: {
- error: Error,
- },
- })).toEqual(expected);
- });
-});
diff --git a/src/components/enterprise-user-subsidy/coupons/data/tests/utils.test.js b/src/components/enterprise-user-subsidy/coupons/data/tests/utils.test.js
index 400f50dab7..3e490fe39f 100644
--- a/src/components/enterprise-user-subsidy/coupons/data/tests/utils.test.js
+++ b/src/components/enterprise-user-subsidy/coupons/data/tests/utils.test.js
@@ -1,4 +1,4 @@
-import findCouponCodeRedemptionCount from '../utils';
+import { findCouponCodeRedemptionCount } from '../utils';
describe('find coupon code redemption count function', () => {
it('should not fail and return 0 if there are no remaining redemptions', () => {
diff --git a/src/components/enterprise-user-subsidy/coupons/data/utils.jsx b/src/components/enterprise-user-subsidy/coupons/data/utils.jsx
index 8cf2ad00ec..c3c6a320fa 100644
--- a/src/components/enterprise-user-subsidy/coupons/data/utils.jsx
+++ b/src/components/enterprise-user-subsidy/coupons/data/utils.jsx
@@ -1,4 +1,4 @@
-export default function findCouponCodeRedemptionCount(couponCodes) {
+export function findCouponCodeRedemptionCount(couponCodes) {
let totalRedemptionsRemaining = 0;
couponCodes.forEach((couponCode) => {
totalRedemptionsRemaining += couponCode.redemptionsRemaining;
diff --git a/src/components/enterprise-user-subsidy/coupons/index.js b/src/components/enterprise-user-subsidy/coupons/index.js
index de13cd12cf..3707679222 100644
--- a/src/components/enterprise-user-subsidy/coupons/index.js
+++ b/src/components/enterprise-user-subsidy/coupons/index.js
@@ -1,2 +1 @@
-export { fetchCouponCodeAssignments } from './data/actions';
-export { default as reducer } from './data/reducer';
+export * from './data';
diff --git a/src/components/enterprise-user-subsidy/data/constants.js b/src/components/enterprise-user-subsidy/data/constants.js
index b03a3b2f99..f3541c6efb 100644
--- a/src/components/enterprise-user-subsidy/data/constants.js
+++ b/src/components/enterprise-user-subsidy/data/constants.js
@@ -4,35 +4,3 @@ export const LICENSE_STATUS = {
REVOKED: 'revoked',
UNASSIGNED: 'unassigned',
};
-
-export const LOADING_SCREEN_READER_TEXT = 'loading your edX benefits from your organization';
-
-export const enterpriseUserSubsidyQueryKeys = {
- // Namespace for all user subsidy query keys
- all: ['user-subsidy'],
- policy: () => [
- ...enterpriseUserSubsidyQueryKeys.all,
- 'policy',
- ],
- // Used with query against `can-redeem` API endpoint
- coursePolicyRedeemability: ({
- enterpriseId, lmsUserId, courseRunKeys, activeCourseRunKey,
- }) => [
- ...enterpriseUserSubsidyQueryKeys.policy(),
- enterpriseId,
- 'can-redeem',
- { lmsUserId, courseRunKeys, activeCourseRunKey },
- ],
- // Used with query to fetch user's redeemable subsidy access policies
- redeemablePolicies: (enterpriseId, userId) => [
- ...enterpriseUserSubsidyQueryKeys.policy(),
- 'redeemable-policies',
- enterpriseId,
- userId],
- // Used with query for polling pending policy transactions after initial policy redemption
- pollPendingPolicyTransaction: (transaction) => [
- ...enterpriseUserSubsidyQueryKeys.policy(),
- 'transactions',
- transaction,
- ],
-};
diff --git a/src/components/enterprise-user-subsidy/data/hooks/hooks.js b/src/components/enterprise-user-subsidy/data/hooks/hooks.js
deleted file mode 100644
index f71d13c44d..0000000000
--- a/src/components/enterprise-user-subsidy/data/hooks/hooks.js
+++ /dev/null
@@ -1,204 +0,0 @@
-import {
- useEffect, useMemo, useReducer, useState,
-} from 'react';
-import { logError } from '@edx/frontend-platform/logging';
-import { camelCaseObject } from '@edx/frontend-platform/utils';
-import { useQuery } from '@tanstack/react-query';
-
-import { fetchCouponCodeAssignments } from '../../coupons';
-import couponCodesReducer, { initialCouponCodesState } from '../../coupons/data/reducer';
-
-import { enterpriseUserSubsidyQueryKeys, LICENSE_STATUS } from '../constants';
-import {
- fetchCustomerAgreementData,
- fetchRedeemableLearnerCreditPolicies,
- fetchSubscriptionLicensesForUser,
-} from '../service';
-import { features } from '../../../../config';
-import { fetchCouponsOverview } from '../../coupons/data/service';
-import { getAssignmentsByState, transformRedeemablePoliciesData } from '../utils';
-
-/**
- * Attempts to fetch any existing licenses associated with the authenticated user and the
- * specified enterprise customer. Priority is given to activated licenses, then assigned
- * licenses.
- *
- * @param {string} enterpriseId The UUID of the enterprise customer
- * @returns An object representing a user's subscription license or null if no license was found.
- */
-const fetchExistingUserLicense = async (enterpriseId) => {
- try {
- const response = await fetchSubscriptionLicensesForUser(enterpriseId);
- const { results } = camelCaseObject(response.data);
- /**
- * Ordering of these status keys (i.e., activated, assigned, revoked) is important as the first
- * license found when iterating through each status key in this order will be selected as the
- * applicable license for use by the rest of the application.
- *
- * Example: an activated license will be chosen as the applicable license because activated licenses
- * come first in ``licensesByStatus`` even if the user also has a revoked license.
- */
- const licensesByStatus = {
- [LICENSE_STATUS.ACTIVATED]: [],
- [LICENSE_STATUS.ASSIGNED]: [],
- [LICENSE_STATUS.REVOKED]: [],
- };
- results.forEach((item) => {
- licensesByStatus[item.status].push(item);
- });
- const applicableLicense = Object.values(licensesByStatus).flat()[0];
- return applicableLicense;
- } catch (error) {
- logError(error);
- return null;
- }
-};
-
-/**
- * Retrieves a license for the authenticated user, if applicable. First attempts to find any existing licenses
- * for the user. If a license is found, the app uses it.
- *
- * @param {object} args
- * @param {object} args.enterpriseCustomer The enterprise customer config
- * @param {object} args.customerAgreementConfig The customer agreement config associated with the enterprise
- * @param {boolean} args.isLoadingCustomerAgreementConfig Whether the customer agreement is still resolving
- * @param {object} args.user The authenticated user
- * @returns Object containing a user license, if applicable, whether the license data is still resolving, and a callback
- * to activate the user license.
- */
-export function useSubscriptionLicense({
- enterpriseCustomer,
- customerAgreementConfig,
- isLoadingCustomerAgreementConfig,
- user,
-}) {
- const [license, setLicense] = useState();
- const [isLoading, setIsLoading] = useState(true);
-
- const {
- enterpriseId,
- enterpriseIdentityProvider,
- } = useMemo(() => ({
- enterpriseId: enterpriseCustomer.uuid,
- enterpriseIdentityProvider: enterpriseCustomer.identityProvider,
- }), [enterpriseCustomer]);
-
- useEffect(() => {
- async function retrieveUserLicense() {
- const result = await fetchExistingUserLicense(enterpriseId);
- return result;
- }
-
- if (!isLoadingCustomerAgreementConfig) {
- setIsLoading(true);
- retrieveUserLicense().then((userLicense) => {
- const subscriptionPlan = customerAgreementConfig?.subscriptions?.find(
- subscription => subscription.uuid === userLicense?.subscriptionPlanUuid,
- );
-
- if (userLicense) {
- setLicense({
- ...userLicense,
- subscriptionPlan,
- });
- } else {
- setLicense(null);
- }
-
- setIsLoading(false);
- });
- }
- }, [customerAgreementConfig, enterpriseId, enterpriseIdentityProvider, isLoadingCustomerAgreementConfig, user]);
-
- return { license, isLoading };
-}
-
-/**
- * Given an enterprise UUID, returns overview of coupons associated with the enterprise
- * and a list of coupon codes assigned to the authenticated user.
- */
-export function useCouponCodes(enterpriseId) {
- const [state, dispatch] = useReducer(couponCodesReducer, initialCouponCodesState);
-
- const couponsOverviewQueryData = useQuery({
- queryKey: ['coupons', 'overview', enterpriseId],
- queryFn: async () => {
- const response = await fetchCouponsOverview({ enterpriseId });
- return camelCaseObject(response.data);
- },
- });
- useEffect(
- () => {
- if (features.ENROLL_WITH_CODES) {
- fetchCouponCodeAssignments(
- {
- enterprise_uuid: enterpriseId,
- full_discount_only: 'True', // Must be a string because the API does a string compare not a true JSON boolean compare.
- is_active: 'True',
- },
- dispatch,
- );
- }
- },
- [enterpriseId],
- );
-
- const result = useMemo(() => {
- const updatedState = {
- ...state,
- couponsOverview: couponsOverviewQueryData,
- loading: state.loading || couponsOverviewQueryData.isLoading,
- };
- return [updatedState, updatedState.loading];
- }, [state, couponsOverviewQueryData]);
-
- return result;
-}
-
-export function useCustomerAgreementData(enterpriseId) {
- const [customerAgreement, setCustomerAgreement] = useState();
- const [isLoading, setIsLoading] = useState(true);
-
- useEffect(() => {
- fetchCustomerAgreementData(enterpriseId)
- .then((response) => {
- const { results } = camelCaseObject(response.data);
- // Note: customer agreements are unique, only 1 can exist per customer
- setCustomerAgreement(results[0] || null);
- })
- .catch((error) => {
- logError(new Error(error));
- setCustomerAgreement(null);
- })
- .finally(() => {
- setIsLoading(false);
- });
- }, [enterpriseId]);
-
- return [customerAgreement, isLoading];
-}
-
-const getRedeemablePoliciesData = async ({ queryKey }) => {
- const enterpriseId = queryKey[3];
- const userID = queryKey[4];
- const response = await fetchRedeemableLearnerCreditPolicies(enterpriseId, userID);
- const responseData = camelCaseObject(response.data);
- const redeemablePolicies = transformRedeemablePoliciesData(responseData);
- const learnerContentAssignments = getAssignmentsByState(
- redeemablePolicies?.flatMap(item => item.learnerContentAssignments || []),
- );
- return {
- redeemablePolicies,
- learnerContentAssignments,
- };
-};
-
-export function useRedeemableLearnerCreditPolicies(enterpriseId, userID) {
- return useQuery({
- queryKey: enterpriseUserSubsidyQueryKeys.redeemablePolicies(enterpriseId, userID),
- queryFn: getRedeemablePoliciesData,
- onError: (error) => {
- logError(error);
- },
- });
-}
diff --git a/src/components/enterprise-user-subsidy/data/hooks/index.js b/src/components/enterprise-user-subsidy/data/hooks/index.js
deleted file mode 100644
index 6a59410e84..0000000000
--- a/src/components/enterprise-user-subsidy/data/hooks/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './hooks';
-export { default as useSubscriptions } from './useSubscriptions';
diff --git a/src/components/enterprise-user-subsidy/data/hooks/useSubscriptions.js b/src/components/enterprise-user-subsidy/data/hooks/useSubscriptions.js
deleted file mode 100644
index c713168f2c..0000000000
--- a/src/components/enterprise-user-subsidy/data/hooks/useSubscriptions.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { useState, useEffect } from 'react';
-
-import { useSubscriptionLicense, useCustomerAgreementData } from './hooks';
-import { hasValidStartExpirationDates } from '../../../../utils/common';
-
-/**
- * Given an authenticated user and an enterprise customer config, returns the user's subscription license (if any)
- * along with metadata about the customer agreement and subscription plan(s). Includes a function to allow consumers
- * to activate the user's license.
- */
-function useSubscriptions({
- authenticatedUser,
- enterpriseCustomer,
-}) {
- const [subscriptionPlan, setSubscriptionPlan] = useState();
- const [showExpirationNotifications, setShowExpirationNotifications] = useState();
- const [customerAgreementConfig, isLoadingCustomerAgreementConfig] = useCustomerAgreementData(enterpriseCustomer.uuid);
- const {
- license: subscriptionLicense,
- isLoading: isLoadingLicense,
- } = useSubscriptionLicense({
- enterpriseCustomer,
- customerAgreementConfig,
- isLoadingCustomerAgreementConfig,
- user: authenticatedUser,
- });
-
- useEffect(
- () => {
- if (subscriptionLicense?.subscriptionPlan) {
- setSubscriptionPlan({
- ...subscriptionLicense.subscriptionPlan,
- isCurrent: hasValidStartExpirationDates({
- startDate: subscriptionLicense.subscriptionPlan.startDate,
- expirationDate: subscriptionLicense.subscriptionPlan.expirationDate,
- }),
- });
- }
- setShowExpirationNotifications(!(customerAgreementConfig?.disableExpirationNotifications));
- },
- [subscriptionLicense, customerAgreementConfig],
- );
-
- return {
- customerAgreementConfig,
- subscriptionPlan,
- subscriptionLicense,
- isLoading: isLoadingCustomerAgreementConfig || isLoadingLicense,
- showExpirationNotifications,
- };
-}
-
-export default useSubscriptions;
diff --git a/src/components/enterprise-user-subsidy/data/hooks/useSubscriptions.test.js b/src/components/enterprise-user-subsidy/data/hooks/useSubscriptions.test.js
deleted file mode 100644
index e32988626d..0000000000
--- a/src/components/enterprise-user-subsidy/data/hooks/useSubscriptions.test.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import { renderHook } from '@testing-library/react-hooks';
-
-import useSubscriptions from './useSubscriptions';
-import { useCustomerAgreementData, useSubscriptionLicense } from './hooks';
-import { hasValidStartExpirationDates } from '../../../../utils/common';
-
-jest.mock('./hooks', () => ({
- ...jest.requireActual('./hooks'),
- useCustomerAgreementData: jest.fn(),
- useSubscriptionLicense: jest.fn(),
-}));
-
-jest.mock('../../../../utils/common', () => ({
- ...jest.requireActual('../../../../utils/common'),
- hasValidStartExpirationDates: jest.fn(),
-}));
-
-const mockCustomerAgreement = {
- uuid: 'test-customer-agreement-uuid',
- disableExpirationNotifications: false,
-};
-const mockSubscriptionPlan = { uuid: 'test-subscription-plan-uuid', isCurrent: true };
-
-describe('useSubscriptions', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it.each([
- {
- isLoadingCustomerAgreement: false,
- isLoadingLicense: false,
- expectedLoadingState: false,
- },
- {
- isLoadingCustomerAgreement: false,
- isLoadingLicense: true,
- expectedLoadingState: true,
- },
- {
- isLoadingCustomerAgreement: true,
- isLoadingLicense: false,
- expectedLoadingState: true,
- },
- {
- isLoadingCustomerAgreement: true,
- isLoadingLicense: true,
- expectedLoadingState: true,
- },
- ])('handles loading states (%s)', ({
- isLoadingCustomerAgreement,
- isLoadingLicense,
- expectedLoadingState,
- }) => {
- useCustomerAgreementData.mockReturnValue([undefined, isLoadingCustomerAgreement]);
- useSubscriptionLicense.mockReturnValue({
- license: undefined,
- isLoading: isLoadingLicense,
- });
-
- const args = {
- authenticatedUser: {},
- enterpriseCustomer: {},
- };
- const { result } = renderHook(() => useSubscriptions(args));
- expect(result.current).toEqual(
- expect.objectContaining({
- isLoading: expectedLoadingState,
- }),
- );
- });
-
- it.each([
- {
- hasDisabledExpirationNotifications: false,
- expectedShowExpirationNotifications: true,
- isSubscriptionPlanCurrent: true,
- },
- {
- hasDisabledExpirationNotifications: true,
- expectedShowExpirationNotifications: false,
- isSubscriptionPlanCurrent: true,
- },
- ])('does stuff (%s)', async ({
- hasDisabledExpirationNotifications,
- expectedShowExpirationNotifications,
- isSubscriptionPlanCurrent,
- }) => {
- const anotherMockCustomerAgreement = {
- ...mockCustomerAgreement,
- disableExpirationNotifications: hasDisabledExpirationNotifications,
- };
- const mockSubscriptionPlanWithCurrentStatus = {
- ...mockSubscriptionPlan,
- isCurrent: isSubscriptionPlanCurrent,
- };
- const mockSubscriptionLicense = {
- uuid: 'test-license-uuid',
- subscriptionPlan: mockSubscriptionPlanWithCurrentStatus,
- };
- useCustomerAgreementData.mockReturnValue([anotherMockCustomerAgreement, false]);
- useSubscriptionLicense.mockReturnValue({
- license: mockSubscriptionLicense,
- isLoading: false,
- });
- hasValidStartExpirationDates.mockReturnValue(isSubscriptionPlanCurrent);
- const args = {
- authenticatedUser: {},
- enterpriseCustomer: {},
- };
- const { result } = renderHook(() => useSubscriptions(args));
- expect(result.current).toEqual(
- expect.objectContaining({
- customerAgreementConfig: anotherMockCustomerAgreement,
- isLoading: false,
- subscriptionLicense: mockSubscriptionLicense,
- subscriptionPlan: mockSubscriptionPlanWithCurrentStatus,
- showExpirationNotifications: expectedShowExpirationNotifications,
- }),
- );
- });
-});
diff --git a/src/components/enterprise-user-subsidy/data/service.js b/src/components/enterprise-user-subsidy/data/service.js
deleted file mode 100644
index c3fd9f26ee..0000000000
--- a/src/components/enterprise-user-subsidy/data/service.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { getConfig } from '@edx/frontend-platform/config';
-import { loginRefresh } from '../../../utils/common';
-
-export function fetchSubscriptionLicensesForUser(enterpriseUUID) {
- const queryParams = new URLSearchParams({
- enterprise_customer_uuid: enterpriseUUID,
- include_revoked: true,
- });
- const config = getConfig();
- const url = `${config.LICENSE_MANAGER_URL}/api/v1/learner-licenses/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-}
-
-export async function activateLicense(activationKey) {
- const config = getConfig();
-
- // If the user has not refreshed their JWT since they created their account,
- // we should refresh it so that they'll have appropriate roles (if available),
- // and thus, have any appropriate permissions when making downstream requests.
- loginRefresh();
-
- const queryParams = new URLSearchParams({ activation_key: activationKey });
- const url = `${config.LICENSE_MANAGER_URL}/api/v1/license-activation/?${queryParams.toString()}`;
-
- return getAuthenticatedHttpClient().post(url);
-}
-
-export function fetchCustomerAgreementData(enterpriseUUID) {
- const queryParams = new URLSearchParams({
- enterprise_customer_uuid: enterpriseUUID,
- });
- const config = getConfig();
- const url = `${config.LICENSE_MANAGER_URL}/api/v1/customer-agreement/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-}
-
-export function fetchRedeemableLearnerCreditPolicies(enterpriseUUID, userID) {
- const queryParams = new URLSearchParams({
- enterprise_customer_uuid: enterpriseUUID,
- lms_user_id: userID,
- });
- const config = getConfig();
- const url = `${config.ENTERPRISE_ACCESS_BASE_URL}/api/v1/policy-redemption/credits_available/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-}
diff --git a/src/components/enterprise-user-subsidy/data/utils.js b/src/components/enterprise-user-subsidy/data/utils.js
deleted file mode 100644
index 9a71102527..0000000000
--- a/src/components/enterprise-user-subsidy/data/utils.js
+++ /dev/null
@@ -1,150 +0,0 @@
-import { logError } from '@edx/frontend-platform/logging';
-
-import { ASSIGNMENT_TYPES, POLICY_TYPES } from '../enterprise-offers/data/constants';
-import { LICENSE_STATUS } from './constants';
-
-/**
- * Transforms the redeemable policies data by attaching the subsidy expiration date
- * to each assignment within the policies, if available.
- * @param {object[]} [policies] - Array of policy objects containing learner assignments.
- * @returns {object} - Returns modified policies data with subsidy expiration dates attached to assignments.
- */
-export const transformRedeemablePoliciesData = (policies = []) => policies.map((policy) => {
- const assignmentsWithSubsidyExpiration = policy.learnerContentAssignments?.map(assignment => ({
- ...assignment,
- subsidyExpirationDate: policy.subsidyExpirationDate,
- }));
- return {
- ...policy,
- learnerContentAssignments: assignmentsWithSubsidyExpiration,
- };
-});
-
-/**
- * Determine whether learner has only content assignments available to them, based on the presence of:
- * - content assignments for display (allocated or canceled)
- * - no auto-applied budgets
- * - no current enterprise offers
- * - no active license or license requests
- * - no assigned codes or code requests
- *
- * @param {Object} params - The parameters object.
- * @param {Object} params.subscriptionPlan - The subscription plan of the learner.
- * @param {Object} params.subscriptionLicense - The subscription license of the learner.
- * @param {Array} params.licenseRequests - The license requests of the learner.
- * @param {number} params.couponCodesCount - The count of assigned coupon codes of the learner.
- * @param {Array} params.couponCodeRequests - The coupon code requests of the learner.
- * @param {Object} params.redeemableLearnerCreditPolicies - The redeemable learner credit policies.
- * @param {boolean} params.hasCurrentEnterpriseOffers - Whether the learner has current enterprise offers.
- * @returns {boolean} - Returns true if the learner has only content assignments available to them, false otherwise.
- */
-export const determineLearnerHasContentAssignmentsOnly = ({
- subscriptionPlan,
- subscriptionLicense,
- licenseRequests,
- couponCodesCount,
- couponCodeRequests,
- redeemableLearnerCreditPolicies,
- hasCurrentEnterpriseOffers,
-}) => {
- const hasActiveLicense = !!(subscriptionPlan?.isActive && subscriptionLicense?.status === LICENSE_STATUS.ACTIVATED);
- const hasActiveLicenseOrLicenseRequest = hasActiveLicense || licenseRequests.length > 0;
- const hasAssignedCodesOrCodeRequests = couponCodesCount > 0 || couponCodeRequests.length > 0;
- const autoAppliedPolicyTypes = [
- POLICY_TYPES.PER_LEARNER_CREDIT,
- POLICY_TYPES.PER_ENROLLMENT_CREDIT,
- ];
- const hasAutoAppliedLearnerCreditPolicies = !!redeemableLearnerCreditPolicies?.redeemablePolicies?.filter(
- policy => autoAppliedPolicyTypes.includes(policy.policyType),
- ).length > 0;
- const hasAllocatedOrAcceptedAssignments = !!(
- redeemableLearnerCreditPolicies?.learnerContentAssignments?.hasAllocatedAssignments
- || redeemableLearnerCreditPolicies?.learnerContentAssignments?.hasAcceptedAssignments
- );
-
- return (
- hasAllocatedOrAcceptedAssignments
- && !hasCurrentEnterpriseOffers
- && !hasActiveLicenseOrLicenseRequest
- && !hasAssignedCodesOrCodeRequests
- && !hasAutoAppliedLearnerCreditPolicies
- );
-};
-
-/**
- * Takes a flattened array of assignments and returns an object containing
- * lists of assignments for each assignment state.
- *
- * @param {Array} assignments - List of content assignments.
- * @returns {{
-* assignments: Array,
-* hasAssignments: Boolean,
-* allocatedAssignments: Array,
-* hasAllocatedAssignments: Boolean,
-* canceledAssignments: Array,
-* hasCanceledAssignments: Boolean,
-* acceptedAssignments: Array,
-* hasAcceptedAssignments: Boolean,
-* }}
-*/
-export function getAssignmentsByState(assignments = []) {
- const allocatedAssignments = [];
- const acceptedAssignments = [];
- const canceledAssignments = [];
- const expiredAssignments = [];
- const erroredAssignments = [];
- const assignmentsForDisplay = [];
-
- assignments.forEach((assignment) => {
- switch (assignment.state) {
- case ASSIGNMENT_TYPES.ALLOCATED:
- allocatedAssignments.push(assignment);
- break;
- case ASSIGNMENT_TYPES.ACCEPTED:
- acceptedAssignments.push(assignment);
- break;
- case ASSIGNMENT_TYPES.CANCELED:
- canceledAssignments.push(assignment);
- break;
- case ASSIGNMENT_TYPES.EXPIRED:
- expiredAssignments.push(assignment);
- break;
- case ASSIGNMENT_TYPES.ERRORED:
- erroredAssignments.push(assignment);
- break;
- default:
- logError(`[getAssignmentsByState] Unsupported state ${assignment.state} for assignment ${assignment.uuid}`);
- break;
- }
- });
-
- const hasAssignments = assignments.length > 0;
- const hasAllocatedAssignments = allocatedAssignments.length > 0;
- const hasAcceptedAssignments = acceptedAssignments.length > 0;
- const hasCanceledAssignments = canceledAssignments.length > 0;
- const hasExpiredAssignments = expiredAssignments.length > 0;
- const hasErroredAssignments = erroredAssignments.length > 0;
-
- // Concatenate all assignments for display (includes allocated and canceled assignments)
- assignmentsForDisplay.push(...allocatedAssignments);
- assignmentsForDisplay.push(...canceledAssignments);
- assignmentsForDisplay.push(...expiredAssignments);
- const hasAssignmentsForDisplay = assignmentsForDisplay.length > 0;
-
- return {
- assignments,
- hasAssignments,
- allocatedAssignments,
- hasAllocatedAssignments,
- acceptedAssignments,
- hasAcceptedAssignments,
- canceledAssignments,
- hasCanceledAssignments,
- expiredAssignments,
- hasExpiredAssignments,
- erroredAssignments,
- hasErroredAssignments,
- assignmentsForDisplay,
- hasAssignmentsForDisplay,
- };
-}
diff --git a/src/components/enterprise-user-subsidy/data/utils.test.js b/src/components/enterprise-user-subsidy/data/utils.test.js
deleted file mode 100644
index 8fbedee082..0000000000
--- a/src/components/enterprise-user-subsidy/data/utils.test.js
+++ /dev/null
@@ -1,496 +0,0 @@
-import { ASSIGNMENT_TYPES, POLICY_TYPES } from '../enterprise-offers/data/constants';
-import { LICENSE_STATUS } from './constants';
-import { determineLearnerHasContentAssignmentsOnly, transformRedeemablePoliciesData } from './utils';
-
-import { emptyRedeemableLearnerCreditPolicies } from '../../app/data';
-
-describe('transformRedeemablePoliciesData', () => {
- test('transforms policies data by attaching subsidy expiration date to assignments', () => {
- const mockPolicies = [
- {
- subsidyExpirationDate: '2024-03-15T18:48:26Z',
- learnerContentAssignments: [
- { assignmentId: 1 },
- { assignmentId: 2 },
- ],
- },
- {
- subsidyExpirationDate: '2023-12-31T23:59:59Z',
- learnerContentAssignments: [
- { assignmentId: 3 },
- ],
- },
- ];
-
- const expectedTransformedData = [
- {
- subsidyExpirationDate: '2024-03-15T18:48:26Z',
- learnerContentAssignments: [
- { assignmentId: 1, subsidyExpirationDate: '2024-03-15T18:48:26Z' },
- { assignmentId: 2, subsidyExpirationDate: '2024-03-15T18:48:26Z' },
- ],
- },
- {
- subsidyExpirationDate: '2023-12-31T23:59:59Z',
- learnerContentAssignments: [
- { assignmentId: 3, subsidyExpirationDate: '2023-12-31T23:59:59Z' },
- ],
- },
- ];
-
- const transformedData = transformRedeemablePoliciesData(mockPolicies);
- expect(transformedData).toEqual(expectedTransformedData);
- });
-});
-
-describe('determineLearnerHasContentAssignmentsOnly', () => {
- test.each([
- /**
- * - `isAssignmentLearnerOnly`: true
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: true,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: true
- * - Has assignable redeemable policy with accepted assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: true,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ACCEPTED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ACCEPTED }],
- hasAssignments: true,
- allocatedAssignments: [],
- hasAllocatedAssignments: false,
- acceptedAssignments: [{ state: ASSIGNMENT_TYPES.ACCEPTED }],
- hasAcceptedAssignments: true,
- assignmentsForDisplay: [],
- hasAssignmentsForDisplay: false,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has assignable redeemable policy with allocated assignment
- * - Has another auto-applied redeemable policy
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- {
- policyType: POLICY_TYPES.PER_LEARNER_CREDIT,
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has current enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: true,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: true
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has active subscription plan (without activated license)
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: true,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: true,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: true
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has inactive subscription plan (with activated license)
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: true,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: { status: LICENSE_STATUS.ACTIVATED },
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has active subscription plan (with activated license)
- * - Has no subscription license requests
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: true,
- },
- subscriptionLicense: { status: LICENSE_STATUS.ACTIVATED },
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has subscription license request(s)
- * - Has no coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [{ id: 1 }],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license request(s)
- * - Has available coupon codes
- * - Has no coupon code requests
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 1,
- couponCodeRequests: [],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has assignable redeemable policy with allocated assignment
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license request(s)
- * - Has no coupon codes
- * - Has coupon code request(s)
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: {
- redeemablePolicies: [
- {
- policyType: POLICY_TYPES.ASSIGNED_CREDIT,
- learnerContentAssignments: [
- { state: ASSIGNMENT_TYPES.ALLOCATED },
- ],
- },
- ],
- learnerContentAssignments: {
- ...emptyRedeemableLearnerCreditPolicies.learnerContentAssignments,
- assignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignments: true,
- allocatedAssignments: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAllocatedAssignments: true,
- assignmentsForDisplay: [{ state: ASSIGNMENT_TYPES.ALLOCATED }],
- hasAssignmentsForDisplay: true,
- },
- },
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [{ id: 1 }],
- },
- /**
- * - `isAssignmentLearnerOnly`: false
- * - Has no assignable redeemable policy
- * - Has no other redeemable policies (auto-applied)
- * - Has no enterprise offer
- * - Has no active subscription plan and/or activated license
- * - Has no subscription license request(s)
- * - Has no coupon codes
- * - Has no coupon code request(s)
- */
- {
- isAssignmentLearnerOnly: false,
- redeemableLearnerCreditPolicies: emptyRedeemableLearnerCreditPolicies,
- hasCurrentEnterpriseOffers: false,
- subscriptionPlan: {
- isActive: false,
- },
- subscriptionLicense: undefined,
- licenseRequests: [],
- couponCodesCount: 0,
- couponCodeRequests: [],
- },
- ])('determines whether learner only has assignments available, i.e. no other subsidies (%s)', ({
- isAssignmentLearnerOnly,
- redeemableLearnerCreditPolicies,
- hasCurrentEnterpriseOffers,
- subscriptionPlan,
- subscriptionLicense,
- licenseRequests,
- couponCodesCount,
- couponCodeRequests,
- }) => {
- const actualResult = determineLearnerHasContentAssignmentsOnly({
- subscriptionPlan,
- subscriptionLicense,
- licenseRequests,
- couponCodesCount,
- couponCodeRequests,
- redeemableLearnerCreditPolicies,
- hasCurrentEnterpriseOffers,
- });
- expect(actualResult).toEqual(isAssignmentLearnerOnly);
- });
-});
diff --git a/src/components/enterprise-user-subsidy/enterprise-offers/data/constants.js b/src/components/enterprise-user-subsidy/enterprise-offers/data/constants.js
index c0df59bb38..fa7f56b3cc 100644
--- a/src/components/enterprise-user-subsidy/enterprise-offers/data/constants.js
+++ b/src/components/enterprise-user-subsidy/enterprise-offers/data/constants.js
@@ -30,6 +30,7 @@ export const NO_BALANCE_CONTACT_ADMIN_TEXT = 'Contact administrator';
export const OFFER_BALANCE_CLICK_EVENT = 'edx.ui.enterprise.learner_portal.offer_balance_alert.clicked';
+// TODO: why are these Learner Credit policies and content assignments related constants in this file?
export const ASSIGNMENT_TYPES = {
ACCEPTED: 'accepted',
ALLOCATED: 'allocated',
@@ -37,7 +38,6 @@ export const ASSIGNMENT_TYPES = {
EXPIRED: 'expired',
ERRORED: 'errored',
};
-
export const POLICY_TYPES = {
ASSIGNED_CREDIT: 'AssignedLearnerCreditAccessPolicy',
PER_LEARNER_CREDIT: 'PerLearnerSpendCreditAccessPolicy',
diff --git a/src/components/enterprise-user-subsidy/enterprise-offers/data/hooks.js b/src/components/enterprise-user-subsidy/enterprise-offers/data/hooks.js
deleted file mode 100644
index c56204f144..0000000000
--- a/src/components/enterprise-user-subsidy/enterprise-offers/data/hooks.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import {
- useState,
- useEffect,
-} from 'react';
-import { camelCaseObject } from '@edx/frontend-platform/utils';
-import { logError } from '@edx/frontend-platform/logging';
-import { features } from '../../../../config';
-import * as enterpriseOffersService from './service';
-import { transformEnterpriseOffer } from './utils';
-
-export const useEnterpriseOffers = ({
- enterpriseId,
- enableLearnerPortalOffers,
-}) => {
- const [enterpriseOffers, setEnterpriseOffers] = useState([]);
- const [currentEnterpriseOffers, setCurrentEnterpriseOffers] = useState([]);
- const [isLoadingOffers, setIsLoadingOffers] = useState(true);
- const [canEnrollWithEnterpriseOffers, setCanEnrollWithEnterpriseOffers] = useState(false);
- const [hasCurrentEnterpriseOffers, setHasCurrentEnterpriseOffers] = useState(false);
- const [hasLowEnterpriseOffersBalance, setHasLowEnterpriseOffersBalance] = useState(false);
- const [hasNoEnterpriseOffersBalance, setHasNoEnterpriseOffersBalance] = useState(false);
-
- const enableOffers = features.FEATURE_ENROLL_WITH_ENTERPRISE_OFFERS && enableLearnerPortalOffers;
-
- useEffect(() => {
- // Fetch enterprise offers here if features.FEATURE_ENROLL_WITH_ENTERPRISE_OFFERS is true
- const fetchEnterpriseOffers = async () => {
- try {
- const response = await enterpriseOffersService.fetchEnterpriseOffers(enterpriseId);
- const { results } = camelCaseObject(response.data);
- setEnterpriseOffers(results.map(offer => transformEnterpriseOffer(offer)));
- } catch (error) {
- logError(error);
- } finally {
- setIsLoadingOffers(false);
- }
- };
-
- if (enableOffers) {
- fetchEnterpriseOffers();
- } else {
- setIsLoadingOffers(false);
- }
- }, [enterpriseId, enableOffers]);
-
- useEffect(() => {
- if (!enableOffers || isLoadingOffers) {
- return;
- }
-
- if (enterpriseOffers.length > 0) {
- setCanEnrollWithEnterpriseOffers(true);
- }
-
- const currentEntOffers = enterpriseOffers.filter(offer => offer.isCurrent);
- if (currentEntOffers.length > 0) {
- setCurrentEnterpriseOffers(currentEntOffers);
- setHasCurrentEnterpriseOffers(true);
- const hasLowBalance = currentEntOffers.some(offer => offer.isLowOnBalance);
- const hasNoBalance = currentEntOffers.every(offer => offer.isOutOfBalance);
- setHasLowEnterpriseOffersBalance(hasLowBalance);
- setHasNoEnterpriseOffersBalance(hasNoBalance);
- }
- }, [
- isLoadingOffers,
- enableOffers,
- enterpriseOffers,
- ]);
-
- return {
- enterpriseOffers,
- currentEnterpriseOffers,
- hasCurrentEnterpriseOffers,
- canEnrollWithEnterpriseOffers,
- hasLowEnterpriseOffersBalance,
- hasNoEnterpriseOffersBalance,
- isLoading: isLoadingOffers,
- };
-};
diff --git a/src/components/enterprise-user-subsidy/enterprise-offers/data/service.js b/src/components/enterprise-user-subsidy/enterprise-offers/data/service.js
deleted file mode 100644
index 777910d957..0000000000
--- a/src/components/enterprise-user-subsidy/enterprise-offers/data/service.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { getConfig } from '@edx/frontend-platform/config';
-import { ENTERPRISE_OFFER_STATUS, ENTERPRISE_OFFER_USAGE_TYPE } from './constants';
-
-export function fetchEnterpriseOffers(enterpriseId, options = {
- usage_type: ENTERPRISE_OFFER_USAGE_TYPE.PERCENTAGE,
- discount_value: 100,
- status: ENTERPRISE_OFFER_STATUS.OPEN,
- page_size: 100,
-}) {
- const queryParams = new URLSearchParams({
- ...options,
- });
- const config = getConfig();
- const url = `${config.ECOMMERCE_BASE_URL}/api/v2/enterprise/${enterpriseId}/enterprise-learner-offers/?${queryParams.toString()}`;
- return getAuthenticatedHttpClient().get(url);
-}
diff --git a/src/components/enterprise-user-subsidy/enterprise-offers/data/tests/hooks.test.jsx b/src/components/enterprise-user-subsidy/enterprise-offers/data/tests/hooks.test.jsx
deleted file mode 100644
index a3480dee0e..0000000000
--- a/src/components/enterprise-user-subsidy/enterprise-offers/data/tests/hooks.test.jsx
+++ /dev/null
@@ -1,194 +0,0 @@
-import { renderHook } from '@testing-library/react-hooks';
-import { camelCaseObject } from '@edx/frontend-platform/utils';
-
-import { useEnterpriseOffers } from '../hooks';
-import * as enterpriseOffersService from '../service';
-import * as config from '../../../../../config';
-import { transformEnterpriseOffer } from '../utils';
-
-jest.mock('../../../coupons/data/service');
-jest.mock('../service');
-jest.mock('../../../data/service');
-
-const mockEnterpriseUUID = 'enterprise-uuid';
-const defaultProps = {
- enterpriseId: mockEnterpriseUUID,
- enableLearnerPortalOffers: true,
- customerAgreementConfig: undefined,
- isLoadingCustomerAgreementConfig: false,
-};
-const mockEnterpriseOffers = [{
- discount_value: 100,
- end_datetime: '2023-01-06T00:00:00Z',
- enterprise_catalog_uuid: 'uuid',
- id: 1,
- max_discount: 200,
- remaining_balance: 200,
- start_datetime: '2022-06-09T00:00:00Z',
- usage_type: 'Percentage',
- is_current: true,
-}];
-
-describe('useEnterpriseOffers', () => {
- beforeEach(() => {
- config.features.FEATURE_ENROLL_WITH_ENTERPRISE_OFFERS = true;
- });
- afterEach(() => jest.resetAllMocks());
- it('fetches and sets enterprise offers', async () => {
- enterpriseOffersService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: mockEnterpriseOffers,
- },
- });
-
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers(defaultProps));
-
- await waitForNextUpdate();
-
- expect(enterpriseOffersService.fetchEnterpriseOffers).toHaveBeenCalled();
- expect(result.current.enterpriseOffers).toEqual(
- camelCaseObject(mockEnterpriseOffers).map(offer => transformEnterpriseOffer(offer)),
- );
- });
-
- it.each([{
- featureFlagToggled: true,
- featureEnabledForEnterprise: false,
- }, {
- featureFlagToggled: false,
- featureEnabledForEnterprise: true,
- }])(
- 'does not fetch enterprise offers if FEATURE_ENROLL_WITH_ENTERPRISE_OFFERS = false or enableLearnerPortalOffers = false',
- async (
- {
- featureFlagToggled,
- featureEnabledForEnterprise,
- },
- ) => {
- config.features.FEATURE_ENROLL_WITH_ENTERPRISE_OFFERS = featureFlagToggled;
-
- renderHook(() => useEnterpriseOffers({
- ...defaultProps,
- enableLearnerPortalOffers: featureEnabledForEnterprise,
- }));
-
- expect(enterpriseOffersService.fetchEnterpriseOffers).not.toHaveBeenCalled();
- },
- );
-
- it.each([
- {
- offers: [],
- expectedResult: false,
- },
- {
- offers: [
- { ...mockEnterpriseOffers[0], is_current: false },
- { ...mockEnterpriseOffers[0], id: 2, is_current: false },
- ],
- expectedResult: true,
- },
- {
- offers: [mockEnterpriseOffers[0], { ...mockEnterpriseOffers[0], id: 2, is_current: false }],
- expectedResult: true,
- },
- ])('returns canEnrollWithEnterpriseOffers = true if the enterprise has >= 1 current (non-expired) enterprise offer', async ({
- offers,
- expectedResult,
- }) => {
- enterpriseOffersService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: { results: offers },
- });
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers(defaultProps));
- await waitForNextUpdate();
- expect(result.current.canEnrollWithEnterpriseOffers).toEqual(expectedResult);
- });
-
- it.each([
- {
- offerResults: [{
- ...mockEnterpriseOffers[0],
- remaining_balance: 50,
- max_discount: 1000,
- }, {
- ...mockEnterpriseOffers[0],
- id: 2,
- remaining_balance: 500,
- max_discount: 1000,
- }],
- expectedHasLowEnterpriseOffersBalance: true,
- },
- {
- offerResults: [{
- ...mockEnterpriseOffers[0],
- remaining_balance: 150,
- max_discount: 1000,
- }, {
- ...mockEnterpriseOffers[0],
- id: 2,
- remaining_balance: 500,
- max_discount: 1000,
- }],
- expectedHasLowEnterpriseOffersBalance: false,
- },
- ])(
- 'determines low balance when any enterprise offer has remainingBalance <= 10%',
- async ({ offerResults, expectedHasLowEnterpriseOffersBalance }) => {
- enterpriseOffersService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: offerResults,
- },
- });
-
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers(defaultProps));
-
- await waitForNextUpdate();
-
- expect(result.current.hasLowEnterpriseOffersBalance).toEqual(expectedHasLowEnterpriseOffersBalance);
- },
- );
-
- it.each([
- {
- offerResults: [{
- ...mockEnterpriseOffers[0],
- remaining_balance: 50,
- max_discount: 1000,
- }, {
- ...mockEnterpriseOffers[0],
- remaining_balance: 80,
- max_discount: 1000,
- }],
- expectedHasNoEnterpriseOffersBalance: true,
- },
- {
- offerResults: [{
- ...mockEnterpriseOffers[0],
- remaining_balance: 100,
- max_discount: 1000,
- }, {
- ...mockEnterpriseOffers[0],
- remaining_balance: 25,
- max_discount: 1000,
- }],
- expectedHasNoEnterpriseOffersBalance: false,
- },
- ])(
- 'determines no balance when all enterprise offers have remainingBalance <= 99',
- async ({
- offerResults, expectedHasNoEnterpriseOffersBalance,
- }) => {
- enterpriseOffersService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: offerResults,
- },
- });
-
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers(defaultProps));
-
- await waitForNextUpdate();
-
- expect(result.current.hasNoEnterpriseOffersBalance).toEqual(expectedHasNoEnterpriseOffersBalance);
- },
- );
-});
diff --git a/src/components/enterprise-user-subsidy/enterprise-offers/data/tests/service.test.js b/src/components/enterprise-user-subsidy/enterprise-offers/data/tests/service.test.js
deleted file mode 100644
index 70bcf52fb7..0000000000
--- a/src/components/enterprise-user-subsidy/enterprise-offers/data/tests/service.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { getConfig } from '@edx/frontend-platform/config';
-
-import { fetchEnterpriseOffers } from '../service';
-import { ENTERPRISE_OFFER_STATUS, ENTERPRISE_OFFER_USAGE_TYPE } from '../constants';
-
-jest.mock('@edx/frontend-platform/auth');
-const axiosMock = new MockAdapter(axios);
-getAuthenticatedHttpClient.mockReturnValue(axios);
-axiosMock.onAny().reply(200);
-axios.get = jest.fn();
-
-const TEST_ENTERPRISE_UUID = 'enterprise-uuid';
-
-describe('fetchEnterpriseOffers', () => {
- const config = getConfig();
-
- it('fetches enterprise offers with the correct query params', () => {
- const queryParams = new URLSearchParams({
- usage_type: ENTERPRISE_OFFER_USAGE_TYPE.PERCENTAGE,
- discount_value: 100,
- status: ENTERPRISE_OFFER_STATUS.OPEN,
- page_size: 100,
- });
- const url = `${config.ECOMMERCE_BASE_URL}/api/v2/enterprise/${TEST_ENTERPRISE_UUID}/enterprise-learner-offers/?${queryParams.toString()}`;
- fetchEnterpriseOffers(TEST_ENTERPRISE_UUID);
- expect(axios.get).toBeCalledWith(url);
- });
-});
diff --git a/src/components/enterprise-user-subsidy/index.js b/src/components/enterprise-user-subsidy/index.js
index 5b71e947de..dc09ebe534 100644
--- a/src/components/enterprise-user-subsidy/index.js
+++ b/src/components/enterprise-user-subsidy/index.js
@@ -1,2 +1 @@
-export { default as UserSubsidy, UserSubsidyContext } from './UserSubsidy';
export { default as EnterpriseOffersBalanceAlert } from './enterprise-offers/EnterpriseOffersBalanceAlert';
diff --git a/src/components/enterprise-user-subsidy/tests/UserSubsidy.test.jsx b/src/components/enterprise-user-subsidy/tests/UserSubsidy.test.jsx
deleted file mode 100644
index 70b7df9dce..0000000000
--- a/src/components/enterprise-user-subsidy/tests/UserSubsidy.test.jsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import { AppContext } from '@edx/frontend-platform/react';
-import '@testing-library/jest-dom/extend-expect';
-
-import UserSubsidy from '../UserSubsidy';
-import { LOADING_SCREEN_READER_TEXT } from '../data/constants';
-import { useCouponCodes, useSubscriptions, useRedeemableLearnerCreditPolicies } from '../data/hooks';
-import { useEnterpriseOffers } from '../enterprise-offers/data/hooks';
-import { useEnterpriseCustomer } from '../../app/data';
-import { authenticatedUserFactory, enterpriseCustomerFactory } from '../../app/data/services/data/__factories__';
-
-jest.mock('../data/hooks', () => ({
- ...jest.requireActual('../data/hooks'),
- useSubscriptions: jest.fn().mockReturnValue({}),
- useCouponCodes: jest.fn().mockReturnValue([]),
- useRedeemableLearnerCreditPolicies: jest.fn().mockReturnValue({ data: undefined }),
-}));
-
-jest.mock('../enterprise-offers/data/hooks', () => ({
- ...jest.requireActual('../enterprise-offers/data/hooks'),
- useEnterpriseOffers: jest.fn().mockReturnValue({}),
-}));
-
-jest.mock('../../app/data', () => ({
- ...jest.requireActual('../../app/data'),
- useEnterpriseCustomer: jest.fn(),
-}));
-
-const mockEnterpriseCustomer = enterpriseCustomerFactory();
-const mockAuthenticatedUser = authenticatedUserFactory();
-
-const UserSubsidyWithAppContext = ({
- contextValue = {},
- authenticatedUser = mockAuthenticatedUser,
- children,
-}) => (
-
-
- {children}
-
-
-);
-
-describe('UserSubsidy', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
- });
-
- test.each([
- {
- isSubscriptionsLoading: false,
- isCouponCodesLoading: false,
- isEnterpriseOffersLoading: false,
- isLoadingRedeemablePolicies: false,
- isLoadingExpected: false,
- },
- {
- isSubscriptionsLoading: true,
- isCouponCodesLoading: false,
- isEnterpriseOffersLoading: false,
- isLoadingRedeemablePolicies: false,
- isLoadingExpected: true,
- },
- {
- isSubscriptionsLoading: false,
- isCouponCodesLoading: true,
- isEnterpriseOffersLoading: false,
- isLoadingRedeemablePolicies: false,
- isLoadingExpected: true,
- },
- {
- isSubscriptionsLoading: false,
- isCouponCodesLoading: false,
- isEnterpriseOffersLoading: true,
- isLoadingRedeemablePolicies: false,
- isLoadingExpected: true,
- },
- {
- isSubscriptionsLoading: false,
- isCouponCodesLoading: false,
- isEnterpriseOffersLoading: false,
- isLoadingRedeemablePolicies: true,
- isLoadingExpected: true,
- },
- {
- isSubscriptionsLoading: true,
- isCouponCodesLoading: true,
- isEnterpriseOffersLoading: true,
- isLoadingRedeemablePolicies: true,
- isLoadingExpected: true,
- },
- {
- isSubscriptionsLoading: true,
- isCouponCodesLoading: true,
- isEnterpriseOffersLoading: false,
- isLoadingRedeemablePolicies: true,
- isLoadingExpected: true,
- },
- ])('shows loading spinner when expected (%s)', ({
- isSubscriptionsLoading,
- isCouponCodesLoading,
- isEnterpriseOffersLoading,
- isLoadingRedeemablePolicies,
- isLoadingExpected,
- }) => {
- useSubscriptions.mockReturnValue({ isLoading: isSubscriptionsLoading });
- useCouponCodes.mockReturnValue([[], isCouponCodesLoading]);
- useEnterpriseOffers.mockReturnValue({ isLoading: isEnterpriseOffersLoading });
- useRedeemableLearnerCreditPolicies.mockReturnValue({
- data: { redeemablePolicies: [], learnerContentAssignments: [] },
- isLoading: isLoadingRedeemablePolicies,
- });
-
- const Component = (
-
- hello world
-
- );
- render(Component);
-
- if (isLoadingExpected) {
- // assert component is loading
- expect(screen.getByText(LOADING_SCREEN_READER_TEXT)).toBeInTheDocument();
- expect(screen.queryByText('hello world')).not.toBeInTheDocument();
- } else {
- // assert component is not loading
- expect(screen.queryByText(LOADING_SCREEN_READER_TEXT)).not.toBeInTheDocument();
- expect(screen.getByText('hello world')).toBeInTheDocument();
- }
- });
-});
diff --git a/src/constants/subsidies.js b/src/constants/subsidies.js
index d2f588f3c0..852e9d2d8b 100644
--- a/src/constants/subsidies.js
+++ b/src/constants/subsidies.js
@@ -9,4 +9,3 @@ export const SUBSIDY_REQUEST_STATE = {
DECLINED: 'declined',
ERROR: 'error',
};
-export const LOADING_SCREEN_READER_TEXT = 'loading your subsidy requests from your organization';