Skip to content

Commit

Permalink
feat: implementing gated top down allocation table subsidy service ap…
Browse files Browse the repository at this point in the history
…i usage
  • Loading branch information
alex-sheehan-edx committed Nov 7, 2023
1 parent d518a4d commit 8c525e4
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { useBudgetId, useOfferRedemptions } from './data';
import { useBudgetId, useOfferRedemptions, useSubsidyAccessPolicy } from './data';

const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
isLoading,
offerRedemptions,
fetchOfferRedemptions,
} = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);

} = useOfferRedemptions(
enterpriseUUID,
enterpriseOfferId,
subsidyAccessPolicyId,
enterpriseFeatures.topDownAssignmentRealTimeLcm,
subsidyAccessPolicy?.subsidyUuid,
);
return (
<section>
<h3 className="mb-3">Spent</h3>
Expand All @@ -30,11 +36,15 @@ const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
};

const mapStateToProps = state => ({
enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
enterpriseUUID: state.portalConfiguration.enterpriseId,
});

BudgetDetailRedemptions.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
enterpriseFeatures: PropTypes.shape({
topDownAssignmentRealTimeLcm: PropTypes.bool,
}).isRequired,
};

export default connect(mapStateToProps)(BudgetDetailRedemptions);
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import debounce from 'lodash.debounce';

import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
import { API_FIELDS_BY_TABLE_COLUMN_ACCESSOR } from '../constants';
import { transformUtilizationTableResults } from '../utils';
import { transformUtilizationTableResults, transformUtilizationTableSubsidyTransactionResults } from '../utils';

const applySortByToOptions = (sortBy, options) => {
const orderingStrings = sortBy.map(({ id, desc }) => {
Expand All @@ -29,19 +30,27 @@ const applySortByToOptions = (sortBy, options) => {
});
};

const applyFiltersToOptions = (filters, options) => {
const applyFiltersToOptions = (filters, options, shouldFetchSubsidyTransactions = false) => {
const courseProductLineSearchQuery = filters?.find(filter => filter.id === 'courseProductLine')?.value;
const searchQuery = filters?.find(filter => filter.id.toLowerCase() === 'enrollment details')?.value;
const searchQuery = filters?.find(filter => filter.id === 'enrollmentDetails')?.value;

if (courseProductLineSearchQuery) {
Object.assign(options, { courseProductLine: courseProductLineSearchQuery });
}
if (searchQuery) {
Object.assign(options, { searchAll: searchQuery });
const searchParams = {};
searchParams[shouldFetchSubsidyTransactions ? 'search' : 'searchAll'] = searchQuery;
Object.assign(options, searchParams);
}
};

const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => {
const useOfferRedemptions = (
enterpriseUUID,
offerId = null,
budgetId = null,
shouldFetchSubsidyTransactions = false,
subsidyUuid = null,
) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
const [offerRedemptions, setOfferRedemptions] = useState({
Expand Down Expand Up @@ -69,14 +78,27 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
applySortByToOptions(args.sortBy, options);
}
if (args.filters?.length > 0) {
applyFiltersToOptions(args.filters, options);
applyFiltersToOptions(args.filters, options, shouldFetchSubsidyTransactions);
}
const response = await EnterpriseDataApiService.fetchCourseEnrollments(
enterpriseUUID,
options,
);
const data = camelCaseObject(response.data);
const transformedTableResults = transformUtilizationTableResults(data.results);
let data;
let transformedTableResults;
if (budgetId && shouldFetchSubsidyTransactions) {
const response = await SubsidyApiService.fetchCustomerTransactions(
enterpriseUUID,
subsidyUuid,
options,
);
data = camelCaseObject(response.data);
transformedTableResults = transformUtilizationTableSubsidyTransactionResults(data.results);
} else {
const response = await EnterpriseDataApiService.fetchCourseEnrollments(
enterpriseUUID,
options,
);
data = camelCaseObject(response.data);
transformedTableResults = transformUtilizationTableResults(data.results);
}

setOfferRedemptions({
itemCount: data.count,
pageCount: data.numPages,
Expand Down Expand Up @@ -104,7 +126,7 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
if (offerId || budgetId) {
fetch();
}
}, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents]);
}, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents, shouldFetchSubsidyTransactions, subsidyUuid]);

