richie-education 2.28.2-dev39 → 2.28.2-dev58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/.eslintrc.json +11 -2
  2. package/i18n/locales/ar-SA.json +209 -125
  3. package/i18n/locales/es-ES.json +210 -126
  4. package/i18n/locales/fa-IR.json +209 -125
  5. package/i18n/locales/fr-CA.json +209 -125
  6. package/i18n/locales/fr-FR.json +209 -125
  7. package/i18n/locales/ko-KR.json +209 -125
  8. package/i18n/locales/pt-PT.json +212 -128
  9. package/i18n/locales/ru-RU.json +209 -125
  10. package/i18n/locales/vi-VN.json +209 -125
  11. package/js/api/joanie.ts +14 -17
  12. package/js/api/lms/dummy.ts +1 -12
  13. package/js/components/ContractFrame/AbstractContractFrame.spec.tsx +16 -9
  14. package/js/components/ContractFrame/AbstractContractFrame.tsx +32 -25
  15. package/js/components/ContractFrame/LearnerContractFrame.tsx +2 -2
  16. package/js/components/ContractFrame/_styles.scss +6 -14
  17. package/js/components/CreditCardSelector/index.spec.tsx +7 -7
  18. package/js/components/CreditCardSelector/index.tsx +2 -2
  19. package/js/components/DownloadContractButton/index.spec.tsx +1 -1
  20. package/js/components/OpenEdxFullNameForm/index.spec.tsx +229 -0
  21. package/js/components/OpenEdxFullNameForm/index.tsx +7 -7
  22. package/js/components/PaymentInterfaces/LyraPopIn.tsx +2 -2
  23. package/js/components/PaymentInterfaces/PayplugLightbox.tsx +1 -1
  24. package/js/components/PaymentInterfaces/__mocks__/index.tsx +1 -4
  25. package/js/components/PaymentInterfaces/types.ts +5 -2
  26. package/js/components/PurchaseButton/index.spec.tsx +69 -37
  27. package/js/components/SaleTunnel/AddressSelector/index.spec.tsx +2 -1
  28. package/js/components/SaleTunnel/CertificateSaleTunnel/index.tsx +2 -2
  29. package/js/components/SaleTunnel/CredentialSaleTunnel/index.tsx +6 -10
  30. package/js/components/SaleTunnel/GenericSaleTunnel.tsx +75 -41
  31. package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +0 -30
  32. package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/_styles.scss +12 -0
  33. package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/index.tsx +160 -0
  34. package/js/components/SaleTunnel/SaleTunnelSuccess/index.tsx +15 -29
  35. package/js/components/SaleTunnel/Sponsors/SaleTunnelSponsors.tsx +5 -0
  36. package/js/components/SaleTunnel/SubscriptionButton/_styles.scss +7 -0
  37. package/js/components/SaleTunnel/SubscriptionButton/index.tsx +202 -0
  38. package/js/components/SaleTunnel/_styles.scss +10 -1
  39. package/js/components/SaleTunnel/hooks/useTerms.tsx +0 -77
  40. package/js/components/SaleTunnel/index.credential.spec.tsx +12 -21
  41. package/js/components/SaleTunnel/index.full-process.spec.tsx +110 -48
  42. package/js/components/SaleTunnel/index.spec.tsx +330 -779
  43. package/js/components/SignContractButton/index.omniscientOrders.spec.tsx +16 -11
  44. package/js/components/SignContractButton/index.spec.tsx +16 -20
  45. package/js/components/SignContractButton/index.tsx +3 -1
  46. package/js/hooks/useCreditCards/index.spec.tsx +70 -6
  47. package/js/hooks/useCreditCards/index.ts +49 -11
  48. package/js/hooks/useOrders/index.spec.tsx +322 -0
  49. package/js/hooks/{useOrders.ts → useOrders/index.ts} +40 -14
  50. package/js/hooks/useProductOrder/index.spec.tsx +77 -60
  51. package/js/hooks/useProductOrder/index.tsx +2 -2
  52. package/js/hooks/useResources/useResourcesRoot.ts +1 -0
  53. package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.spec.tsx +1 -1
  54. package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.tsx +4 -2
  55. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +8 -5
  56. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +8 -9
  57. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.spec.tsx +1 -1
  58. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +1 -6
  59. package/js/settings/settings.test.ts +11 -2
  60. package/js/translations/ar-SA.json +1 -1
  61. package/js/translations/es-ES.json +1 -1
  62. package/js/translations/fa-IR.json +1 -1
  63. package/js/translations/fr-CA.json +1 -1
  64. package/js/translations/fr-FR.json +1 -1
  65. package/js/translations/ko-KR.json +1 -1
  66. package/js/translations/pt-PT.json +1 -1
  67. package/js/translations/ru-RU.json +1 -1
  68. package/js/translations/vi-VN.json +1 -1
  69. package/js/types/Joanie.ts +49 -34
  70. package/js/utils/OrderHelper/index.ts +38 -42
  71. package/js/utils/search/getSuggestionsSection/index.spec.ts +3 -2
  72. package/js/utils/test/factories/joanie.ts +36 -51
  73. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +8 -18
  74. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +26 -32
  75. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +11 -6
  76. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +7 -6
  77. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +9 -10
  78. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.spec.tsx +3 -1
  79. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +6 -7
  80. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/index.tsx +28 -8
  81. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +4 -6
  82. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.spec.tsx +18 -71
  83. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.tsx +34 -35
  84. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +27 -24
  85. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.spec.tsx +18 -73
  86. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.tsx +32 -16
  87. package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +3 -11
  88. package/js/widgets/Dashboard/components/Signature/SignatureDummy.tsx +25 -3
  89. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/EnrollableCourseRunList.tsx +2 -6
  90. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/index.spec.tsx +7 -14
  91. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.spec.tsx +7 -5
  92. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.tsx +5 -7
  93. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +242 -332
  94. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +12 -13
  95. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +10 -21
  96. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.joanie.spec.tsx +2 -2
  97. package/package.json +27 -27
  98. package/scss/components/_index.scss +2 -1
  99. package/js/components/PaymentButton/_styles.scss +0 -27
  100. package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +0 -333
  101. package/js/components/SaleTunnel/SaleTunnelNotValidated/index.tsx +0 -70
  102. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader/index.tsx +0 -41
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  Alert,
3
3
  Button,
