richie-education 3.3.2-dev6 → 3.4.1-dev3

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 (153) hide show
  1. package/.eslintrc.json +0 -3
  2. package/.storybook/main.js +11 -12
  3. package/.storybook/preview.tsx +49 -24
  4. package/cunningham.cjs +31 -0
  5. package/i18n/compile-translations.js +12 -10
  6. package/i18n/locales/ar-SA.json +20 -0
  7. package/i18n/locales/es-ES.json +20 -0
  8. package/i18n/locales/fa-IR.json +20 -0
  9. package/i18n/locales/fr-CA.json +20 -0
  10. package/i18n/locales/fr-FR.json +21 -1
  11. package/i18n/locales/ko-KR.json +20 -0
  12. package/i18n/locales/pt-PT.json +42 -22
  13. package/i18n/locales/ru-RU.json +20 -0
  14. package/i18n/locales/vi-VN.json +20 -0
  15. package/jest.config.js +5 -0
  16. package/js/components/AddressesManagement/AddressForm/index.stories.tsx +1 -1
  17. package/js/components/AddressesManagement/AddressForm/index.tsx +4 -3
  18. package/js/components/AddressesManagement/index.stories.tsx +1 -1
  19. package/js/components/AddressesManagement/index.tsx +5 -3
  20. package/js/components/Badge/index.stories.tsx +1 -1
  21. package/js/components/Badge/index.tsx +1 -1
  22. package/js/components/Banner/index.stories.tsx +1 -1
  23. package/js/components/CourseGlimpse/index.stories.tsx +1 -1
  24. package/js/components/CourseGlimpseList/index.stories.tsx +1 -1
  25. package/js/components/CreditCardSelector/_styles.scss +2 -2
  26. package/js/components/CreditCardSelector/index.tsx +11 -3
  27. package/js/components/DownloadCertificateButton/index.tsx +2 -1
  28. package/js/components/DownloadContractButton/index.tsx +7 -1
  29. package/js/components/Form/Form/index.tsx +4 -2
  30. package/js/components/Icon/index.stories.tsx +2 -1
  31. package/js/components/Modal/index.stories.tsx +1 -1
  32. package/js/components/Modal/index.tsx +2 -1
  33. package/js/components/OpenEdxFullNameForm/index.stories.tsx +1 -1
  34. package/js/components/OpenEdxFullNameForm/index.tsx +2 -2
  35. package/js/components/PaymentScheduleGrid/_styles.scss +2 -2
  36. package/js/components/PurchaseButton/index.stories.tsx +1 -1
  37. package/js/components/RegisteredAddress/index.stories.tsx +1 -1
  38. package/js/components/RegisteredAddress/index.tsx +4 -2
  39. package/js/components/SaleTunnel/AddressSelector/CreateAddressFormModal.tsx +1 -1
  40. package/js/components/SaleTunnel/AddressSelector/EditAddressFormModal.tsx +1 -1
  41. package/js/components/SaleTunnel/AddressSelector/index.tsx +4 -2
  42. package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationGroup.tsx +4 -3
  43. package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationSingular.tsx +27 -5
  44. package/js/components/SaleTunnel/SubscriptionButton/index.tsx +7 -5
  45. package/js/components/SaleTunnel/_styles.scss +9 -8
  46. package/js/components/SaleTunnel/index.credential.spec.tsx +50 -1
  47. package/js/components/SaleTunnel/index.stories.tsx +1 -1
  48. package/js/components/Spinner/index.stories.tsx +1 -1
  49. package/js/components/Tabs/index.stories.tsx +1 -1
  50. package/js/components/Tabs/index.tsx +2 -1
  51. package/js/components/TeacherDashboardCourseList/index.tsx +2 -1
  52. package/js/hooks/useAddressesManagement.tsx +4 -2
  53. package/js/hooks/useCreditCards/index.ts +6 -4
  54. package/js/hooks/useDashboardAddressForm.tsx +3 -3
  55. package/js/hooks/useMatchMedia.ts +1 -1
  56. package/js/hooks/useResources/useResourcesRoot.ts +1 -1
  57. package/js/hooks/useUnionResource/index.ts +6 -2
  58. package/js/hooks/useUnionResource/utils/fetchEntity.ts +1 -0
  59. package/js/pages/DashboardAddressesManagement/DashboardAddressBox.tsx +3 -3
  60. package/js/pages/DashboardAddressesManagement/DashboardCreateAddress.stories.tsx +1 -1
  61. package/js/pages/DashboardAddressesManagement/DashboardCreateAddress.tsx +1 -1
  62. package/js/pages/DashboardAddressesManagement/DashboardEditAddress.stories.tsx +1 -1
  63. package/js/pages/DashboardAddressesManagement/DashboardEditAddress.tsx +3 -2
  64. package/js/pages/DashboardAddressesManagement/index.stories.tsx +1 -1
  65. package/js/pages/DashboardAddressesManagement/index.tsx +1 -1
  66. package/js/pages/DashboardCourses/index.tsx +2 -1
  67. package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.stories.tsx +1 -1
  68. package/js/pages/DashboardCreditCardsManagement/DashboardCreditCardBox.tsx +3 -3
  69. package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.stories.tsx +1 -1
  70. package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.tsx +3 -2
  71. package/js/pages/DashboardCreditCardsManagement/index.stories.tsx +1 -1
  72. package/js/pages/DashboardOpenEdxProfile/index.stories.tsx +1 -1
  73. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +4 -2
  74. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +2 -1
  75. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +4 -4
  76. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +8 -9
  77. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +14 -3
  78. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +6 -1
  79. package/js/pages/TeacherDashboardCourseLoader/CourseRunList/utils.tsx +2 -1
  80. package/js/pages/TeacherDashboardOrganizationAgreements/BulkAgreementContractButton.tsx +4 -2
  81. package/js/pages/TeacherDashboardOrganizationAgreements/SignOrganizationAgreementButton.tsx +2 -1
  82. package/js/pages/TeacherDashboardOrganizationQuotes/index.full-process.spec.tsx +2 -2
  83. package/js/pages/TeacherDashboardOrganizationQuotes/index.spec.tsx +1 -1
  84. package/js/pages/TeacherDashboardOrganizationQuotes/index.tsx +3 -2
  85. package/js/translations/ar-SA.json +1 -1
  86. package/js/translations/es-ES.json +1 -1
  87. package/js/translations/fa-IR.json +1 -1
  88. package/js/translations/fr-CA.json +1 -1
  89. package/js/translations/fr-FR.json +1 -1
  90. package/js/translations/ko-KR.json +1 -1
  91. package/js/translations/pt-PT.json +1 -1
  92. package/js/translations/ru-RU.json +1 -1
  93. package/js/translations/vi-VN.json +1 -1
  94. package/js/types/Joanie.ts +1 -1
  95. package/js/utils/ProductHelper/index.spec.ts +1 -1
  96. package/js/utils/StorybookHelper/index.tsx +3 -6
  97. package/js/utils/cunningham-tokens.ts +1111 -142
  98. package/js/utils/errors/handle.spec.ts +3 -3
  99. package/js/utils/react-query/useSessionMutation/index.ts +8 -3
  100. package/js/utils/test/factories/joanie.ts +1 -1
  101. package/js/widgets/Dashboard/components/DashboardAvatar/index.stories.tsx +1 -1
  102. package/js/widgets/Dashboard/components/DashboardBox/index.stories.tsx +13 -5
  103. package/js/widgets/Dashboard/components/DashboardBreadcrumbs/index.stories.tsx +1 -1
  104. package/js/widgets/Dashboard/components/DashboardBreadcrumbs/index.tsx +3 -2
  105. package/js/widgets/Dashboard/components/DashboardCard/index.spec.tsx +13 -2
  106. package/js/widgets/Dashboard/components/DashboardCard/index.stories.tsx +12 -4
  107. package/js/widgets/Dashboard/components/DashboardCard/index.tsx +1 -1
  108. package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/BatchOrderPaymentManager.tsx +1 -1
  109. package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/index.tsx +2 -1
  110. package/js/widgets/Dashboard/components/DashboardItem/Certificate/index.stories.tsx +1 -1
  111. package/js/widgets/Dashboard/components/DashboardItem/Contract/index.stories.tsx +1 -1
  112. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.stories.tsx +1 -1
  113. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +8 -4
  114. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.stories.tsx +1 -1
  115. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +1 -1
  116. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderReadonly.stories.tsx +1 -1
  117. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderWritable.stories.tsx +1 -1
  118. package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +1 -1
  119. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/_styles.scss +2 -2
  120. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +2 -1
  121. package/js/widgets/Dashboard/components/DashboardItem/Order/OrganizationBlock/index.tsx +6 -3
  122. package/js/widgets/Dashboard/components/DashboardItem/index.stories.tsx +1 -1
  123. package/js/widgets/Dashboard/components/DashboardItem/index.tsx +2 -1
  124. package/js/widgets/Dashboard/components/DashboardListAvatar/index.stories.tsx +1 -1
  125. package/js/widgets/Dashboard/components/DashboardSidebar/index.stories.tsx +1 -1
  126. package/js/widgets/Dashboard/components/LearnerDashboardSidebar/index.stories.tsx +1 -1
  127. package/js/widgets/Dashboard/components/ProtectedOutlet/AuthenticatedOutlet.spec.tsx +1 -1
  128. package/js/widgets/Dashboard/components/ProtectedOutlet/ProtectedOutlet.spec.tsx +1 -1
  129. package/js/widgets/Dashboard/components/SearchBar/index.tsx +2 -1
  130. package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.stories.tsx +1 -1
  131. package/js/widgets/Dashboard/components/TeacherDashboardProfileSidebar/index.stories.tsx +1 -1
  132. package/js/widgets/Dashboard/hooks/useRouteInfo/index.spec.tsx +2 -2
  133. package/js/widgets/Dashboard/index.spec.tsx +1 -1
  134. package/js/widgets/Search/components/SearchFilterValueParent/index.stories.tsx +1 -1
  135. package/js/widgets/Search/components/SearchFiltersPane/index.tsx +2 -1
  136. package/js/widgets/Slider/index.stories.tsx +1 -1
  137. package/js/widgets/Slider/index.tsx +7 -6
  138. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCertificateItem/index.stories.tsx +1 -1
  139. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.stories.tsx +1 -1
  140. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +1 -1
  141. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.tsx +4 -2
  142. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.stories.tsx +1 -1
  143. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.stories.tsx +41 -0
  144. package/js/widgets/UserLogin/index.stories.tsx +1 -1
  145. package/package.json +76 -81
  146. package/scss/components/_subheader.scss +1 -1
  147. package/scss/components/templates/richie/slider/_slider.scss +1 -1
  148. package/scss/objects/_course_glimpses.scss +1 -0
  149. package/scss/trumps/_bootstrap.scss +1 -0
  150. package/scss/vendors/css/cunningham-tokens.css +1259 -154
  151. package/scss/vendors/cunningham-tokens.scss +1479 -150
  152. package/tsconfig.json +1 -1
  153. package/webpack.config.js +8 -0