const debouncedFetchOfferRedemptions = useMemo(() => debounce(fetchOfferRedemptions, 300), [fetchOfferRedemptions]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('useOfferRedemptions', () => {
{ id: 'enrollmentDate', desc: true },
],
filters: [
{ id: 'Enrollment Details', value: mockOfferEnrollments[0].user_email },
{ id: 'enrollmentDetails', value: mockOfferEnrollments[0].user_email },
],
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const mockAssignableSubsidyAccessPolicy = {
spendAvailableUsd: 10000,
},
isAssignable: true,
subsidyUuid: 'mock-subsidy-uuid',
};

export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
Expand All @@ -22,4 +23,5 @@ export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
spendAvailableUsd: 10000,
},
isAssignable: false,
subsidyUuid: 'mock-subsidy-uuid',
};
10 changes: 10 additions & 0 deletions src/components/learner-credit-management/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ export const transformUtilizationTableResults = results => results.map(result =>
courseKey: result.courseKey,
}));

export const transformUtilizationTableSubsidyTransactionResults = results => results.map(result => ({
created: result.created,
enterpriseEnrollmentId: result.fulfillmentIdentifier,
userEmail: result.lmsUserEmail,
courseTitle: result.contentTitle,
courseListPrice: result.unit === 'usd_cents' ? -1 * (result.quantity / 100) : -1 * results.quantity,
uuid: result.uuid,
courseKey: result.contentKey,
}));

/**
* Gets appropriate color variant for the annotated progress bar.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,11 @@ describe('<BudgetDetailPage />', () => {
it.each([
{
budgetId: mockEnterpriseOfferId,
expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null],
expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null, true, undefined],
},
{
budgetId: mockSubsidyAccessPolicyUUID,
expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID],
expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID, true, undefined],
},
])('displays spend table in "Activity" tab with empty results (%s)', async ({
budgetId,
Expand Down
20 changes: 15 additions & 5 deletions src/data/services/EnterpriseSubsidyApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,29 @@ import { snakeCaseObject } from '@edx/frontend-platform';
import { configuration } from '../../config';

class SubsidyApiService {
static baseUrl = `${configuration.ENTERPRISE_SUBSIDY_BASE_URL}/api/v1`;
static baseUrl = `${configuration.ENTERPRISE_SUBSIDY_BASE_URL}/api/`;

static baseUrlV1 = `${this.baseUrl}/v1/`;

static baseUrlV2 = `${this.baseUrl}/v2/`;

static apiClient = getAuthenticatedHttpClient;

static fetchCustomerTransactions(customerUuid, subsidyUuid, options = {}) {
const queryParams = new URLSearchParams({
...snakeCaseObject(options),
});
const url = `${SubsidyApiService.baseUrlV2}/subsidies/${subsidyUuid}/transactions/?${queryParams.toString()}`;
return SubsidyApiService.apiClient().get(url);
}

static getSubsidyByCustomerUUID(uuid, options = {}) {
const queryParams = new URLSearchParams({
enterprise_customer_uuid: uuid,
...snakeCaseObject(options),
});
const url = `${SubsidyApiService.baseUrl}/subsidies/?${queryParams.toString()}`;
return SubsidyApiService.apiClient({
useCache: configuration.USE_API_CACHE,
}).get(url, { clearCacheEntry: true });
const url = `${SubsidyApiService.baseUrlV1}/subsidies/?${queryParams.toString()}`;
return SubsidyApiService.apiClient().get(url);
}
}

Expand Down

0 comments on commit 8c525e4

Please sign in to comment.