Skip to content

Commit

Permalink
feat: subsidy box points to learner credit
Browse files Browse the repository at this point in the history
added tests
  • Loading branch information
Sameen Fatima authored and Sameen Fatima committed Aug 24, 2023
1 parent 49579e5 commit 8d64e38
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 5 deletions.
83 changes: 83 additions & 0 deletions src/components/dashboard/sidebar/LearnerCreditSummaryCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Badge, Row, Col } from '@edx/paragon';
import dayjs from 'dayjs';
import {
LEARNER_CREDIT_SUMMARY_CARD_TITLE,
LEARNER_CREDIT_ACTIVE_BADGE_LABEL,
LEARNER_CREDIT_ACTIVE_BADGE_VARIANT,
LEARNER_CREDIT_CARD_SUMMARY,
} from './data/constants';
import SidebarCard from './SidebarCard';

const LearnerCreditSummaryCard = ({
className, subsidyLearnerCredit, searchCoursesCta,
}) => {
const totalRemainingBalanceForUser = subsidyLearnerCredit.reduce(
(accumulator, currentSubsidy) => accumulator + (currentSubsidy.remainingBalancePerUser || 0),
0,
);
const subsidyExpiringFirst = subsidyLearnerCredit.sort(
(a, b) => new Date(a.subsidyExpirationDate) - new Date(b.subsidyExpirationDate),
)[0];
return (
<SidebarCard
title={
(
<div className="d-flex align-items-center justify-content-between">
<h3 className="m-0">{LEARNER_CREDIT_SUMMARY_CARD_TITLE}</h3>
<Badge
variant={LEARNER_CREDIT_ACTIVE_BADGE_VARIANT}
className="ml-2"
data-testid="learner-credit-status-badge"
>
{LEARNER_CREDIT_ACTIVE_BADGE_LABEL}
</Badge>
</div>
)
}
cardClassNames={className}
>
{totalRemainingBalanceForUser
? (
<p data-testid="learner-credit-text-detailed">
Apply your <b>${totalRemainingBalanceForUser}</b>{' '}
balance to enroll into courses.
</p>
)
: (
<p data-testid="learner-credit-summary-text">
{ LEARNER_CREDIT_CARD_SUMMARY }
</p>
)}

{subsidyExpiringFirst.subsidyExpirationDate && (
<p data-testid="learner-credit-summary-end-date-text">
Available until <b>{dayjs(subsidyExpiringFirst.subsidyExpirationDate).format('MMM D, YYYY')}</b>
</p>
)}

{searchCoursesCta && (
<Row className="mt-3 d-flex justify-content-end">
<Col xl={12}>{searchCoursesCta}</Col>
</Row>
)}
</SidebarCard>
);
};

LearnerCreditSummaryCard.propTypes = {
subsidyLearnerCredit: PropTypes.arrayOf(PropTypes.shape({
subsidyExpirationDate: PropTypes.string,
remainingBalancePerUser: PropTypes.number,
})).isRequired,
className: PropTypes.string,
searchCoursesCta: PropTypes.node,
};

LearnerCreditSummaryCard.defaultProps = {
className: undefined,
searchCoursesCta: undefined,
};