@@ -166,7 +166,9 @@ describe('SaleTunnel / Credential', () => {
166
166
 
167
167
  // - CPF description and redirect button should be visible.
168
168
  expect(
169
- screen.getByText(/pay for your training using your personal training account/i),
169
+ screen.getByText(
170
+ /purchase your training course by using your Personal Training Account \(CPF\) on Mon Compte Formation/i,
171
+ ),
170
172
  ).toBeInTheDocument();
171
173
  const cpfButton = screen.getByRole('link', { name: /go to mon compte formation/i });
172
174
 
@@ -222,4 +224,51 @@ describe('SaleTunnel / Credential', () => {
222
224
  // - Classic billing information section should be displayed.
223
225
  expect(screen.getByText(/this information will be used for billing/i)).toBeInTheDocument();
224
226
  });
227
+
228
+ it('should display voucher input and subscribe button in CPF mode', async () => {
229
+ const course = PacedCourseFactory().one();
230
+ const product = CredentialProductFactory().one();
231
+ const billingAddress: Joanie.Address = AddressFactory({ is_main: true }).one();
232
+ const deepLink = 'https://placeholder.com/course/1';
233
+ const orderQueryParameters = {
234
+ course_code: course.code,
235
+ product_id: product.id,
236
+ state: NOT_CANCELED_ORDER_STATES,
237
+ };
238
+
239
+ fetchMock
240
+ .get(
241
+ `https://joanie.endpoint/api/v1.0/orders/?${queryString.stringify(orderQueryParameters)}`,
242
+ [],
243
+ )
244
+ .get(
245
+ `https://joanie.endpoint/api/v1.0/courses/${course.code}/products/${product.id}/payment-plan/`,
246
+ [],
247
+ )
248
+ .get(
249
+ `https://joanie.endpoint/api/v1.0/courses/${course.code}/products/${product.id}/deep-link/`,
250
+ { deep_link: deepLink },
251
+ )
252
+ .get('https://joanie.endpoint/api/v1.0/addresses/', [billingAddress], {
253
+ overwriteRoutes: true,
254
+ });
255
+
256
+ const user = userEvent.setup({ delay: null });
257
+
258
+ render(<Wrapper product={product} course={course} />, {
259
+ queryOptions: { client: createTestQueryClient({ user: richieUser }) },
260
+ });
261
+
262
+ await screen.findByRole('heading', { level: 3, name: /payment method/i });
263
+
264
+ // Switch to CPF mode
265
+ await user.click(screen.getByRole('radio', { name: /my training account \(cpf\)/i }));
266
+
267
+ // - Voucher input should be visible in CPF mode.
268
+ expect(screen.getByLabelText('Voucher code')).toBeInTheDocument();
269
+ expect(screen.getByRole('button', { name: 'Validate' })).toBeInTheDocument();
270
+
271
+ // - Subscribe button should be visible in CPF mode.
272
+ expect(screen.getByRole('button', { name: 'Subscribe' })).toBeInTheDocument();
273
+ });
225
274
  });
