richie-education 3.3.2-dev6 → 3.4.0
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.
- package/.eslintrc.json +0 -3
- package/.storybook/preview.tsx +49 -24
- package/cunningham.cjs +31 -0
- package/i18n/compile-translations.js +12 -10
- package/i18n/locales/ar-SA.json +20 -0
- package/i18n/locales/es-ES.json +20 -0
- package/i18n/locales/fa-IR.json +20 -0
- package/i18n/locales/fr-CA.json +20 -0
- package/i18n/locales/fr-FR.json +21 -1
- package/i18n/locales/ko-KR.json +20 -0
- package/i18n/locales/pt-PT.json +42 -22
- package/i18n/locales/ru-RU.json +20 -0
- package/i18n/locales/vi-VN.json +20 -0
- package/jest.config.js +5 -0
- package/js/components/AddressesManagement/AddressForm/index.stories.tsx +1 -1
- package/js/components/AddressesManagement/AddressForm/index.tsx +4 -3
- package/js/components/AddressesManagement/index.stories.tsx +1 -1
- package/js/components/AddressesManagement/index.tsx +5 -3
- package/js/components/Badge/index.stories.tsx +1 -1
- package/js/components/Badge/index.tsx +1 -1
- package/js/components/Banner/index.stories.tsx +1 -1
- package/js/components/CourseGlimpse/index.stories.tsx +1 -1
- package/js/components/CourseGlimpseList/index.stories.tsx +1 -1
- package/js/components/CreditCardSelector/_styles.scss +2 -2
- package/js/components/CreditCardSelector/index.tsx +11 -3
- package/js/components/DownloadCertificateButton/index.tsx +2 -1
- package/js/components/DownloadContractButton/index.tsx +7 -1
- package/js/components/Form/Form/index.tsx +4 -2
- package/js/components/Icon/index.stories.tsx +2 -1
- package/js/components/Modal/index.stories.tsx +1 -1
- package/js/components/Modal/index.tsx +2 -1
- package/js/components/OpenEdxFullNameForm/index.stories.tsx +1 -1
- package/js/components/OpenEdxFullNameForm/index.tsx +2 -2
- package/js/components/PaymentScheduleGrid/_styles.scss +2 -2
- package/js/components/PurchaseButton/index.stories.tsx +1 -1
- package/js/components/RegisteredAddress/index.stories.tsx +1 -1
- package/js/components/RegisteredAddress/index.tsx +4 -2
- package/js/components/SaleTunnel/AddressSelector/CreateAddressFormModal.tsx +1 -1
- package/js/components/SaleTunnel/AddressSelector/EditAddressFormModal.tsx +1 -1
- package/js/components/SaleTunnel/AddressSelector/index.tsx +4 -2
- package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationGroup.tsx +4 -3
- package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationSingular.tsx +27 -5
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +7 -5
- package/js/components/SaleTunnel/_styles.scss +9 -8
- package/js/components/SaleTunnel/index.credential.spec.tsx +50 -1
- package/js/components/SaleTunnel/index.stories.tsx +1 -1
- package/js/components/Spinner/index.stories.tsx +1 -1
- package/js/components/Tabs/index.stories.tsx +1 -1
- package/js/components/Tabs/index.tsx +2 -1
- package/js/components/TeacherDashboardCourseList/index.tsx +2 -1
- package/js/hooks/useAddressesManagement.tsx +4 -2
- package/js/hooks/useCreditCards/index.ts +6 -4
- package/js/hooks/useDashboardAddressForm.tsx +3 -3
- package/js/hooks/useMatchMedia.ts +1 -1
- package/js/hooks/useResources/useResourcesRoot.ts +1 -1
- package/js/hooks/useUnionResource/index.ts +6 -2
- package/js/hooks/useUnionResource/utils/fetchEntity.ts +1 -0
- package/js/pages/DashboardAddressesManagement/DashboardAddressBox.tsx +3 -3
- package/js/pages/DashboardAddressesManagement/DashboardCreateAddress.stories.tsx +1 -1
- package/js/pages/DashboardAddressesManagement/DashboardCreateAddress.tsx +1 -1
- package/js/pages/DashboardAddressesManagement/DashboardEditAddress.stories.tsx +1 -1
- package/js/pages/DashboardAddressesManagement/DashboardEditAddress.tsx +3 -2
- package/js/pages/DashboardAddressesManagement/index.stories.tsx +1 -1
- package/js/pages/DashboardAddressesManagement/index.tsx +1 -1
- package/js/pages/DashboardCourses/index.tsx +2 -1
- package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.stories.tsx +1 -1
- package/js/pages/DashboardCreditCardsManagement/DashboardCreditCardBox.tsx +3 -3
- package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.stories.tsx +1 -1
- package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.tsx +3 -2
- package/js/pages/DashboardCreditCardsManagement/index.stories.tsx +1 -1
- package/js/pages/DashboardOpenEdxProfile/index.stories.tsx +1 -1
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +4 -2
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +2 -1
- package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +8 -9
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +14 -3
- package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +6 -1
- package/js/pages/TeacherDashboardCourseLoader/CourseRunList/utils.tsx +2 -1
- package/js/pages/TeacherDashboardOrganizationAgreements/BulkAgreementContractButton.tsx +4 -2
- package/js/pages/TeacherDashboardOrganizationAgreements/SignOrganizationAgreementButton.tsx +2 -1
- package/js/pages/TeacherDashboardOrganizationQuotes/index.full-process.spec.tsx +2 -2
- package/js/pages/TeacherDashboardOrganizationQuotes/index.spec.tsx +1 -1
- package/js/pages/TeacherDashboardOrganizationQuotes/index.tsx +3 -2
- package/js/translations/ar-SA.json +1 -1
- package/js/translations/es-ES.json +1 -1
- package/js/translations/fa-IR.json +1 -1
- package/js/translations/fr-CA.json +1 -1
- package/js/translations/fr-FR.json +1 -1
- package/js/translations/ko-KR.json +1 -1
- package/js/translations/pt-PT.json +1 -1
- package/js/translations/ru-RU.json +1 -1
- package/js/translations/vi-VN.json +1 -1
- package/js/types/Joanie.ts +1 -1
- package/js/utils/ProductHelper/index.spec.ts +1 -1
- package/js/utils/StorybookHelper/index.tsx +3 -6
- package/js/utils/cunningham-tokens.ts +1111 -142
- package/js/utils/errors/handle.spec.ts +3 -3
- package/js/utils/react-query/useSessionMutation/index.ts +8 -3
- package/js/utils/test/factories/joanie.ts +1 -1
- package/js/widgets/Dashboard/components/DashboardAvatar/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardBox/index.stories.tsx +13 -5
- package/js/widgets/Dashboard/components/DashboardBreadcrumbs/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardBreadcrumbs/index.tsx +3 -2
- package/js/widgets/Dashboard/components/DashboardCard/index.spec.tsx +13 -2
- package/js/widgets/Dashboard/components/DashboardCard/index.stories.tsx +12 -4
- package/js/widgets/Dashboard/components/DashboardCard/index.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/BatchOrderPaymentManager.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/index.tsx +2 -1
- package/js/widgets/Dashboard/components/DashboardItem/Certificate/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Contract/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +8 -4
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderReadonly.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderWritable.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/_styles.scss +2 -2
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +2 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrganizationBlock/index.tsx +6 -3
- package/js/widgets/Dashboard/components/DashboardItem/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/index.tsx +2 -1
- package/js/widgets/Dashboard/components/DashboardListAvatar/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardSidebar/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/LearnerDashboardSidebar/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/ProtectedOutlet/AuthenticatedOutlet.spec.tsx +1 -1
- package/js/widgets/Dashboard/components/ProtectedOutlet/ProtectedOutlet.spec.tsx +1 -1
- package/js/widgets/Dashboard/components/SearchBar/index.tsx +2 -1
- package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/components/TeacherDashboardProfileSidebar/index.stories.tsx +1 -1
- package/js/widgets/Dashboard/hooks/useRouteInfo/index.spec.tsx +2 -2
- package/js/widgets/Dashboard/index.spec.tsx +1 -1
- package/js/widgets/Search/components/SearchFilterValueParent/index.stories.tsx +1 -1
- package/js/widgets/Search/components/SearchFiltersPane/index.tsx +2 -1
- package/js/widgets/Slider/index.stories.tsx +1 -1
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCertificateItem/index.stories.tsx +1 -1
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.stories.tsx +1 -1
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +1 -1
- package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.tsx +4 -2
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.stories.tsx +1 -1
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.stories.tsx +41 -0
- package/js/widgets/UserLogin/index.stories.tsx +1 -1
- package/package.json +81 -81
- package/scss/components/_subheader.scss +1 -1
- package/scss/trumps/_bootstrap.scss +1 -0
- package/scss/vendors/css/cunningham-tokens.css +1259 -154
- package/scss/vendors/cunningham-tokens.scss +1479 -150
- package/tsconfig.json +1 -1
- package/webpack.config.js +8 -0
package/i18n/locales/vi-VN.json
CHANGED
|
@@ -1911,6 +1911,14 @@
|
|
|
1911
1911
|
"description": "Validate button for the credit card modal",
|
|
1912
1912
|
"message": "Validate"
|
|
1913
1913
|
},
|
|
1914
|
+
"components.SaleTunnel.Information.cpf.buttonLabel": {
|
|
1915
|
+
"description": "Label for the button redirecting to Mon Compte Formation",
|
|
1916
|
+
"message": "Go to Mon Compte Formation"
|
|
1917
|
+
},
|
|
1918
|
+
"components.SaleTunnel.Information.cpf.description": {
|
|
1919
|
+
"description": "Explanatory text for the CPF payment option",
|
|
1920
|
+
"message": "Purchase your training course by using your Personal Training Account (CPF) on Mon Compte Formation."
|
|
1921
|
+
},
|
|
1914
1922
|
"components.SaleTunnel.Information.description": {
|
|
1915
1923
|
"description": "Description of the information section",
|
|
1916
1924
|
"message": "This information will be used for billing"
|
|
@@ -1951,6 +1959,18 @@
|
|
|
1951
1959
|
"description": "Message displayed representing the payment schedule when the order is free.",
|
|
1952
1960
|
"message": "No payment required. This order is fully covered."
|
|
1953
1961
|
},
|
|
1962
|
+
"components.SaleTunnel.Information.paymentMode.classic": {
|
|
1963
|
+
"description": "Label for the classic card payment option",
|
|
1964
|
+
"message": "Credit card payment"
|
|
1965
|
+
},
|
|
1966
|
+
"components.SaleTunnel.Information.paymentMode.cpf": {
|
|
1967
|
+
"description": "Label for the CPF (Mon Compte Formation) payment option",
|
|
1968
|
+
"message": "My Training Account (CPF)"
|
|
1969
|
+
},
|
|
1970
|
+
"components.SaleTunnel.Information.paymentMode.title": {
|
|
1971
|
+
"description": "Title for the payment mode selection section",
|
|
1972
|
+
"message": "Payment method"
|
|
1973
|
+
},
|
|
1954
1974
|
"components.SaleTunnel.Information.paymentSchedule": {
|
|
1955
1975
|
"description": "Title for the payment schedule section",
|
|
1956
1976
|
"message": "Payment schedule"
|
package/jest.config.js
CHANGED
|
@@ -17,6 +17,10 @@ module.exports = {
|
|
|
17
17
|
transformIgnorePatterns: [
|
|
18
18
|
'node_modules/(?!(' +
|
|
19
19
|
'react-intl' +
|
|
20
|
+
'|@formatjs' +
|
|
21
|
+
'|intl-messageformat' +
|
|
22
|
+
'|@tanstack' +
|
|
23
|
+
'|uuid' +
|
|
20
24
|
'|lodash-es' +
|
|
21
25
|
'|@hookform/resolvers' +
|
|
22
26
|
'|query-string' +
|
|
@@ -25,6 +29,7 @@ module.exports = {
|
|
|
25
29
|
'|filter-obj' +
|
|
26
30
|
'|@openfun/cunningham-react' +
|
|
27
31
|
'|keycloak-js' +
|
|
32
|
+
'|@faker-js/faker' +
|
|
28
33
|
')/)',
|
|
29
34
|
],
|
|
30
35
|
globals: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
2
2
|
import { Fragment, useEffect } from 'react';
|
|
3
|
-
import { FormProvider, useForm } from 'react-hook-form';
|
|
3
|
+
import { FormProvider, Resolver, useForm } from 'react-hook-form';
|
|
4
4
|
import { FormattedMessage, useIntl } from 'react-intl';
|
|
5
5
|
import { Button, Checkbox } from '@openfun/cunningham-react';
|
|
6
6
|
import { getLocalizedCunninghamErrorProp } from 'components/Form/utils';
|
|
@@ -39,7 +39,7 @@ const AddressForm = ({ handleReset, onSubmit, address }: Props) => {
|
|
|
39
39
|
defaultValues: address || defaultValues,
|
|
40
40
|
mode: 'onBlur',
|
|
41
41
|
reValidateMode: 'onChange',
|
|
42
|
-
resolver: yupResolver(validationSchema)
|
|
42
|
+
resolver: yupResolver(validationSchema) as Resolver<AddressFormValues>,
|
|
43
43
|
});
|
|
44
44
|
const { register, handleSubmit, reset, formState } = form;
|
|
45
45
|
|
|
@@ -140,7 +140,8 @@ const AddressForm = ({ handleReset, onSubmit, address }: Props) => {
|
|
|
140
140
|
{address ? (
|
|
141
141
|
<Fragment>
|
|
142
142
|
<Button
|
|
143
|
-
color="
|
|
143
|
+
color="brand"
|
|
144
|
+
variant="tertiary"
|
|
144
145
|
onClick={handleCancel}
|
|
145
146
|
title={intl.formatMessage(messages.cancelTitleButton)}
|
|
146
147
|
>
|
|
@@ -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 { RichieContextFactory } from 'utils/test/factories/richie';
|
|
4
4
|
import AddressesManagement from '.';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Children, useEffect, useState,
|
|
1
|
+
import { Children, useEffect, useState, Ref } from 'react';
|
|
2
2
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
3
3
|
import { Button } from '@openfun/cunningham-react';
|
|
4
4
|
import AddressForm, { type AddressFormValues } from 'components/AddressesManagement/AddressForm';
|
|
@@ -105,9 +105,10 @@ export const messages = defineMessages({
|
|
|
105
105
|
},
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
interface AddressesManagementProps
|
|
108
|
+
interface AddressesManagementProps {
|
|
109
109
|
handleClose: () => void;
|
|
110
110
|
selectAddress: (address: Joanie.Address) => void;
|
|
111
|
+
ref?: Ref<HTMLDivElement>;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
const AddressesManagement = ({ handleClose, selectAddress, ref }: AddressesManagementProps) => {
|
|
@@ -191,7 +192,8 @@ const AddressesManagement = ({ handleClose, selectAddress, ref }: AddressesManag
|
|
|
191
192
|
<div className="AddressesManagement" ref={ref}>
|
|
192
193
|
<Button
|
|
193
194
|
className="AddressesManagement__closeButton"
|
|
194
|
-
color="
|
|
195
|
+
color="brand"
|
|
196
|
+
variant="tertiary"
|
|
195
197
|
size="small"
|
|
196
198
|
onClick={handleClose}
|
|
197
199
|
>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { CourseLightFactory, RichieContextFactory } from 'utils/test/factories/richie';
|
|
3
3
|
import { CourseGlimpse, getCourseGlimpseProps } from 'components/CourseGlimpse';
|
|
4
4
|
import { CourseCertificateOffer, CourseOffer } from 'types/Course';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { RichieContextFactory, CourseLightFactory } from 'utils/test/factories/richie';
|
|
3
3
|
import { CourseGlimpseList, getCourseGlimpseListProps } from '.';
|
|
4
4
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
text-align: left;
|
|
16
16
|
font-size: rem-calc(12px);
|
|
17
17
|
color: r-theme-val(credit-card-selector, title-color);
|
|
18
|
-
font-weight: var(--c--
|
|
19
|
-
font-family: var(--c--
|
|
18
|
+
font-weight: var(--c--globals--font--weights--bold);
|
|
19
|
+
font-family: var(--c--globals--font--families--accent);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
&__meta {
|
|
@@ -111,7 +111,8 @@ export const CreditCardSelector = ({
|
|
|
111
111
|
{allowEdit && creditCards?.length > 0 && (
|
|
112
112
|
<Button
|
|
113
113
|
icon={<span className="material-icons">edit</span>}
|
|
114
|
-
color="
|
|
114
|
+
color="brand"
|
|
115
|
+
variant="tertiary"
|
|
115
116
|
size="medium"
|
|
116
117
|
onClick={modal.open}
|
|
117
118
|
aria-label={intl.formatMessage(messages.editCreditCardAriaLabel)}
|
|
@@ -122,7 +123,8 @@ export const CreditCardSelector = ({
|
|
|
122
123
|
<Button
|
|
123
124
|
onClick={() => setCreditCard(undefined)}
|
|
124
125
|
size="small"
|
|
125
|
-
color="
|
|
126
|
+
color="brand"
|
|
127
|
+
variant="secondary"
|
|
126
128
|
className="mt-t"
|
|
127
129
|
fullWidth={isMobile}
|
|
128
130
|
>
|
|
@@ -217,7 +219,13 @@ const CreditCardSelectorModal = ({
|
|
|
217
219
|
size={ModalSize.MEDIUM}
|
|
218
220
|
title={intl.formatMessage(messages.modalTitle)}
|
|
219
221
|
actions={
|
|
220
|
-
<Button
|
|
222
|
+
<Button
|
|
223
|
+
color="brand"
|
|
224
|
+
variant="primary"
|
|
225
|
+
size="small"
|
|
226
|
+
fullWidth={true}
|
|
227
|
+
onClick={() => onChange(selected)}
|
|
228
|
+
>
|
|
221
229
|
<FormattedMessage {...messages.modalTitle} />
|
|
222
230
|
</Button>
|
|
223
231
|
}
|
|
@@ -39,7 +39,13 @@ const DownloadContractButton = ({ contract, className }: DownloadContractButtonP
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<Button
|
|
42
|
+
<Button
|
|
43
|
+
size="small"
|
|
44
|
+
className={className}
|
|
45
|
+
color="brand"
|
|
46
|
+
variant="secondary"
|
|
47
|
+
onClick={downloadContract}
|
|
48
|
+
>
|
|
43
49
|
<FormattedMessage {...messages.contractDownloadActionLabel} />
|
|
44
50
|
</Button>
|
|
45
51
|
);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import c from 'classnames';
|
|
2
2
|
import { PropsWithChildren } from 'react';
|
|
3
3
|
|
|
4
|
-
interface FormProps
|
|
5
|
-
|
|
4
|
+
interface FormProps extends React.DetailedHTMLProps<
|
|
5
|
+
React.FormHTMLAttributes<HTMLFormElement>,
|
|
6
|
+
HTMLFormElement
|
|
7
|
+
> {}
|
|
6
8
|
|
|
7
9
|
const Form = ({ children, onSubmit, className, name, noValidate = true }: FormProps) => {
|
|
8
10
|
return (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { PropsWithChildren, useState, useRef, CSSProperties } from 'react';
|
|
3
3
|
import { HttpStatusCode } from 'utils/errors/HttpError';
|
|
4
4
|
import { Icon, IconTypeEnum } from './index';
|
|
@@ -69,6 +69,7 @@ const IconContainer = ({ name, enumKey }: IconContainerProps) => {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
const clipboardCopy = () => {
|
|
72
|
+
// eslint-disable-next-line compat/compat
|
|
72
73
|
navigator.clipboard.writeText(`${ENUM_NAME}.${enumKey}`);
|
|
73
74
|
setShowTooltip(true);
|
|
74
75
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -78,7 +78,8 @@ export const Modal = ({
|
|
|
78
78
|
className="modal__closeButton"
|
|
79
79
|
onClick={(e) => props.onRequestClose?.(e)}
|
|
80
80
|
title={intl.formatMessage(messages.closeDialog)}
|
|
81
|
-
color="
|
|
81
|
+
color="brand"
|
|
82
|
+
variant="tertiary"
|
|
82
83
|
size="small"
|
|
83
84
|
>
|
|
84
85
|
<Icon name={IconTypeEnum.ROUND_CLOSE} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ButtonElement, Input, Alert, VariantType } from '@openfun/cunningham-react';
|
|
2
2
|
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
|
3
|
-
import { FormProvider, useForm } from 'react-hook-form';
|
|
3
|
+
import { FormProvider, Resolver, useForm } from 'react-hook-form';
|
|
4
4
|
import * as Yup from 'yup';
|
|
5
5
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
6
6
|
import { useEffect, useMemo, useRef } from 'react';
|
|
@@ -78,7 +78,7 @@ const OpenEdxFullNameForm = () => {
|
|
|
78
78
|
defaultValues,
|
|
79
79
|
mode: 'onBlur',
|
|
80
80
|
reValidateMode: 'onChange',
|
|
81
|
-
resolver: yupResolver(validationSchema)
|
|
81
|
+
resolver: yupResolver(validationSchema) as Resolver<OpenEdxFullNameFormValues>,
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
const { getValues, register, handleSubmit, reset, formState } = form;
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
height: 24px;
|
|
19
19
|
padding: 0 12px;
|
|
20
20
|
border-radius: 24px;
|
|
21
|
-
font-family: var(--c--
|
|
22
|
-
font-weight: var(--c--
|
|
21
|
+
font-family: var(--c--globals--font--families--accent);
|
|
22
|
+
font-weight: var(--c--globals--font--weights--medium);
|
|
23
23
|
font-size: rem-calc(12px);
|
|
24
24
|
|
|
25
25
|
&--canceled,
|
|
@@ -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 RegisteredAddress from '.';
|
|
@@ -91,7 +91,8 @@ const RegisteredAddress = ({ promote, select, edit, remove, address }: Props) =>
|
|
|
91
91
|
</Button>
|
|
92
92
|
<Button
|
|
93
93
|
aria-label={intl.formatMessage(messages.editButtonLabel, { title: address.title })}
|
|
94
|
-
color="
|
|
94
|
+
color="brand"
|
|
95
|
+
variant="secondary"
|
|
95
96
|
size="small"
|
|
96
97
|
onClick={() => edit(address)}
|
|
97
98
|
>
|
|
@@ -101,7 +102,8 @@ const RegisteredAddress = ({ promote, select, edit, remove, address }: Props) =>
|
|
|
101
102
|
aria-label={intl.formatMessage(messages.deleteButtonLabel, {
|
|
102
103
|
title: address.title,
|
|
103
104
|
})}
|
|
104
|
-
color="
|
|
105
|
+
color="brand"
|
|
106
|
+
variant="secondary"
|
|
105
107
|
size="small"
|
|
106
108
|
disabled={address.is_main}
|
|
107
109
|
onClick={() => remove(address)}
|
|
@@ -52,7 +52,7 @@ export const CreateAddressFormModal = (props: AddressFormModalProps) => {
|
|
|
52
52
|
size={ModalSize.MEDIUM}
|
|
53
53
|
title={intl.formatMessage(messages.title)}
|
|
54
54
|
actions={
|
|
55
|
-
<Button color="primary" size="small" onClick={handleSubmit(onSubmit)}>
|
|
55
|
+
<Button color="brand" variant="primary" size="small" onClick={handleSubmit(onSubmit)}>
|
|
56
56
|
<FormattedMessage {...messages.submit} />
|
|
57
57
|
</Button>
|
|
58
58
|
}
|
|
@@ -49,7 +49,7 @@ export const EditAddressFormModal = ({
|
|
|
49
49
|
size={ModalSize.MEDIUM}
|
|
50
50
|
title={intl.formatMessage(messages.title)}
|
|
51
51
|
actions={
|
|
52
|
-
<Button color="primary" size="small" onClick={handleSubmit(onSubmit)}>
|
|
52
|
+
<Button color="brand" variant="primary" size="small" onClick={handleSubmit(onSubmit)}>
|
|
53
53
|
<FormattedMessage {...messages.save} />
|
|
54
54
|
</Button>
|
|
55
55
|
}
|
|
@@ -75,7 +75,8 @@ export const AddressSelector = () => {
|
|
|
75
75
|
<Button
|
|
76
76
|
size="small"
|
|
77
77
|
icon={<span className="material-icons">edit</span>}
|
|
78
|
-
color="
|
|
78
|
+
color="brand"
|
|
79
|
+
variant="tertiary"
|
|
79
80
|
onClick={editFormModal.open}
|
|
80
81
|
fullWidth={isMobile}
|
|
81
82
|
>
|
|
@@ -85,7 +86,8 @@ export const AddressSelector = () => {
|
|
|
85
86
|
<Button
|
|
86
87
|
size="small"
|
|
87
88
|
icon={<span className="material-icons">add</span>}
|
|
88
|
-
color="
|
|
89
|
+
color="brand"
|
|
90
|
+
variant="primary"
|
|
89
91
|
onClick={createFormModal.open}
|
|
90
92
|
fullWidth={isMobile}
|
|
91
93
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
3
|
-
import { FormProvider, useForm } from 'react-hook-form';
|
|
3
|
+
import { FormProvider, Resolver, useForm } from 'react-hook-form';
|
|
4
4
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
5
5
|
import * as Yup from 'yup';
|
|
6
6
|
import { Step, StepLabel, Stepper } from '@mui/material';
|
|
@@ -197,7 +197,7 @@ const BatchOrderForm = () => {
|
|
|
197
197
|
const form = useForm<BatchOrder>({
|
|
198
198
|
defaultValues: batchOrder || defaultValues,
|
|
199
199
|
mode: 'onBlur',
|
|
200
|
-
resolver: yupResolver(validationSchema)
|
|
200
|
+
resolver: yupResolver(validationSchema) as Resolver<BatchOrder>,
|
|
201
201
|
});
|
|
202
202
|
const { watch } = form;
|
|
203
203
|
const values = watch();
|
|
@@ -268,7 +268,8 @@ const BatchOrderForm = () => {
|
|
|
268
268
|
}
|
|
269
269
|
}}
|
|
270
270
|
hidden={activeStep === 0}
|
|
271
|
-
color="
|
|
271
|
+
color="brand"
|
|
272
|
+
variant="tertiary"
|
|
272
273
|
>
|
|
273
274
|
<FormattedMessage {...messages.previousButton} />
|
|
274
275
|
</Button>
|
|
@@ -143,7 +143,7 @@ const messages = defineMessages({
|
|
|
143
143
|
id: 'components.SaleTunnel.Information.cpf.description',
|
|
144
144
|
description: 'Explanatory text for the CPF payment option',
|
|
145
145
|
defaultMessage:
|
|
146
|
-
'
|
|
146
|
+
'Purchase your training course by using your Personal Training Account (CPF) on Mon Compte Formation.',
|
|
147
147
|
},
|
|
148
148
|
cpfButtonLabel: {
|
|
149
149
|
id: 'components.SaleTunnel.Information.cpf.buttonLabel',
|
|
@@ -236,7 +236,12 @@ export const SaleTunnelInformationSingular = () => {
|
|
|
236
236
|
</div>
|
|
237
237
|
)}
|
|
238
238
|
{paymentMode === PaymentMode.CPF ? (
|
|
239
|
-
<CpfPayment
|
|
239
|
+
<CpfPayment
|
|
240
|
+
deepLink={deepLink!}
|
|
241
|
+
discount={discount}
|
|
242
|
+
voucherError={voucherError}
|
|
243
|
+
setVoucherError={setVoucherError}
|
|
244
|
+
/>
|
|
240
245
|
) : (
|
|
241
246
|
<>
|
|
242
247
|
{needsPayment && (
|
|
@@ -443,7 +448,13 @@ const Voucher = ({
|
|
|
443
448
|
label={intl.formatMessage(messages.voucherTitle)}
|
|
444
449
|
disabled={!!voucherCode}
|
|
445
450
|
/>
|
|
446
|
-
<Button
|
|
451
|
+
<Button
|
|
452
|
+
size="small"
|
|
453
|
+
color="brand"
|
|
454
|
+
variant="primary"
|
|
455
|
+
onClick={submitVoucher}
|
|
456
|
+
disabled={!!voucherCode}
|
|
457
|
+
>
|
|
447
458
|
<FormattedMessage {...messages.voucherValidate} />
|
|
448
459
|
</Button>
|
|
449
460
|
</div>
|
|
@@ -493,14 +504,24 @@ const PaymentScheduleBlock = ({ schedule }: { schedule: PaymentSchedule }) => {
|
|
|
493
504
|
);
|
|
494
505
|
};
|
|
495
506
|
|
|
496
|
-
const CpfPayment = ({
|
|
507
|
+
const CpfPayment = ({
|
|
508
|
+
deepLink,
|
|
509
|
+
discount,
|
|
510
|
+
voucherError,
|
|
511
|
+
setVoucherError,
|
|
512
|
+
}: {
|
|
513
|
+
deepLink: string;
|
|
514
|
+
discount?: string;
|
|
515
|
+
voucherError: HttpError | null;
|
|
516
|
+
setVoucherError: (value: HttpError | null) => void;
|
|
517
|
+
}) => {
|
|
497
518
|
return (
|
|
498
519
|
<div className="sale-tunnel__cpf">
|
|
499
520
|
<p className="description mb-s">
|
|
500
521
|
<FormattedMessage {...messages.cpfDescription} />
|
|
501
522
|
</p>
|
|
502
523
|
<Button
|
|
503
|
-
color="
|
|
524
|
+
color="brand"
|
|
504
525
|
fullWidth={true}
|
|
505
526
|
href={deepLink}
|
|
506
527
|
target="_blank"
|
|
@@ -508,6 +529,7 @@ const CpfPayment = ({ deepLink }: { deepLink: string }) => {
|
|
|
508
529
|
>
|
|
509
530
|
<FormattedMessage {...messages.cpfButtonLabel} />
|
|
510
531
|
</Button>
|
|
532
|
+
<Voucher discount={discount} voucherError={voucherError} setVoucherError={setVoucherError} />
|
|
511
533
|
</div>
|
|
512
534
|
);
|
|
513
535
|
};
|
|
@@ -110,9 +110,6 @@ const SubscriptionButton = ({ buildOrderPayload }: Props) => {
|
|
|
110
110
|
paymentMode,
|
|
111
111
|
} = useSaleTunnelContext();
|
|
112
112
|
|
|
113
|
-
if (paymentMode === PaymentMode.CPF) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
113
|
const { methods: orderMethods } = useOrders(undefined, { enabled: false });
|
|
117
114
|
const { methods: batchOrderMethods } = useBatchOrder();
|
|
118
115
|
const [state, setState] = useState<ComponentStates>(ComponentStates.IDLE);
|
|
@@ -138,12 +135,17 @@ const SubscriptionButton = ({ buildOrderPayload }: Props) => {
|
|
|
138
135
|
return;
|
|
139
136
|
}
|
|
140
137
|
|
|
141
|
-
if (!billingAddress && needsPayment) {
|
|
138
|
+
if (!billingAddress && needsPayment && paymentMode !== PaymentMode.CPF) {
|
|
142
139
|
handleError(SubscriptionErrorMessageId.ERROR_ADDRESS);
|
|
143
140
|
return;
|
|
144
141
|
}
|
|
145
142
|
|
|
146
|
-
if (
|
|
143
|
+
if (
|
|
144
|
+
!saleTunnelProps.isWithdrawable &&
|
|
145
|
+
!hasWaivedWithdrawalRight &&
|
|
146
|
+
needsPayment &&
|
|
147
|
+
paymentMode !== PaymentMode.CPF
|
|
148
|
+
) {
|
|
147
149
|
handleError(SubscriptionErrorMessageId.ERROR_WITHDRAWAL_RIGHT);
|
|
148
150
|
return;
|
|
149
151
|
}
|
|
@@ -32,12 +32,13 @@
|
|
|
32
32
|
&__right {
|
|
33
33
|
flex: 1;
|
|
34
34
|
overflow: hidden;
|
|
35
|
+
position: relative;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
&__column {
|
|
38
39
|
display: flex;
|
|
39
40
|
flex-direction: column;
|
|
40
|
-
gap: var(--c--
|
|
41
|
+
gap: var(--c--globals--spacings--b);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -135,7 +136,7 @@
|
|
|
135
136
|
margin-top: 1rem;
|
|
136
137
|
display: flex;
|
|
137
138
|
justify-content: space-between;
|
|
138
|
-
font-size: var(--c--
|
|
139
|
+
font-size: var(--c--globals--font--sizes--md);
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
|
|
@@ -159,8 +160,8 @@
|
|
|
159
160
|
gap: 0.5rem;
|
|
160
161
|
|
|
161
162
|
.title {
|
|
162
|
-
font-weight: var(--c--
|
|
163
|
-
font-size: var(--c--
|
|
163
|
+
font-weight: var(--c--globals--font--weights--bold);
|
|
164
|
+
font-size: var(--c--globals--font--sizes--md);
|
|
164
165
|
margin: 0 0 0.5rem;
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -209,17 +210,17 @@
|
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
.block-title {
|
|
212
|
-
font-size: var(--c--
|
|
213
|
+
font-size: var(--c--globals--font--sizes--md);
|
|
213
214
|
color: r-theme-val(sale-tunnel, title-color);
|
|
214
|
-
font-weight: var(--c--
|
|
215
|
+
font-weight: var(--c--globals--font--weights--extrabold);
|
|
215
216
|
text-align: left;
|
|
216
|
-
font-family: var(--c--
|
|
217
|
+
font-family: var(--c--globals--font--families--accent);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
.sub-block-title {
|
|
220
221
|
font-size: 12px;
|
|
221
222
|
color: r-theme-val(sale-tunnel, title-color);
|
|
222
|
-
font-weight: var(--c--
|
|
223
|
+
font-weight: var(--c--globals--font--weights--bold);
|
|
223
224
|
text-align: left;
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -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(
|
|
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
|
});
|