Skip to content

Commit

Permalink
🐛(frontend) fix ProductCertificateFooter state issue
Browse files Browse the repository at this point in the history
Currently, if the learner has a submitted or pending order, the label displayed
is the one as it has a validated order. Instead for submitted order, we want to
prevent that the user tries to purchase again the product and with pending order
 the learner should be able to retry to purchase the certificate.
  • Loading branch information
jbpenrath committed Jun 21, 2024
1 parent 747973c commit fcfefde
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 15 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ Versioning](https://semver.org/spec/v2.0.0.html).

## [Unrealeased]

### Fixed

- Fix ProductCertificateFooter component to display proper CTA
according to Order state

## [2.28.0]

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('<ProductCertificateFooter/>', () => {
},
])(
"shouldn't display purchase button for a closed course run without order (state $courseRunStateData.priority).",
async ({ courseRunStateData }) => {
({ courseRunStateData }) => {
render(
<ProductCertificateFooter
product={product}
Expand All @@ -132,7 +132,7 @@ describe('<ProductCertificateFooter/>', () => {
},
);

it('should display download button for a course run with certificate.', async () => {
it('should display download button for a course run with certificate.', () => {
const order = OrderEnrollmentFactory({
certificate_id: 'FAKE_CERTIFICATE_ID',
state: OrderState.VALIDATED,
Expand All @@ -147,11 +147,41 @@ describe('<ProductCertificateFooter/>', () => {
CertificateFactory({ id: order.certificate_id }).one(),
);
render(<ProductCertificateFooter product={product} enrollment={enrollment} />);
expect(await screen.findByRole('button', { name: 'Download' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument();
expect(screen.queryByTestId('PurchaseButton__cta')).not.toBeInTheDocument();
});

it('should not display purchase button for a course run with submitted order.', () => {
const order = OrderEnrollmentFactory({
certificate_id: undefined,
product_id: product.id,
state: OrderState.SUBMITTED,
}).one();
const enrollment = EnrollmentFactory({
orders: [order],
course_run: CourseRunFactory({ course }).one(),
}).one();
render(<ProductCertificateFooter product={product} enrollment={enrollment} />);
expect(screen.queryByRole('button', { name: 'Download' })).not.toBeInTheDocument();
expect(screen.queryByTestId('PurchaseButton__cta')).not.toBeInTheDocument();
});

it('should not display button (download or purchase) for a course run with order but without certificate.', async () => {
it('should display purchase button for a course run with pending order.', () => {
const order = OrderEnrollmentFactory({
certificate_id: undefined,
product_id: product.id,
state: OrderState.PENDING,
}).one();
const enrollment = EnrollmentFactory({
orders: [order],
course_run: CourseRunFactory({ course }).one(),
}).one();
render(<ProductCertificateFooter product={product} enrollment={enrollment} />);
expect(screen.queryByRole('button', { name: 'Download' })).not.toBeInTheDocument();
expect(screen.getByTestId('PurchaseButton__cta')).toBeInTheDocument();
});

it('should not display button (download or purchase) for a course run with order but without certificate.', () => {
const order = OrderEnrollmentFactory({
certificate_id: undefined,
product_id: product.id,
Expand All @@ -161,7 +191,7 @@ describe('<ProductCertificateFooter/>', () => {
course_run: CourseRunFactory({ course }).one(),
}).one();
render(<ProductCertificateFooter product={product} enrollment={enrollment} />);
expect(await screen.queryByRole('button', { name: 'Download' })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Download' })).not.toBeInTheDocument();
expect(screen.queryByTestId('PurchaseButton__cta')).not.toBeInTheDocument();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FormattedMessage, defineMessages } from 'react-intl';
import { useState } from 'react';
import PurchaseButton from 'components/PurchaseButton';
import { Icon, IconTypeEnum } from 'components/Icon';
import { CertificateProduct, Enrollment, ProductType } from 'types/Joanie';
import { CertificateProduct, Enrollment, OrderState, ProductType } from 'types/Joanie';
import DownloadCertificateButton from 'components/DownloadCertificateButton';
import { useCertificate } from 'hooks/useCertificates';
import { isOpenedCourseRunCertificate } from 'utils/CourseRuns';
Expand Down Expand Up @@ -36,22 +36,22 @@ const ProductCertificateFooter = ({ product, enrollment }: ProductCertificateFoo
if (product.type !== ProductType.CERTIFICATE) {
return null;
}
const [activeOrder, setActiveOrder] = useState(
const [order, setOrder] = useState(
OrderHelper.getActiveEnrollmentOrder(enrollment.orders || [], product.id),
);
const { item: certificate } = useCertificate(activeOrder?.certificate_id);
const { item: certificate } = useCertificate(order?.certificate_id);

// The course run is no longer available
// and no product certificate had been bought therefore there isn't any certificate to download.
if (!activeOrder && !isOpenedCourseRunCertificate(enrollment.course_run.state)) {
if (!order && !isOpenedCourseRunCertificate(enrollment.course_run.state)) {
return null;
}

return (
<div className="dashboard-item__course-enrolling__infos">
<div className="dashboard-item__block__status">
<Icon name={IconTypeEnum.CERTIFICATE} />
{activeOrder ? (
{order?.state === OrderState.VALIDATED ? (
<>
{product.certificate_definition.title + '. '}
<CertificateStatus certificate={certificate} productType={product.type} />
Expand All @@ -60,11 +60,11 @@ const ProductCertificateFooter = ({ product, enrollment }: ProductCertificateFoo
<FormattedMessage {...messages.buyProductCertificateLabel} />
)}
</div>
{activeOrder ? (
activeOrder.certificate_id && (
{order?.state === OrderState.VALIDATED ? (
order.certificate_id && (
<DownloadCertificateButton
className="dashboard-item__button"
certificateId={activeOrder.certificate_id}
certificateId={order.certificate_id}
/>
)
) : (
Expand All @@ -73,13 +73,14 @@ const ProductCertificateFooter = ({ product, enrollment }: ProductCertificateFoo
product={product}
enrollment={enrollment}
buttonProps={{ size: 'small' }}
onFinish={(order) => {
disabled={order?.state === OrderState.SUBMITTED}
onFinish={(o) => {
/**
* As we do not refetch enrollments in DashboardCourses after SaleTunnel cache invalidation (to avoid
* scroll reset - and SaleTunnel modal unmounting too early caused by list reset) we need to manually
* update the active order in the enrollment in order to hide the buy button and display the download button.
*/
setActiveOrder(order);
setOrder(o);
}}
/>
)}
Expand Down

0 comments on commit fcfefde

Please sign in to comment.