@@ -1,4 +1,4 @@
1
- import { StoryObj, Meta } from '@storybook/react';
1
+ import { StoryObj, Meta } from '@storybook/react-webpack5';
2
2
  import { BaseJoanieAppWrapper } from 'utils/test/wrappers/BaseJoanieAppWrapper';
3
3
  import {
4
4
  CertificateProductFactory,
@@ -1,4 +1,4 @@
1
- import { StoryObj, Meta } from '@storybook/react';
1
+ import { StoryObj, Meta } from '@storybook/react-webpack5';
2
2
  import { Spinner } from './index';
3
3
 
4
4
  export default {
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { useState } from 'react';
3
3
  import { RouterWrapper } from 'utils/test/wrappers/RouterWrapper';
4
4
  import Tabs from './index';
@@ -45,7 +45,8 @@ const Tab = ({
45
45
  return (
46
46
  <div className="tabs__tab">
47
47
  <Button
48
- color="tertiary-text"
48
+ color="brand"
49
+ variant="tertiary"
49
50
  onClick={handleOnClick}
50
51
  className={classNames('c__button--tab', {
51
52
  'c__button--active': isActive,
@@ -91,7 +91,8 @@ const TeacherDashboardCourseList = ({
91
91
  onClick={() => loadMore()}
92
92
  disabled={isLoadingMore}
93
93
  ref={loadMoreButtonRef}
94
- color="tertiary"
94
+ color="brand"
95
+ variant="tertiary"
95
96
  >
96
97
  <FormattedMessage {...messages.loadMore} />
97
98
  </Button>
@@ -1,5 +1,4 @@
1
1
  import { defineMessages, useIntl } from 'react-intl';
2
- import { MutateOptions } from '@tanstack/react-query';
3
2
  import { useAddresses } from 'hooks/useAddresses';
4
3
  import * as Joanie from 'types/Joanie';
5
4
  import { confirm } from 'utils/indirection/window';
@@ -58,7 +57,10 @@ export function useAddressesManagement() {
58
57
  * @param {Joanie.Address} address
59
58
  * @param {AddressesMutateOptions} options
60
59
  */
61
- const remove = (address: Joanie.Address, options?: MutateOptions) => {
60
+ const remove = (
61
+ address: Joanie.Address,
62
+ options?: Parameters<typeof addresses.methods.delete>[1],
63
+ ) => {
62
64
  if (address.is_main) {
63
65
  addresses.methods.setError(intl.formatMessage(messages.errorCannotRemoveMain));
64
66
  return;
@@ -1,6 +1,5 @@
1
1
  import { defineMessages, useIntl } from 'react-intl';
2
2
  import { useMutation, useQueryClient } from '@tanstack/react-query';
3
- import { MutateOptions } from '@tanstack/query-core';
4
3
  import { API, CreditCard } from 'types/Joanie';
5
4
  import { useJoanieApi } from 'contexts/JoanieApiContext';
6
5
  import { useSessionMutation } from 'utils/react-query/useSessionMutation';
@@ -84,10 +83,13 @@ const useCreditCardResources =
84
83
  * If the error is a 409, it means the credit card is used to pay at least one order
85
84
  * and the user should be informed about that.
86
85
  */
87
- const deleteMutateAsync = async (creditCard: CreditCard, options?: MutateOptions) => {
86
+ const deleteMutateAsync = async (
87
+ creditCard: CreditCard,
88
+ options?: Parameters<typeof custom.methods.delete>[1],
89
+ ) => {
88
90
  return custom.methods.delete(creditCard.id, {
89
91
  ...options,
90
- onError: (error: HttpError, variables, context) => {
92
+ onError: (error: HttpError, variables, context, mutationContext) => {
91
93
  if (error.code === HttpStatusCode.CONFLICT) {
92
94
  custom.methods.setError(
93
95
  intl.formatMessage(messages.errorCannotDelete, {
@@ -97,7 +99,7 @@ const useCreditCardResources =
97
99
  } else {
98
100
  custom.methods.setError(intl.formatMessage(messages.errorDelete));
99
101
  }
100
- options?.onError?.(error, variables, context);
102
+ options?.onError?.(error, variables, context, mutationContext);
101
103
  },
102
104
  });
103
105
  };
@@ -1,5 +1,5 @@
1
- import { yupResolver } from '@hookform/resolvers/yup/dist/yup';
2
- import { FormProvider, useForm } from 'react-hook-form';
1
+ import { yupResolver } from '@hookform/resolvers/yup';
2
+ import { FormProvider, Resolver, useForm } from 'react-hook-form';
3
3
  import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
4
4
  import * as Yup from 'yup';
5
5
  import countries from 'i18n-iso-countries';
@@ -60,7 +60,7 @@ export const useDashboardAddressForm = (address?: Address) => {
60
60
  defaultValues: address || defaultValues,
61
61
  mode: 'onBlur',
62
62
  reValidateMode: 'onChange',
63
- resolver: yupResolver(validationSchema),
63
+ resolver: yupResolver(validationSchema) as Resolver<AddressFormValues>,
64
64
  });
65
65
  const { register, handleSubmit, formState } = form;
66
66
 
@@ -36,6 +36,6 @@ const useMatchMedia = (query: string): boolean => {
36
36
  };
37
37
 
38
38
  export const useMatchMediaLg = () =>
39
- useMatchMedia(`(max-width: ${tokens.themes.default.theme.breakpoints.lg})`);
39
+ useMatchMedia(`(max-width: ${tokens.themes.default.globals.breakpoints.lg})`);
40
40
 
41
41
  export default useMatchMedia;
@@ -1,7 +1,7 @@
1
1
  import { useMutation, useQuery, useQueryClient, UseQueryResult } from '@tanstack/react-query';
2
2
  import { useCallback, useMemo, useState } from 'react';
3
3
  import { defineMessages, useIntl } from 'react-intl';
4
- import { MutateOptions } from '@tanstack/query-core/src/types';
4
+ import { MutateOptions } from '@tanstack/query-core';
5
5
  import { AddParameters, Maybe } from 'types/utils';
6
6
  import { HttpError } from 'utils/errors/HttpError';
7
7
  import { useSessionQuery } from 'utils/react-query/useSessionQuery';
@@ -35,8 +35,12 @@ export interface ResourceUnionPaginationProps {
35
35
  perPage?: number;
36
36
  }
37
37
 
38
- interface UseUnionResourceProps<DataA, DataB, FiltersA, FiltersB>
39
- extends ResourceUnionPaginationProps {
38
+ interface UseUnionResourceProps<
39
+ DataA,
40
+ DataB,
41
+ FiltersA,
42
+ FiltersB,
43
+ > extends ResourceUnionPaginationProps {
40
44
  queryAConfig: QueryConfig<DataA, FiltersA>;
41
45
  queryBConfig: QueryConfig<DataB, FiltersB>;
42
46
  errorGetMessage?: MessageDescriptor;
@@ -53,6 +53,7 @@ export const fetchEntity = async <
53
53
  // Here we need to mimic the behavior of staleTime, which does not seems to be implemented when using `getQueryData`.
54
54
  if (
55
55
  state &&
56
+ state.status === 'success' &&
56
57
  state.dataUpdatedAt >= new Date().getTime() - REACT_QUERY_SETTINGS.staleTimes.sessionItems &&
57
58
  !state.isInvalidated
58
59
  ) {
@@ -47,16 +47,16 @@ export const DashboardAddressBox = ({
47
47
  <>
48
48
  <div className="dashboard-address-box__buttons">
49
49
  {!address.is_main && (
50
- <Button color="primary" onClick={() => promote(address)}>
50
+ <Button color="brand" variant="primary" onClick={() => promote(address)}>
51
51
  <FormattedMessage {...messages.setMain} />
52
52
  </Button>
53
53
  )}
54
- <Button color="primary" onClick={() => edit(address)}>
54
+ <Button color="brand" variant="primary" onClick={() => edit(address)}>
55
55
  <FormattedMessage {...messages.edit} />
56
56
  </Button>
57
57
  </div>
58
58
  {!address.is_main && (
59
- <Button color="primary" onClick={() => remove(address)}>
59
+ <Button color="brand" variant="primary" onClick={() => remove(address)}>
60
60
  <FormattedMessage {...messages.delete} />
61
61
  </Button>
62
62
  )}
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { StorybookHelper } from 'utils/StorybookHelper';
3
3
  import { DashboardCreateAddress } from './DashboardCreateAddress';
4
4
 
@@ -38,7 +38,7 @@ export const DashboardCreateAddress = ({ onSettled }: { onSettled?: Function })
38
38
  <DashboardCard
39
39
  header={<FormattedMessage {...messages.header} />}
40
40
  footer={
41
- <Button color="primary" onClick={handleSubmit(onSubmit)}>
41
+ <Button color="brand" variant="primary" onClick={handleSubmit(onSubmit)}>
42
42
  <FormattedMessage {...messages.submit} />
43
43
  </Button>
44
44
  }
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { StorybookHelper } from 'utils/StorybookHelper';
3
3
  import { AddressFactory } from 'utils/test/factories/joanie';
4
4
  import { DashboardEditAddress } from './DashboardEditAddress';
@@ -54,12 +54,13 @@ export const DashboardEditAddress = ({ address, onSettled = noop }: DashboardEdi
54
54
  header={<FormattedMessage {...messages.header} values={{ title: address.title }} />}
55
55
  footer={
56
56
  <>
57
- <Button color="secondary" onClick={handleSubmit(onSubmit)}>
57
+ <Button color="brand" variant="secondary" onClick={handleSubmit(onSubmit)}>
58
58
  <FormattedMessage {...messages.submit} />
59
59
  </Button>
60
60
  {!address.is_main && (
61
61
  <Button
62
- color="secondary"
62
+ color="brand"
63
+ variant="secondary"
63
64
  onClick={() => {
64
65
  remove(address, {
65
66
  onSuccess: () => onSettled(),
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { StorybookHelper } from 'utils/StorybookHelper';
3
3
  import { DashboardAddressesManagement } from './index';
4
4
 
@@ -86,7 +86,7 @@ export const DashboardAddressesManagement = ({
86
86
  ))}
87
87
  </DashboardBox.List>
88
88
  )}
89
- <Button color="secondary" fullWidth onClick={() => onClickCreate?.()}>
89
+ <Button color="brand" variant="secondary" fullWidth onClick={() => onClickCreate?.()}>
90
90
  <Icon name={IconTypeEnum.PLUS} className="button__icon" />
91
91
  <FormattedMessage {...messages.add} />
92
92
  </Button>
@@ -88,7 +88,8 @@ export const DashboardCourses = () => {
88
88
  onClick={() => next()}
89
89
  disabled={isLoadingMore}
90
90
  ref={loadMoreButtonRef}
91
- color="tertiary"
91
+ color="brand"
92
+ variant="tertiary"
92
93
  >
93
94
  <FormattedMessage {...messages.loadMore} />
94
95
  </Button>
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { CreditCardFactory } from 'utils/test/factories/joanie';
3
3
  import { CreditCard, CreditCardBrand } from 'types/Joanie';
4
4
  import { CreditCardBrandLogo } from './CreditCardBrandLogo';
@@ -68,15 +68,15 @@ export const DashboardCreditCardBox = ({ creditCard, promote, edit, remove }: Pr
68
68
  <>
69
69
  <div className="dashboard-credit-card__buttons">
70
70
  {!creditCard.is_main && (
71
- <Button color="primary" onClick={() => promote(creditCard)}>
71
+ <Button color="brand" variant="primary" onClick={() => promote(creditCard)}>
72
72
  <FormattedMessage {...messages.setMain} />
73
73
  </Button>
74
74
  )}
75
- <Button color="primary" onClick={() => edit(creditCard)}>
75
+ <Button color="brand" variant="primary" onClick={() => edit(creditCard)}>
76
76
  <FormattedMessage {...messages.edit} />
77
77
  </Button>
78
78
  </div>
79
- <Button color="primary" onClick={() => remove(creditCard)}>
79
+ <Button color="brand" variant="primary" onClick={() => remove(creditCard)}>
80
80
  <FormattedMessage {...messages.delete} />
81
81
  </Button>
82
82
  </>
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { StorybookHelper } from 'utils/StorybookHelper';
3
3
  import { CreditCardFactory } from 'utils/test/factories/joanie';
4
4
  import { DashboardEditCreditCard } from './DashboardEditCreditCard';
@@ -159,12 +159,13 @@ export const DashboardEditCreditCard = ({ creditCard, onSettled = noop }: Props)
159
159
  header={<FormattedMessage {...messages.header} />}
160
160
  footer={
161
161
  <>
162
- <Button color="secondary" onClick={handleSubmit(onSubmit)}>
162
+ <Button color="brand" variant="secondary" onClick={handleSubmit(onSubmit)}>
163
163
  <FormattedMessage {...messages.submit} />
164
164
  </Button>
165
165
  {!creditCard.is_main && (
166
166
  <Button
167
- color="secondary"
167
+ color="brand"
168
+ variant="secondary"
168
169
  onClick={() =>
169
170
  safeDelete(creditCard, {
170
171
  onSuccess: () => {
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { StorybookHelper } from 'utils/StorybookHelper';
3
3
  import { DashboardCreditCardsManagement } from '.';
4
4
 
@@ -1,4 +1,4 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import DashboardOpenEdxProfile from '.';
3
3
 
4
4
  export default {
@@ -49,7 +49,8 @@ const BulkDownloadContractButton = ({
49
49
  return (
50
50
  <Button
51
51
  disabled={true}
52
- color="tertiary"
52
+ color="brand"
53
+ variant="tertiary"
53
54
  size="small"
54
55
  icon={<div className="spinner spinner--small" />}
55
56
  >
@@ -62,7 +63,8 @@ const BulkDownloadContractButton = ({
62
63
  <Button
63
64
  onClick={downloadContractArchive}
64
65
  disabled={status === ContractDownloadStatus.INITIALIZING}
65
- color={status === ContractDownloadStatus.READY ? 'primary' : 'tertiary'}
66
+ color="brand"
67
+ variant={status === ContractDownloadStatus.READY ? 'primary' : 'tertiary'}
66
68
  size="small"
67
69
  icon={<span className="material-icons">download</span>}
68
70
  >
@@ -32,7 +32,8 @@ const SignOrganizationContractButton = ({
32
32
  {hasContractToSign && (
33
33
  <Button
34
34
  size="small"
35
- color="primary"
35
+ color="brand"
36
+ variant="primary"
36
37
  onClick={() => setContractFrameOpened(true)}
37
38
  disabled={contractFrameOpened}
38
39
  icon={
@@ -116,9 +116,9 @@ describe.each([
116
116
  });
117
117
 
118
118
  await waitFor(() => {
119
- expect(result.current.isPolling).toBe(false);
119
+ expect(result.current.isContractArchiveExists).toBe(true);
120
120
  });
121
- expect(result.current.isContractArchiveExists).toBe(true);
121
+ expect(result.current.isPolling).toBe(false);
122
122
  });
123
123
 
124
124
  it('should do nothing when enable is false', () => {
@@ -160,8 +160,8 @@ describe.each([
160
160
  mockCheckArchive.mockResolvedValue(true);
161
161
  await waitFor(() => {
162
162
  expect(mockCheckArchive).toHaveBeenCalledTimes(2);
163
+ expect(result.current.isContractArchiveExists).toBe(true);
164
+ expect(result.current.isPolling).toBe(false);
163
165
  });
164
- expect(result.current.isPolling).toBe(false);
165
- expect(result.current.isContractArchiveExists).toBe(true);
166
166
  });
167
167
  });
@@ -232,6 +232,7 @@ describe.each([
232
232
  const contractArchiveId = faker.string.uuid();
233
233
  storeContractArchiveId({ ...localStorageArchiveFilters, contractArchiveId });
234
234
  mockCheckArchive.mockResolvedValue(true);
235
+ mockGetArchive.mockResolvedValue('fake-content');
235
236
 
236
237
  const { result } = renderHook(
237
238
  () => useDownloadContractArchive(localStorageArchiveFilters),
@@ -246,8 +247,8 @@ describe.each([
246
247
  });
247
248
  expect(mockCheckArchive).toHaveBeenCalledTimes(1);
248
249
 
249
- act(() => {
250
- result.current.downloadContractArchive();
250
+ await act(async () => {
251
+ await result.current.downloadContractArchive();
251
252
  });
252
253
 
253
254
  await waitFor(() => {
@@ -255,11 +256,8 @@ describe.each([
255
256
  });
256
257
 
257
258
  expect(mockCheckArchive).toHaveBeenCalledTimes(1);
258
- // backend is called to download the archive
259
- expect(mockGetArchive).toHaveBeenCalledTimes(1);
260
-
261
- // but not to generate the archive
262
259
  expect(mockCreateArchive).not.toHaveBeenCalled();
260
+ expect(mockGetArchive).toHaveBeenCalledTimes(1);
263
261
  });
264
262
 
265
263
  it("doDownloadArchive should call 'createArchive' if no contractArchiveId is stored", async () => {
@@ -271,9 +269,11 @@ describe.each([
271
269
  );
272
270
 
273
271
  mockCheckArchive.mockResolvedValue(true);
272
+
274
273
  mockCreateArchive.mockResolvedValue(faker.string.uuid());
275
- act(() => {
276
- result.current.downloadContractArchive();
274
+
275
+ await act(async () => {
276
+ await result.current.downloadContractArchive();
277
277
  });
278
278
 
279
279
  await waitFor(() => {
@@ -282,7 +282,6 @@ describe.each([
282
282
 
283
283
  expect(mockCreateArchive).toHaveBeenCalledTimes(1);
284
284
  expect(mockCheckArchive).toHaveBeenCalledTimes(1);
285
- expect(mockGetArchive).toHaveBeenCalledTimes(1);
286
285
  });
287
286
  });
288
287
 
@@ -157,10 +157,21 @@ const useDownloadContractArchive = ({
157
157
  // this effect will trigger the download
158
158
  // if it have been previously requested by the user
159
159
  useEffect(() => {
160
- if (isDownloadRequest && isContractArchiveExists) {
161
- downloadContractArchive();
160
+ if (isDownloadRequest && isContractArchiveExists && contractArchiveId !== null) {
161
+ (async () => {
162
+ await getArchive(contractArchiveId);
163
+ setIsDownloadRequest(false);
164
+ setContractArchiveId(null);
165
+ unstoreContractArchiveId(localstorageArchiveFilters);
166
+ })();
162
167
  }
163
- }, [isDownloadRequest, isContractArchiveExists]);
168
+ }, [
169
+ isDownloadRequest,
170
+ isContractArchiveExists,
171
+ contractArchiveId,
172
+ localstorageArchiveFilters,
173
+ getArchive,
174
+ ]);
164
175
 
165
176
  return {
166
177
  status: contractDownloadStatus,
@@ -82,7 +82,12 @@ const CourseLearnerDataGrid = ({
82
82
  headerName: intl.formatMessage(messages.columnActions),
83
83
  renderCell: (params: { row: Row }) => {
84
84
  return (
85
- <Button href={`mailto:${params.row.owner__email}`} size="small" color="secondary">
85
+ <Button
86
+ href={`mailto:${params.row.owner__email}`}
87
+ size="small"
88
+ color="brand"
89
+ variant="secondary"
90
+ >
86
91
  <FormattedMessage {...messages.contactButton} />
87
92
  </Button>
88
93
  );
@@ -69,7 +69,8 @@ export const buildCourseRunData = (intl: IntlShape, courseRuns: CourseRun[]) =>
69
69
  <CourseRunListCell variant={CourseRunListCell.ALIGN_RIGHT}>
70
70
  <Button
71
71
  href={courseRun.resource_link}
72
- color="secondary"
72
+ color="brand"
73
+ variant="secondary"
73
74
  size="small"
74
75
  icon={<Icon name={IconTypeEnum.LOGOUT_SQUARE} size="small" />}
75
76
  >
@@ -50,7 +50,8 @@ const BulkDownloadAgreementButton = ({
50
50
  return (
51
51
  <Button
52
52
  disabled={true}
53
- color="tertiary"
53
+ color="brand"
54
+ variant="tertiary"
54
55
  size="small"
55
56
  icon={<div className="spinner spinner--small" />}
56
57
  >
@@ -63,7 +64,8 @@ const BulkDownloadAgreementButton = ({
63
64
  <Button
64
65
  onClick={downloadContractArchive}
65
66
  disabled={status === ContractDownloadStatus.INITIALIZING}
66
- color={status === ContractDownloadStatus.READY ? 'primary' : 'tertiary'}
67
+ color="brand"
68
+ variant={status === ContractDownloadStatus.READY ? 'primary' : 'tertiary'}
67
69
  size="small"
68
70
  icon={<span className="material-icons">download</span>}
69
71
  >
@@ -32,7 +32,8 @@ const SignOrganizationAgreementButton = ({
32
32
  {hasAgreementToSign && (
33
33
  <Button
34
34
  size="small"
35
- color="primary"
35
+ color="brand"
36
+ variant="primary"
36
37
  onClick={() => setAgreementFrameOpened(true)}
37
38
  disabled={agreementFrameOpened}
38
39
  icon={
@@ -37,7 +37,7 @@ describe('full process for the organization quotes dashboard', () => {
37
37
 
38
38
  const organization = OrganizationFactory({
39
39
  abilities: {
40
- can_submit_for_signature_batch_order: true,
40
+ can_manage_batch_order_agreement: true,
41
41
  confirm_bank_transfer: true,
42
42
  confirm_quote: true,
43
43
  download_quote: true,
@@ -206,7 +206,7 @@ describe('full process for the organization quotes dashboard', () => {
206
206
  fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/`, []);
207
207
  const organization = OrganizationFactory({
208
208
  abilities: {
209
- can_submit_for_signature_batch_order: true,
209
+ can_manage_batch_order_agreement: true,
210
210
  confirm_bank_transfer: true,
211
211
  confirm_quote: true,
212
212
  download_quote: true,
@@ -165,7 +165,7 @@ describe('pages/TeacherDashboardOrganizationQuotes', () => {
165
165
 
166
166
  const organization = OrganizationFactory({
167
167
  abilities: {
168
- can_submit_for_signature_batch_order: false,
168
+ can_manage_batch_order_agreement: false,
169
169
  confirm_bank_transfer: false,
170
170
  confirm_quote: false,
171
171
  download_quote: true,
@@ -371,7 +371,8 @@ const TeacherDashboardOrganizationQuotes = () => {
371
371
  <div>
372
372
  <Button
373
373
  size="small"
374
- color="secondary"
374
+ color="brand"
375
+ variant="secondary"
375
376
  className="mr-2"
376
377
  onClick={() => handleDownloadQuote(quote.id)}
377
378
  icon={<span className="material-icons">download</span>}
@@ -415,7 +416,7 @@ const TeacherDashboardOrganizationQuotes = () => {
415
416
  const submitForSignatureButton = (
416
417
  <Button
417
418
  size="small"
418
- disabled={batchOrder.contract_submitted || !abilities?.can_submit_for_signature_batch_order}
419
+ disabled={batchOrder.contract_submitted || !abilities?.can_manage_batch_order_agreement}
419
420
  onClick={() =>
420
421
  !batchOrder.contract_submitted && handleSubmitForSignature(quote.batch_order.id)
421
422
  }