export default LearnerCreditSummaryCard;
13 changes: 11 additions & 2 deletions src/components/dashboard/sidebar/SubsidiesSummary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import classNames from 'classnames';
import CouponCodesSummaryCard from './CouponCodesSummaryCard';
import SubscriptionSummaryCard from './SubscriptionSummaryCard';
import EnterpriseOffersSummaryCard from './EnterpriseOffersSummaryCard';
import LearnerCreditSummaryCard from './LearnerCreditSummaryCard';
import { UserSubsidyContext } from '../../enterprise-user-subsidy';
import { LICENSE_STATUS } from '../../enterprise-user-subsidy/data/constants';
import { CATALOG_ACCESS_CARD_BUTTON_TEXT } from './data/constants';
Expand Down Expand Up @@ -35,6 +36,7 @@ const SubsidiesSummary = ({
couponCodes: { couponCodesCount },
enterpriseOffers,
canEnrollWithEnterpriseOffers,
subsidyLearnerCredit,
} = useContext(UserSubsidyContext);

const {
Expand All @@ -54,9 +56,10 @@ const SubsidiesSummary = ({
&& userSubscriptionLicense?.status === LICENSE_STATUS.ACTIVATED) || licenseRequests.length > 0;

const hasAssignedCodesOrCodeRequests = couponCodesCount > 0 || couponCodeRequests.length > 0;
const hasAvailableLearnerCredit = subsidyLearnerCredit?.length > 0;

const hasAvailableSubsidyOrRequests = hasActiveLicenseOrLicenseRequest
|| hasAssignedCodesOrCodeRequests || canEnrollWithEnterpriseOffers;
|| hasAssignedCodesOrCodeRequests || canEnrollWithEnterpriseOffers || hasAvailableLearnerCredit;

if (!hasAvailableSubsidyOrRequests) {
return null;
Expand Down Expand Up @@ -100,12 +103,18 @@ const SubsidiesSummary = ({
className="border-0 shadow-none"
/>
)}
{canEnrollWithEnterpriseOffers && (
{!hasAvailableLearnerCredit && canEnrollWithEnterpriseOffers && (
<EnterpriseOffersSummaryCard
className="border-0 shadow-none"
offers={enterpriseOffers}
/>
)}
{ hasAvailableLearnerCredit && (
<LearnerCreditSummaryCard
className="border-0 shadow-none"
subsidyLearnerCredit={subsidyLearnerCredit}
/>
)}
</div>
{searchCoursesCta && (
<SidebarCard
Expand Down
6 changes: 6 additions & 0 deletions src/components/dashboard/sidebar/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export const ENTERPRISE_OFFER_ACTIVE_BADGE_LABEL = 'Active';
export const ENTERPRISE_OFFER_ACTIVE_BADGE_VARIANT = 'success';
export const ENTERPRISE_OFFER_SUMMARY_CARD_SUMMARY = 'Apply your organization\'s learner credit balance to enroll into courses with no out of pocket cost.';

// LearnerCreditSummaryCard
export const LEARNER_CREDIT_SUMMARY_CARD_TITLE = 'Learner Credit';
export const LEARNER_CREDIT_ACTIVE_BADGE_LABEL = 'Active';
export const LEARNER_CREDIT_ACTIVE_BADGE_VARIANT = 'success';
export const LEARNER_CREDIT_CARD_SUMMARY = 'Apply your organization\'s learner credit balance to enroll into courses with no out of pocket cost.';

// Dashboard Sidebar texts
export const CATALOG_ACCESS_CARD_BUTTON_TEXT = 'Find a course';
export const NEED_HELP_BLOCK_TITLE = 'Need help?';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { screen, render } from '@testing-library/react';
import LearnerCreditSummaryCard from '../LearnerCreditSummaryCard';
import { LEARNER_CREDIT_SUMMARY_CARD_TITLE, LEARNER_CREDIT_CARD_SUMMARY } from '../data/constants';

const mockLearnerCreditSubsidy = {
uuid: 'test-subsidy-uuid',
catalogUuid: 'test-enterprise-catalog-uuid',
subsidyExpirationDate: '2023-06-01T00:00:00Z',
remainingBalancePerUser: null,
};

const mockSubsidyWithRemainingBalance = {
...mockLearnerCreditSubsidy,
remainingBalancePerUser: 200,
};

describe('<LearnerCreditSummaryCard />', () => {
it('should render searchCoursesCta', () => {
const cta = 'Search Courses';
render(
<LearnerCreditSummaryCard
subsidyLearnerCredit={[mockLearnerCreditSubsidy]}
searchCoursesCta={
<button type="button">{cta}</button>
}
/>,
);

expect(screen.getByText(LEARNER_CREDIT_SUMMARY_CARD_TITLE)).toBeInTheDocument();
expect(screen.getByText(cta)).toBeInTheDocument();
});

it('should render default summary text if remainingBalancePerUser is null', () => {
render(
<LearnerCreditSummaryCard
subsidyLearnerCredit={[mockLearnerCreditSubsidy]}
/>,
);

expect(screen.getByTestId('learner-credit-summary-text')).toBeInTheDocument();
});

it('should render detailed summary text if remainingBalancePerUser is not null', () => {
const subsidiesWithCredit= [
mockSubsidyWithRemainingBalance,
{
...mockSubsidyWithRemainingBalance,
remainingBalancePerUser: 100,
},
];
render(
<LearnerCreditSummaryCard
subsidyLearnerCredit={subsidiesWithCredit}
/>,
);
// calculate sum of balance available for user across all subsidies
const totalRemainingBalance = subsidiesWithCredit.reduce((acc, subsidy) => {
if (subsidy.remainingBalancePerUser) {
return acc + subsidy.remainingBalancePerUser;
}
return acc;
}, 0);
expect(screen.getByTestId('learner-credit-text-detailed')).toBeInTheDocument();
expect(screen.getByText(`$${totalRemainingBalance}`)).toBeInTheDocument();
});

it('should render earliest subsidy end date, if applicable', () => {
render(
<LearnerCreditSummaryCard
subsidyLearnerCredit={[
mockLearnerCreditSubsidy,
{
...mockLearnerCreditSubsidy,
subsidyExpirationDate: '2022-04-01T00:00:00Z', // earliest end date
},
]}
/>,
);

expect(screen.getByTestId('learner-credit-summary-end-date-text')).toBeInTheDocument();
expect(screen.getByText('2022', { exact: false })).toBeInTheDocument();
});
});
15 changes: 13 additions & 2 deletions src/components/enterprise-user-subsidy/UserSubsidy.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
useSubscriptionLicense,
useCouponCodes,
useCustomerAgreementData,
useSubsidyLearnerCredit,
} from './data/hooks';
import { useEnterpriseOffers } from './enterprise-offers/data/hooks';
import { LOADING_SCREEN_READER_TEXT } from './data/constants';
Expand All @@ -19,7 +20,7 @@ export const UserSubsidyContext = createContext();

const UserSubsidy = ({ children }) => {
const { enterpriseConfig, authenticatedUser } = useContext(AppContext);

const { userId } = authenticatedUser;
const [customerAgreementConfig, isLoadingCustomerAgreementConfig] = useCustomerAgreementData(enterpriseConfig.uuid);
const {
license: subscriptionLicense,
Expand All @@ -32,6 +33,7 @@ const UserSubsidy = ({ children }) => {
user: authenticatedUser,
});
const [couponCodes, isLoadingCouponCodes] = useCouponCodes(enterpriseConfig.uuid);
const [subsidyLearnerCredit, isLoadingLearnerCredit] = useSubsidyLearnerCredit(enterpriseConfig.uuid, userId);
const [subscriptionPlan, setSubscriptionPlan] = useState();
const [showExpirationNotifications, setShowExpirationNotifications] = useState();

Expand Down Expand Up @@ -63,10 +65,17 @@ const UserSubsidy = ({ children }) => {
isLoadingCouponCodes,
isLoadingCustomerAgreementConfig,
isLoadingEnterpriseOffers,
isLoadingLearnerCredit,
];
return loadingStates.includes(true);
},
[isLoadingLicense, isLoadingCouponCodes, isLoadingCustomerAgreementConfig, isLoadingEnterpriseOffers],
[
isLoadingLicense,
isLoadingCouponCodes,
isLoadingCustomerAgreementConfig,
isLoadingEnterpriseOffers,
isLoadingLearnerCredit,
],
);

const contextValue = useMemo(
Expand All @@ -85,6 +94,7 @@ const UserSubsidy = ({ children }) => {
showExpirationNotifications,
customerAgreementConfig,
activateUserLicense,
subsidyLearnerCredit,
};
},
[
Expand All @@ -99,6 +109,7 @@ const UserSubsidy = ({ children }) => {
showExpirationNotifications,
customerAgreementConfig,
activateUserLicense,
subsidyLearnerCredit,
],
);

Expand Down
26 changes: 26 additions & 0 deletions src/components/enterprise-user-subsidy/data/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { LICENSE_STATUS } from './constants';
import {
fetchSubscriptionLicensesForUser,
fetchCustomerAgreementData,
fetchSubsidyLearnerCredit,
requestAutoAppliedLicense,
activateLicense,
} from './service';
Expand Down Expand Up @@ -224,3 +225,28 @@ export function useCustomerAgreementData(enterpriseId) {

return [customerAgreement, isLoading];
}

export function useSubsidyLearnerCredit(enterpriseId, userID) {
const [learnerCreditSubsidies, setLearnerCreditSubsidies] = useState();
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
fetchSubsidyLearnerCredit(enterpriseId, userID)
.then((response) => {
const results = camelCaseObject(response.data);
if (results.length) {
setLearnerCreditSubsidies(results);
} else {
setLearnerCreditSubsidies(null);
}
})
.catch((error) => {
logError(new Error(error));
setLearnerCreditSubsidies(null);
})
.finally(() => {
setIsLoading(false);
});
}, [enterpriseId, userID]);
return [learnerCreditSubsidies, isLoading];
}
10 changes: 10 additions & 0 deletions src/components/enterprise-user-subsidy/data/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ export function fetchCustomerAgreementData(enterpriseUUID) {
return getAuthenticatedHttpClient().get(url);
}

export function fetchSubsidyLearnerCredit(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);
}

export function requestAutoAppliedLicense(customerAgreementId) {
const config = getConfig();
const url = `${config.LICENSE_MANAGER_URL}/api/v1/customer-agreement/${customerAgreementId}/auto-apply/`;
Expand Down
Loading

0 comments on commit 8d64e38

Please sign in to comment.