Skip to content

Commit

Permalink
feat: Show one card on learner credit screen (#1022)
Browse files Browse the repository at this point in the history
  • Loading branch information
zamanafzal authored Aug 24, 2023
1 parent 4266584 commit 9af4648
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 48 deletions.
163 changes: 163 additions & 0 deletions src/components/learner-credit-management/BudgetCard-V2.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import {
Card,
Button,
Stack,
Row,
Col,
Breadcrumb,
} from '@edx/paragon';

import { useOfferRedemptions, useOfferSummary } from './data/hooks';
import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';

const BudgetCard = ({
offer,
enterpriseUUID,
enterpriseSlug,
}) => {
const {
start,
end,
} = offer;

const {
isLoading: isLoadingOfferSummary,
offerSummary,
} = useOfferSummary(enterpriseUUID, offer);

const {
isLoading: isLoadingOfferRedemptions,
offerRedemptions,
fetchOfferRedemptions,
} = useOfferRedemptions(enterpriseUUID, offer?.id);
const [detailPage, setDetailPage] = useState(false);
const [activeLabel, setActiveLabel] = useState('');
const links = [
{ label: 'Budgets', url: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
];
const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
const navigateToBudgetRedemptions = (budgetType) => {
setDetailPage(true);
links.push({ label: budgetType, url: `/${enterpriseSlug}/admin/learner-credit` });
setActiveLabel(budgetType);
};

const renderActions = (budgetType) => (
<Button
data-testid="view-budget"
onClick={() => navigateToBudgetRedemptions(budgetType)}
>
View Budget
</Button>
);

const renderCardHeader = (budgetType) => {
const subtitle = (
<div className="d-flex flex-wrap align-items-center">
<span data-testid="offer-date">
{formattedStartDate} - {formattedExpirationDate}
</span>
</div>
);

return (
<Card.Header
title={budgetType}
subtitle={subtitle}
actions={(
<div>
{renderActions(budgetType)}
</div>
)}
/>
);
};

const renderCardSection = (available, spent) => (
<Card.Section
title="Balance"
muted
>
<Row className="d-flex flex-row justify-content-start w-md-75">
<Col xs="6" md="auto" className="d-flex flex-column mb-3 mb-md-0">
<span className="small">Available</span>
<span>{available}</span>
</Col>
<Col xs="6" md="auto" className="d-flex flex-column mb-3 mb-md-0">
<span className="small">Spent</span>
<span>{spent}</span>
</Col>
</Row>
</Card.Section>
);

const renderCardAggregate = () => (
<div className="mb-4.5 d-flex flex-wrap mx-n3">
<LearnerCreditAggregateCards
isLoading={isLoadingOfferSummary}
totalFunds={offerSummary?.totalFunds}
redeemedFunds={offerSummary?.redeemedFunds}
remainingFunds={offerSummary?.remainingFunds}
percentUtilized={offerSummary?.percentUtilized}
/>
</div>
);

return (
<Stack>
<Row className="m-3">
<Col xs="12">
<Breadcrumb
ariaLabel="Breadcrumb"
links={links}
activeLabel={activeLabel}
/>
</Col>
</Row>
{!detailPage
? (
<>
{renderCardAggregate()}
<h2>Budgets</h2>
<Card
orientation="horizontal"
>
<Card.Body>
<Stack gap={4}>
{renderCardHeader('Overview')}
{renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFunds)}
</Stack>
</Card.Body>
</Card>
</>
)
: (
<LearnerCreditAllocationTable
isLoading={isLoadingOfferRedemptions}
tableData={offerRedemptions}
fetchTableData={fetchOfferRedemptions}
enterpriseUUID={enterpriseUUID}
/>
)}
</Stack>
);
};

BudgetCard.propTypes = {
offer: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
start: PropTypes.string.isRequired,
end: PropTypes.string.isRequired,
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
};

export default BudgetCard;
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ const LearnerCreditAllocationTable = ({
tableData,
fetchTableData,
enterpriseUUID,
budgetType,
}) => {
const isDesktopTable = useMediaQuery({ minWidth: breakpoints.extraLarge.minWidth });
const defaultFilter = budgetType ? [{ id: 'courseProductLine', value: budgetType }] : [];
const defaultFilter = [];

return (
<DataTable
Expand Down Expand Up @@ -90,9 +89,6 @@ const LearnerCreditAllocationTable = ({
/>
);
};
LearnerCreditAllocationTable.defaultProps = {
budgetType: null,
};

LearnerCreditAllocationTable.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
Expand All @@ -109,7 +105,6 @@ LearnerCreditAllocationTable.propTypes = {
pageCount: PropTypes.number.isRequired,
}).isRequired,
fetchTableData: PropTypes.func.isRequired,
budgetType: PropTypes.string,
};

export default LearnerCreditAllocationTable;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Col,
} from '@edx/paragon';

import BudgetCard from './BudgetCard';
import BudgetCard from './BudgetCard-V2';

const MultipleBudgetsPicker = ({
offers,
Expand Down
44 changes: 3 additions & 41 deletions src/components/learner-credit-management/tests/BudgetCard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import '@testing-library/jest-dom/extend-expect';

import { IntlProvider } from '@edx/frontend-platform/i18n';
import BudgetCard from '../BudgetCard';
import BudgetCard from '../BudgetCard-V2';
import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
import { EXEC_ED_OFFER_TYPE } from '../data/constants';

Expand Down Expand Up @@ -69,45 +69,7 @@ describe('<BudgetCard />', () => {
jest.clearAllMocks();
});

it('displays correctly for all offers', () => {
const mockOffer = {
id: mockEnterpriseOfferId,
name: mockOfferDisplayName,
start: '2022-01-01',
end: '2023-01-01',
};
const mockOfferRedemption = {
created: '2022-02-01',
enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
};
useOfferSummary.mockReturnValue({
isLoading: false,
offerSummary: mockOfferSummary,
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
offerRedemptions: {
results: [mockOfferRedemption],
itemCount: 1,
pageCount: 1,
},
fetchOfferRedemptions: jest.fn(),
});
render(<BudgetCardWrapper
offer={mockOffer}
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseId}
/>);
expect(screen.getByText('Open Courses Marketplace'));
expect(screen.getByText('Executive Education'));
expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
const elementsWithTestId = screen.getAllByTestId('offer-date');
const firstElementWithTestId = elementsWithTestId[0];
expect(firstElementWithTestId).toHaveTextContent(formattedString);
});

it('displays correctly for Offer type Site', () => {
it('displays correctly for Offers', () => {
const mockOffer = {
id: mockEnterpriseOfferId,
name: mockOfferDisplayName,
Expand Down Expand Up @@ -142,7 +104,7 @@ describe('<BudgetCard />', () => {
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseId}
/>);
expect(screen.getByText('Open Courses Marketplace'));
expect(screen.getByText('Overview'));
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
Expand Down

0 comments on commit 9af4648

Please sign in to comment.