4
+ Loader,
4
5
  Modal,
5
6
  ModalProps,
6
7
  ModalSize,
@@ -8,12 +9,14 @@ import {
8
9
  VariantType,
9
10
  } from '@openfun/cunningham-react';
10
11
  import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
11
- import { useState } from 'react';
12
+ import { useState, useEffect } from 'react';
12
13
  import { PaymentScheduleGrid } from 'components/PaymentScheduleGrid';
13
14
  import { CreditCard, Order } from 'types/Joanie';
14
15
  import { CreditCardSelector } from 'components/CreditCardSelector';
15
16
  import { OrderHelper } from 'utils/OrderHelper';
16
17
  import { OrderPaymentRetryModal } from 'widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal';
18
+ import { Maybe } from 'types/utils';
19
+ import { useCreditCard } from 'hooks/useCreditCards';
17
20
 
18
21
  const messages = defineMessages({
19
22
  title: {
@@ -57,7 +60,7 @@ export const OrderPaymentDetailsModal = ({ order, ...props }: PaymentModalProps)
57
60
  <h3 className="order-payment-details__title mb-s">
58
61
  <FormattedMessage {...messages.paymentMethodTitle} />
59
62
  </h3>
60
- <CreditCardSelectorWrapper />
63
+ <CreditCardSelectorWrapper selectedCreditCardId={order.credit_card_id} />
61
64
  <h3 className="order-payment-details__title mb-s mt-b">
62
65
  <FormattedMessage {...messages.scheduleTitle} />
63
66
  </h3>
@@ -91,14 +94,31 @@ export const OrderPaymentDetailsModal = ({ order, ...props }: PaymentModalProps)
91
94
  );
92
95
  };
93
96
 
94
- const CreditCardSelectorWrapper = () => {
95
- // TODO: At the moment is automatically selects the default credit card but it must select the credit card used to
96
- // buy the order.
97
- const [creditCard, setCreditCard] = useState<CreditCard>();
97
+ const CreditCardSelectorWrapper = ({
98
+ selectedCreditCardId,
99
+ }: {
100
+ selectedCreditCardId: Maybe<string>;
101
+ }) => {
102
+ const {
103
+ item: creditCard,
104
+ states: { fetching },
105
+ } = useCreditCard(selectedCreditCardId);
106
+ const [selectedCreditCard, setSelectedCreditCard] = useState<Maybe<CreditCard>>(creditCard);
107
+
108
+ useEffect(() => {
109
+ if (!selectedCreditCard && creditCard) {
110
+ setSelectedCreditCard(creditCard);
111
+ }
112
+ }, [creditCard]);
113
+
114
+ if (fetching) {
115
+ return <Loader size="small" />;
116
+ }
117
+
98
118
  return (
99
119
  <CreditCardSelector
100
- creditCard={creditCard}
101
- setCreditCard={setCreditCard}
120
+ creditCard={selectedCreditCard || creditCard}
121
+ setCreditCard={setSelectedCreditCard}
102
122
  quickRemove={false}
103
123
  allowEdit={false}
104
124
  />
@@ -9,7 +9,7 @@ import {
9
9
  } from '@openfun/cunningham-react';
10
10
  import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
11
11
  import { useRef, useState } from 'react';
12
- import { CreditCard, Order, PaymentInstallment, OrderState } from 'types/Joanie';
12
+ import { CreditCard, Order, PaymentInstallment, ACTIVE_ORDER_STATES } from 'types/Joanie';
13
13
  import { CreditCardSelector } from 'components/CreditCardSelector';
14
14
  import { useJoanieApi } from 'contexts/JoanieApiContext';
15
15
  import { Payment, PaymentErrorMessageId } from 'components/PaymentInterfaces/types';
@@ -98,7 +98,8 @@ export const OrderPaymentRetryModal = ({ installment, order, ...props }: Props)
98
98
  setError(intl.formatMessage(messages.errorFailedSubmitInstallmentPayment));
99
99
  setState(ComponentStates.ERROR);
100
100
  }
101
- } catch (e) {
101
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
102
+ } catch (_error) {
102
103
  setError(intl.formatMessage(messages.errorFailedSubmitInstallmentPayment));
103
104
  setState(ComponentStates.ERROR);
104
105
  }
@@ -111,10 +112,7 @@ export const OrderPaymentRetryModal = ({ installment, order, ...props }: Props)
111
112
 
112
113
  const isOrderValidated = async (id: string): Promise<Boolean> => {
113
114
  const orderToCheck = await API.user.orders.get({ id });
114
- return (
115
- orderToCheck?.state === OrderState.VALIDATED ||
116
- orderToCheck?.state === OrderState.PENDING_PAYMENT
117
- );
115
+ return orderToCheck !== null && ACTIVE_ORDER_STATES.includes(orderToCheck.state);
118
116
  };
119
117
 
120
118
  const settled = async () => {
@@ -1,11 +1,7 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
2
  import { screen } from '@testing-library/react';
3
3
  import { createIntl } from 'react-intl';
4
- import {
5
- ContractDefinitionFactory,
6
- ContractFactory,
7
- CredentialOrderFactory,
8
- } from 'utils/test/factories/joanie';
4
+ import { CredentialOrderFactory } from 'utils/test/factories/joanie';
9
5
  import { OrderState } from 'types/Joanie';
10
6
  import { render } from 'utils/test/render';
11
7
  import { IntlWrapper } from 'utils/test/wrappers/IntlWrapper';
@@ -13,86 +9,37 @@ import OrderStateLearnerMessage, { messages } from '.';
13
9
 
14
10
  const intl = createIntl({ locale: 'en' });
15
11
 
16
- describe('<DashboardItemOrder/>', () => {
12
+ describe('<OrderStateLearnerMessage/>', () => {
17
13
  it.each([
18
- [OrderState.DRAFT, 'Draft'],
19
- [OrderState.SUBMITTED, 'Submitted'],
20
- [OrderState.PENDING, 'Pending'],
14
+ [OrderState.ASSIGNED, 'Pending'],
21
15
  [OrderState.CANCELED, 'Canceled'],
22
- ])(
23
- 'should display message from order state: %s when order have no contract',
24
- (state, expectedMessage) => {
25
- const order = CredentialOrderFactory({ state }).one();
26
- render(<OrderStateLearnerMessage order={order} />, {
27
- wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
28
- });
29
- expect(screen.getByText(expectedMessage)).toBeInTheDocument();
30
- },
31
- );
32
-
33
- it.each([
34
- [OrderState.DRAFT, 'Draft'],
35
- [OrderState.SUBMITTED, 'Submitted'],
36
- [OrderState.PENDING, 'Pending'],
37
- [OrderState.CANCELED, 'Canceled'],
38
- ])(
39
- 'should display message from order state: %s when order have no contract',
40
- (state, expectedMessage) => {
41
- const orderWithContract = CredentialOrderFactory({
42
- state,
43
- contract: ContractFactory().one(),
44
- }).one();
45
- render(<OrderStateLearnerMessage order={orderWithContract} />, {
46
- wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
47
- });
48
- expect(screen.getByText(expectedMessage)).toBeInTheDocument();
49
- },
50
- );
51
-
52
- it('should display message for validated order that need learner signature', () => {
53
- const order = CredentialOrderFactory({
54
- state: OrderState.VALIDATED,
55
- contract: null,
56
- }).one();
57
-
58
- const contractDefinition = ContractDefinitionFactory().one();
59
-
60
- render(<OrderStateLearnerMessage order={order} contractDefinition={contractDefinition} />, {
61
- wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
62
- });
63
- expect(screen.getByText('Signature required')).toBeInTheDocument();
64
- });
65
-
66
- it("should display message for validated order that don't have a generated certificate", () => {
67
- const order = CredentialOrderFactory({
68
- state: OrderState.VALIDATED,
69
- contract: ContractFactory({ student_signed_on: new Date().toISOString() }).one(),
70
- certificate_id: undefined,
71
- }).one();
16
+ [OrderState.COMPLETED, 'On going'],
17
+ [OrderState.DRAFT, 'Pending'],
18
+ [OrderState.FAILED_PAYMENT, 'Last direct debit has failed'],
19
+ [OrderState.NO_PAYMENT, 'First direct debit has failed'],
20
+ [OrderState.PENDING, 'Pending for the first direct debit'],
21
+ [OrderState.PENDING_PAYMENT, 'On going'],
22
+ [OrderState.SIGNING, 'Signature required'],
23
+ [OrderState.TO_SAVE_PAYMENT_METHOD, 'Payment method is missing'],
24
+ [OrderState.TO_SIGN, 'Signature required'],
25
+ ])('should display message from order state: %s', (state, expectedMessage) => {
26
+ const order = CredentialOrderFactory({ state }).one();
72
27
  render(<OrderStateLearnerMessage order={order} />, {
73
28
  wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
74
29
  });
75
- expect(
76
- screen.getByText(intl.formatMessage(messages.statusOnGoing), {
77
- exact: false,
78
- }),
79
- );
30
+ expect(screen.getByText(expectedMessage)).toBeInTheDocument();
80
31
  });
81
32
 
82
- it('should display message for validated order that have a generated certificate', () => {
33
+ it('should display message for completed order that have a generated certificate', () => {
83
34
  const order = CredentialOrderFactory({
84
- state: OrderState.VALIDATED,
85
- contract: ContractFactory({
86
- student_signed_on: new Date().toISOString(),
87
- organization_signed_on: new Date().toISOString(),
88
- }).one(),
35
+ state: OrderState.COMPLETED,
89
36
  certificate_id: 'FAKE_CERTIFICATE_ID',
90
37
  }).one();
91
38
  render(<OrderStateLearnerMessage order={order} />, {
92
39
  wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
93
40
  });
94
41
  expect(
95
- screen.getByText(intl.formatMessage(messages.statusCompleted), {
42
+ screen.getByText(intl.formatMessage(messages.statusPassed), {
96
43
  exact: false,
97
44
  }),
98
45
  );
@@ -1,71 +1,70 @@
1
1
  import { defineMessages } from 'react-intl';
2
- import OrderStateMessage, { OrderStateMessageBaseProps } from '../OrderStateMessage';
2
+ import OrderStateMessage, { OrderStateMessageBaseProps, MessageKeys } from '../OrderStateMessage';
3
3
 
4
- export const messages = defineMessages({
4
+ export const messages = defineMessages<MessageKeys>({
5
5
  statusDraft: {
6
- id: 'components.DashboardItem.Order.OrderStateMessage.statusDraft',
6
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusDraft',
7
7
  description: 'Status shown on the dashboard order item when order is draft.',
8
- defaultMessage: 'Draft',
8
+ defaultMessage: 'Pending',
9
9
  },
10
- statusSubmitted: {
11
- id: 'components.DashboardItem.Order.OrderStateMessage.statusSubmitted',
12
- description: 'Status shown on the dashboard order item when order is submitted.',
13
- defaultMessage: 'Submitted',
10
+ statusAssigned: {
11
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusAssigned',
12
+ description: 'Status shown on the dashboard order item when order is assigned.',
13
+ defaultMessage: 'Pending',
14
14
  },
15
15
  statusPending: {
16
- id: 'components.DashboardItem.Order.OrderStateMessage.statusPending',
16
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusPending',
17
17
  description: 'Status shown on the dashboard order item when order is pending.',
18
- defaultMessage: 'Pending',
18
+ defaultMessage: 'Pending for the first direct debit',
19
19
  },
20
- statusOnGoing: {
21
- id: 'components.DashboardItem.Order.OrderStateMessage.statusOnGoing',
22
- description:
23
- 'Status shown on the dashboard order item when order is validated with no certificate',
20
+ statusPendingPayment: {
21
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusPendingPayment',
22
+ description: 'Status shown on the dashboard order item when order is pending for payment',
24
23
  defaultMessage: 'On going',
25
24
  },
26
25
  statusCompleted: {
27
- id: 'components.DashboardItem.Order.OrderStateMessage.statusCompleted',
28
- description:
29
- 'Status shown on the dashboard order item when order is validated with certificate',
30
- defaultMessage: 'Completed',
31
- },
32
- statusPendingPayment: {
33
- id: 'components.DashboardItem.Order.OrderStateMessage.statusPendingPayment',
34
- description:
35
- 'Status shown on the dashboard order item when order is validated with certificate and pending payment',
36
- defaultMessage: 'Completed',
26
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusCompleted',
27
+ description: 'Status shown on the dashboard order item when order is completed',
28
+ defaultMessage: 'On going',
37
29
  },
38
30
  statusWaitingSignature: {
39
- id: 'components.DashboardItem.Order.OrderStateMessage.statusWaitingSignature',
31
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusWaitingSignature',
40
32
  description:
41
33
  "Status shown on the dashboard order item when order is validated with contract's learner signature missing.",
42
34
  defaultMessage: 'Signature required',
43
35
  },
44
36
  statusWaitingCounterSignature: {
45
- id: 'components.DashboardItem.Order.OrderStateMessage.statusWaitingCounterSignature',
37
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusWaitingCounterSignature',
46
38
  description:
47
39
  "Status shown on the dashboard order item when order is validated with contract's organization signature missing.",
48
40
  defaultMessage: 'On going',
49
41
  },
42
+ statusWaitingPaymentMethod: {
43
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusWaitingPaymentMethod',
44
+ description:
45
+ 'Status shown on the dashboard order item when order is in to_save_payment_method state.',
46
+ defaultMessage: 'Payment method is missing',
47
+ },
50
48
  statusCanceled: {
51
- id: 'components.DashboardItem.Order.OrderStateMessage.statusCanceled',
49
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusCanceled',
52
50
  description: 'Status shown on the dashboard order item when order is canceled',
53
51
  defaultMessage: 'Canceled',
54
52
  },
55
53
  statusNoPayment: {
56
- id: 'components.DashboardItem.Order.OrderStateMessage.statusNoPayment',
54
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusNoPayment',
57
55
  description: 'Status shown on the dashboard order item when order is in no payment state',
58
- defaultMessage: 'Failed payment',
56
+ defaultMessage: 'First direct debit has failed',
59
57
  },
60
58
  statusFailedPayment: {
61
- id: 'components.DashboardItem.Order.OrderStateMessage.statusFailedPayment',
59
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusFailedPayment',
62
60
  description: 'Status shown on the dashboard order item when order is in failed payment state',
63
- defaultMessage: 'Failed payment',
61
+ defaultMessage: 'Last direct debit has failed',
64
62
  },
65
- statusOther: {
66
- id: 'components.DashboardItem.Order.OrderStateMessage.statusOther',
67
- description: 'Status shown on the dashboard order item when order status is unknown',
68
- defaultMessage: '{state}',
63
+ statusPassed: {
64
+ id: 'components.DashboardItem.Order.OrderStateLearnerMessage.statusPassed',
65
+ description:
66
+ 'Status shown on the dashboard order item when order is completed and has a certificate',
67
+ defaultMessage: 'Successfully completed',
69
68
  },
70
69
  });
71
70
 
@@ -1,26 +1,33 @@
1
1
  import { FormattedMessage, MessageDescriptor } from 'react-intl';
2
2
  import { useEffect } from 'react';
3
- import {
4
- CertificateOrder,
5
- CredentialOrder,
6
- OrderState,
7
- ContractDefinition,
8
- NestedCourseOrder,
9
- } from 'types/Joanie';
3
+ import { CertificateOrder, CredentialOrder, OrderState, NestedCourseOrder } from 'types/Joanie';
10
4
  import { StringHelper } from 'utils/StringHelper';
11
5
  import { handle } from 'utils/errors/handle';
12
6
  import { OrderHelper, OrderStatus } from 'utils/OrderHelper';
13
7
 
14
8
  export interface OrderStateMessageBaseProps {
15
9
  order: CredentialOrder | CertificateOrder | NestedCourseOrder;
16
- contractDefinition?: ContractDefinition;
17
10
  }
18
11
 
12
+ export type MessageKeys =
13
+ | 'statusDraft'
14
+ | 'statusAssigned'
15
+ | 'statusPending'
16
+ | 'statusPendingPayment'
17
+ | 'statusCompleted'
18
+ | 'statusWaitingSignature'
19
+ | 'statusWaitingCounterSignature'
20
+ | 'statusWaitingPaymentMethod'
21
+ | 'statusCanceled'
22
+ | 'statusNoPayment'
23
+ | 'statusFailedPayment'
24
+ | 'statusPassed';
25
+
19
26
  interface OrderStateMessageProps extends OrderStateMessageBaseProps {
20
- messages: Record<string, MessageDescriptor>;
27
+ messages: Record<MessageKeys, MessageDescriptor>;
21
28
  }
22
29
 
23
- const OrderStateMessage = ({ order, contractDefinition, messages }: OrderStateMessageProps) => {
30
+ const OrderStateMessage = ({ order, messages }: OrderStateMessageProps) => {
24
31
  useEffect(() => {
25
32
  if (!Object.values(OrderState).includes(order.state)) {
26
33
  handle(new Error(`Unknown order state ${order.state}`));
@@ -28,27 +35,23 @@ const OrderStateMessage = ({ order, contractDefinition, messages }: OrderStateMe
28
35
  }, [order.state]);
29
36
 
30
37
  const orderStatusMessagesMap = {
31
- [OrderStatus.DRAFT]: messages.statusDraft,
32
- [OrderStatus.SUBMITTED]: messages.statusSubmitted,
33
- [OrderStatus.PENDING]: messages.statusPending,
38
+ [OrderStatus.ASSIGNED]: messages.statusAssigned,
34
39
  [OrderStatus.CANCELED]: messages.statusCanceled,
35
- [OrderStatus.WAITING_SIGNATURE]: messages.statusWaitingSignature,
36
- [OrderStatus.WAITING_COUNTER_SIGNATURE]: messages.statusWaitingCounterSignature,
40
+ [OrderStatus.DRAFT]: messages.statusDraft,
37
41
  [OrderStatus.COMPLETED]: messages.statusCompleted,
38
- [OrderStatus.ON_GOING]: messages.statusOnGoing,
42
+ [OrderStatus.FAILED_PAYMENT]: messages.statusFailedPayment,
39
43
  [OrderStatus.NO_PAYMENT]: messages.statusNoPayment,
44
+ [OrderStatus.PASSED]: messages.statusPassed,
45
+ [OrderStatus.PENDING]: messages.statusPending,
40
46
  [OrderStatus.PENDING_PAYMENT]: messages.statusPendingPayment,
41
- [OrderStatus.FAILED_PAYMENT]: messages.statusFailedPayment,
47
+ [OrderStatus.WAITING_COUNTER_SIGNATURE]: messages.statusWaitingCounterSignature,
48
+ [OrderStatus.WAITING_PAYMENT_METHOD]: messages.statusWaitingPaymentMethod,
49
+ [OrderStatus.WAITING_SIGNATURE]: messages.statusWaitingSignature,
42
50
  };
43
- const status = OrderHelper.getState(order, contractDefinition);
51
+ const status = OrderHelper.getState(order);
44
52
 
45
53
  if (status === null) {
46
- return (
47
- <FormattedMessage
48
- {...messages.statusOther}
49
- values={{ state: StringHelper.capitalizeFirst(order.state) }}
50
- />
51
- );
54
+ return StringHelper.capitalizeFirst(order.state);
52
55
  }
53
56
 
54
57
  return <FormattedMessage {...orderStatusMessagesMap[status]} />;
@@ -1,11 +1,7 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
2
  import { screen } from '@testing-library/react';
3
3
  import { createIntl } from 'react-intl';
4
- import {
5
- ContractDefinitionFactory,
6
- ContractFactory,
7
- CredentialOrderFactory,
8
- } from 'utils/test/factories/joanie';
4
+ import { ContractFactory, CredentialOrderFactory } from 'utils/test/factories/joanie';
9
5
  import { OrderState } from 'types/Joanie';
10
6
  import { render } from 'utils/test/render';
11
7
  import { IntlWrapper } from 'utils/test/wrappers/IntlWrapper';
@@ -15,100 +11,49 @@ const intl = createIntl({ locale: 'en' });
15
11
 
16
12
  describe('<OrderStateTeacherMessage/>', () => {
17
13
  it.each([
18
- [OrderState.DRAFT, 'Pending'],
19
- [OrderState.SUBMITTED, 'Pending'],
20
- [OrderState.PENDING, 'Pending'],
14
+ [OrderState.ASSIGNED, 'Pending'],
21
15
  [OrderState.CANCELED, 'Canceled'],
22
- ])(
23
- 'should display message from order state: %s when order have no contract',
24
- (state, expectedMessage) => {
25
- const order = CredentialOrderFactory({ state }).one();
26
- render(<OrderStateTeacherMessage order={order} />, {
27
- wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
28
- });
29
- expect(screen.getByText(expectedMessage)).toBeInTheDocument();
30
- },
31
- );
32
-
33
- it.each([
16
+ [OrderState.COMPLETED, 'On going'],
34
17
  [OrderState.DRAFT, 'Pending'],
35
- [OrderState.SUBMITTED, 'Pending'],
36
- [OrderState.PENDING, 'Pending'],
37
- [OrderState.CANCELED, 'Canceled'],
38
- ])(
39
- 'should display message from order state: %s when order have contract',
40
- (state, expectedMessage) => {
41
- const orderWithContract = CredentialOrderFactory({
42
- state,
43
- contract: ContractFactory().one(),
44
- }).one();
45
- render(
46
- <OrderStateTeacherMessage
47
- order={orderWithContract}
48
- contractDefinition={ContractDefinitionFactory().one()}
49
- />,
50
- {
51
- wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
52
- },
53
- );
54
- expect(screen.getByText(expectedMessage)).toBeInTheDocument();
55
- },
56
- );
57
-
58
- it('should display message for validated order that need learner signature', () => {
59
- const order = CredentialOrderFactory({
60
- state: OrderState.VALIDATED,
61
- contract: null,
62
- }).one();
63
-
64
- const contractDefinition = ContractDefinitionFactory().one();
65
-
66
- render(<OrderStateTeacherMessage order={order} contractDefinition={contractDefinition} />, {
18
+ [OrderState.FAILED_PAYMENT, 'Last direct debit has failed'],
19
+ [OrderState.NO_PAYMENT, 'First direct debit has failed'],
20
+ [OrderState.PENDING, 'Pending for the first direct debit'],
21
+ [OrderState.PENDING_PAYMENT, 'On going'],
22
+ [OrderState.SIGNING, "Pending for learner's signature"],
23
+ [OrderState.TO_SAVE_PAYMENT_METHOD, 'Payment method is missing'],
24
+ [OrderState.TO_SIGN, "Pending for learner's signature"],
25
+ ])('should display message from order state: %s', (state, expectedMessage) => {
26
+ const order = CredentialOrderFactory({ state }).one();
27
+ render(<OrderStateTeacherMessage order={order} />, {
67
28
  wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
68
29
  });
69
- expect(screen.getByText("Pending for learner's signature")).toBeInTheDocument();
30
+ expect(screen.getByText(expectedMessage)).toBeInTheDocument();
70
31
  });
71
32
 
72
33
  it('should display message for validated order that need organization signature', () => {
73
34
  const order = CredentialOrderFactory({
74
- state: OrderState.VALIDATED,
35
+ state: OrderState.PENDING_PAYMENT,
75
36
  contract: ContractFactory({
76
37
  student_signed_on: new Date().toISOString(),
77
38
  }).one(),
78
39
  }).one();
79
- const contractDefinition = ContractDefinitionFactory().one();
80
-
81
- render(<OrderStateTeacherMessage order={order} contractDefinition={contractDefinition} />, {
82
- wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
83
- });
84
- expect(screen.getByText('To be signed')).toBeInTheDocument();
85
- });
86
40
 
87
- it("should display message for validated order that don't have a generated certificate", () => {
88
- const order = CredentialOrderFactory({
89
- state: OrderState.VALIDATED,
90
- certificate_id: undefined,
91
- }).one();
92
41
  render(<OrderStateTeacherMessage order={order} />, {
93
42
  wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
94
43
  });
95
- expect(
96
- screen.getByText(intl.formatMessage(messages.statusOnGoing), {
97
- exact: false,
98
- }),
99
- );
44
+ expect(screen.getByText('To be signed')).toBeInTheDocument();
100
45
  });
101
46
 
102
47
  it('should display message for validated order that have a generated certificate', () => {
103
48
  const order = CredentialOrderFactory({
104
- state: OrderState.VALIDATED,
49
+ state: OrderState.COMPLETED,
105
50
  certificate_id: 'FAKE_CERTIFICATE_ID',
106
51
  }).one();
107
52
  render(<OrderStateTeacherMessage order={order} />, {
108
53
  wrapper: ({ children }: PropsWithChildren) => <IntlWrapper>{children}</IntlWrapper>,
109
54
  });
110
55
  expect(
111
- screen.getByText(intl.formatMessage(messages.statusCompleted), {
56
+ screen.getByText(intl.formatMessage(messages.statusPassed), {
112
57
  exact: false,
113
58
  }),
114
59
  );
@@ -1,33 +1,32 @@
1
1
  import { defineMessages } from 'react-intl';
2
- import OrderStateMessage, { OrderStateMessageBaseProps } from '../OrderStateMessage';
2
+ import OrderStateMessage, { MessageKeys, OrderStateMessageBaseProps } from '../OrderStateMessage';
3
3
 
4
- export const messages = defineMessages({
4
+ export const messages = defineMessages<MessageKeys>({
5
5
  statusDraft: {
6
6
  id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusDraft',
7
7
  description: 'Status shown on the dashboard order item when order is draft.',
8
8
  defaultMessage: 'Pending',
9
9
  },
10
- statusSubmitted: {
11
- id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusSubmitted',
12
- description: 'Status shown on the dashboard order item when order is submitted.',
10
+ statusAssigned: {
11
+ id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusAssigned',
12
+ description: 'Status shown on the dashboard order item when order is assigned.',
13
13
  defaultMessage: 'Pending',
14
14
  },
15
15
  statusPending: {
16
16
  id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusPending',
17
17
  description: 'Status shown on the dashboard order item when order is pending.',
18
- defaultMessage: 'Pending',
18
+ defaultMessage: 'Pending for the first direct debit',
19
19
  },
20
- statusOnGoing: {
21
- id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusOnGoing',
20
+ statusPendingPayment: {
21
+ id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusPendingPayment',
22
22
  description:
23
23
  'Status shown on the dashboard order item when order is validated with no certificate',
24
- defaultMessage: 'Enrolled',
24
+ defaultMessage: 'On going',
25
25
  },
26
26
  statusCompleted: {
27
27
  id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusCompleted',
28
- description:
29
- 'Status shown on the dashboard order item when order is validated with certificate',
30
- defaultMessage: 'Certified',
28
+ description: 'Status shown on the dashboard order item when order is completed',
29
+ defaultMessage: 'On going',
31
30
  },
32
31
  statusWaitingSignature: {
33
32
  id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusWaitingSignature',
@@ -35,6 +34,12 @@ export const messages = defineMessages({
35
34
  "Status shown on the dashboard order item when order is validated with contract's learner signature missing.",
36
35
  defaultMessage: "Pending for learner's signature",
37
36
  },
37
+ statusWaitingPaymentMethod: {
38
+ id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusWaitingPaymentMethod',
39
+ description:
40
+ 'Status shown on the dashboard order item when order is in to_save_payment_method state.',
41
+ defaultMessage: 'Payment method is missing',
42
+ },
38
43
  statusWaitingCounterSignature: {
39
44
  id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusWaitingCounterSignature',
40
45
  description:
@@ -46,10 +51,21 @@ export const messages = defineMessages({
46
51
  description: 'Status shown on the dashboard order item when order is canceled',
47
52
  defaultMessage: 'Canceled',
48
53
  },
49
- statusOther: {
50
- id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusOther',
51
- description: 'Status shown on the dashboard order item when order status is unknown',
52
- defaultMessage: '{state}',
54
+ statusNoPayment: {
55
+ id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusNoPayment',
56
+ description: 'Status shown on the dashboard order item when order is in no payment state',
57
+ defaultMessage: 'First direct debit has failed',
58
+ },
59
+ statusFailedPayment: {
60
+ id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusFailedPayment',
61
+ description: 'Status shown on the dashboard order item when order is in failed payment state',
62
+ defaultMessage: 'Last direct debit has failed',
63
+ },
64
+ statusPassed: {
65
+ id: 'components.DashboardItem.Order.OrderStateTeacherMessage.statusPassed',
66
+ description:
67
+ 'Status shown on the dashboard order item when order is completed with certificate',
68
+ defaultMessage: 'Certified',
53
69
  },
54
70
  });
55
71
 
@@ -4,7 +4,6 @@ import { useMemo } from 'react';
4
4
  import { useOmniscientOrder } from 'hooks/useOrders';
5
5
  import { Spinner } from 'components/Spinner';
6
6
  import Banner, { BannerType } from 'components/Banner';
7
- import { useCourseProduct } from 'hooks/useCourseProducts';
8
7
  import { isCredentialOrder } from 'pages/DashboardCourses/useOrdersEnrollments';
9
8
  import { handle } from 'utils/errors/handle';
10
9
  import { OrderHelper } from 'utils/OrderHelper';
@@ -39,10 +38,6 @@ export const DashboardOrderLoader = () => {
39
38
  item: order,
40
39
  states: { fetching: fetchingOrder, error: errorOrder },
41
40
  } = useOmniscientOrder(params.orderId);
42
- const {
43
- item: courseProduct,
44
- states: { fetching: fetchingCourseProduct, error: errorCourseProduct },
45
- } = useCourseProduct({ course_id: order?.course?.code, product_id: order?.product_id });
46
41
  const intl = useIntl();
47
42
 
48
43
  const credentialOrder = order && isCredentialOrder(order) ? order : undefined;
@@ -52,12 +47,9 @@ export const DashboardOrderLoader = () => {
52
47
  return intl.formatMessage(messages.wrongLinkedProductError);
53
48
  }
54
49
  }, [credentialOrder]);
55
- const error = errorOrder || errorCourseProduct || wrongLinkedProductError;
56
- const fetching = fetchingOrder || fetchingCourseProduct;
57
- const needsSignature = OrderHelper.orderNeedsSignature(
58
- order,
59
- courseProduct?.product.contract_definition,
60
- );
50
+ const error = errorOrder || wrongLinkedProductError;
51
+ const fetching = fetchingOrder;
52
+ const needsSignature = order ? OrderHelper.orderNeedsSignature(order) : false;
61
53
 
62
54
  return (
63
55
  <>