richie-education 2.25.0-b2.dev176 → 2.25.0-b2.dev183
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/js/api/joanie.spec.ts +2 -1
- package/js/api/joanie.ts +1 -51
- package/js/api/lms/dummy.ts +22 -0
- package/js/api/lms/openedx-fonzie.spec.ts +20 -0
- package/js/api/lms/openedx-fonzie.ts +35 -0
- package/js/api/utils.ts +51 -0
- package/js/components/AddressesManagement/AddressForm/index.tsx +15 -14
- package/js/components/Form/Form/index.tsx +23 -0
- package/js/components/Form/index.ts +3 -0
- package/js/hooks/useDashboardAddressForm.tsx +13 -12
- package/js/hooks/useOpenEdxProfile/index.ts +45 -0
- package/js/hooks/useOpenEdxProfile/utils/index.spec.ts +69 -0
- package/js/hooks/useOpenEdxProfile/utils/index.ts +131 -0
- package/js/pages/DashboardAddressesManagement/_styles.scss +1 -1
- package/js/pages/DashboardAddressesManagement/index.spec.tsx +34 -12
- package/js/pages/DashboardAddressesManagement/index.tsx +14 -9
- package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.spec.tsx +156 -115
- package/js/pages/DashboardCreditCardsManagement/DashboardEditCreditCard.tsx +9 -8
- package/js/pages/DashboardCreditCardsManagement/index.spec.tsx +92 -121
- package/js/pages/DashboardOpenEdxProfile/index.spec.tsx +131 -0
- package/js/pages/DashboardOpenEdxProfile/index.tsx +237 -0
- package/js/pages/DashboardPreferences/index.tsx +2 -0
- package/js/types/api.ts +6 -1
- package/js/types/openEdx.ts +41 -0
- package/js/utils/react-query/useSessionMutation/index.spec.tsx +1 -1
- package/js/utils/react-query/useSessionQuery/index.spec.tsx +1 -1
- package/js/utils/test/factories/openEdx.tsx +23 -0
- package/js/widgets/Dashboard/components/DashboardBox/_styles.scss +6 -0
- package/js/widgets/Dashboard/components/DashboardBox/index.tsx +4 -0
- package/package.json +1 -1
- package/scss/objects/_form.scss +9 -2
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { IntlShape, defineMessages } from 'react-intl';
|
|
2
|
+
import countries from 'i18n-iso-countries';
|
|
3
|
+
import { OpenEdxGender, OpenEdxLevelOfEducation, OpenEdxApiProfile } from 'types/openEdx';
|
|
4
|
+
import { Maybe } from 'types/utils';
|
|
5
|
+
|
|
6
|
+
export const levelOfEducationMessages = defineMessages<OpenEdxLevelOfEducation>({
|
|
7
|
+
[OpenEdxLevelOfEducation.MASTER_OR_PROFESSIONNAL_DEGREE]: {
|
|
8
|
+
id: 'openEdxProfile.levelOfEducation.masterOrProfessionnalDegree',
|
|
9
|
+
description:
|
|
10
|
+
'Translation for level of education "master or professional degree" in openEdx profile',
|
|
11
|
+
defaultMessage: 'Master',
|
|
12
|
+
},
|
|
13
|
+
[OpenEdxLevelOfEducation.PHD_OR_DOCTORATE]: {
|
|
14
|
+
id: 'openEdxProfile.levelOfEducation.phdOrDoctorate',
|
|
15
|
+
description: 'Translation for level of education "phd or doctorate" in openEdx profile',
|
|
16
|
+
defaultMessage: 'PHD',
|
|
17
|
+
},
|
|
18
|
+
[OpenEdxLevelOfEducation.BACHELOR_DEGREE]: {
|
|
19
|
+
id: 'openEdxProfile.levelOfEducation.bachelorDegree',
|
|
20
|
+
description: 'Translation for level of education "bachelor degree" in openEdx profile',
|
|
21
|
+
defaultMessage: 'Bachelor degree',
|
|
22
|
+
},
|
|
23
|
+
[OpenEdxLevelOfEducation.ASSOCIATE_DEGREE]: {
|
|
24
|
+
id: 'openEdxProfile.levelOfEducation.associateDegree',
|
|
25
|
+
description: 'Translation for level of education "associate degree" in openEdx profile',
|
|
26
|
+
defaultMessage: 'Associate degree',
|
|
27
|
+
},
|
|
28
|
+
[OpenEdxLevelOfEducation.SECONDARY_OR_HIGH_SCHOOL]: {
|
|
29
|
+
id: 'openEdxProfile.levelOfEducation.secondaryOrHighSchool',
|
|
30
|
+
description: 'Translation for level of education "secondary or high school" in openEdx profile',
|
|
31
|
+
defaultMessage: 'Secondary or high school',
|
|
32
|
+
},
|
|
33
|
+
[OpenEdxLevelOfEducation.JUNIOR_SECONDARY_OR_MIDDLE_SCHOOL]: {
|
|
34
|
+
id: 'openEdxProfile.levelOfEducation.juniorSecondaryOrMiddleSchool',
|
|
35
|
+
description:
|
|
36
|
+
'Translation for level of education "junior secondary or middle school" in openEdx profile',
|
|
37
|
+
defaultMessage: 'Junior secondary or middle school',
|
|
38
|
+
},
|
|
39
|
+
[OpenEdxLevelOfEducation.ELEMENTARY_PRIMARY_SCHOOL]: {
|
|
40
|
+
id: 'openEdxProfile.levelOfEducation.elementaryPrimarySchool',
|
|
41
|
+
description:
|
|
42
|
+
'Translation for level of education "elementary primary school" in openEdx profile',
|
|
43
|
+
defaultMessage: 'Elementary primary school',
|
|
44
|
+
},
|
|
45
|
+
[OpenEdxLevelOfEducation.NONE]: {
|
|
46
|
+
id: 'openEdxProfile.levelOfEducation.none',
|
|
47
|
+
description: 'Translation for level of education "none" in openEdx profile',
|
|
48
|
+
defaultMessage: 'None',
|
|
49
|
+
},
|
|
50
|
+
[OpenEdxLevelOfEducation.OTHER]: {
|
|
51
|
+
id: 'openEdxProfile.levelOfEducation.other',
|
|
52
|
+
description: 'Translation for level of education "other" in openEdx profile',
|
|
53
|
+
defaultMessage: 'Other',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export const genderMessages = defineMessages<OpenEdxGender>({
|
|
58
|
+
[OpenEdxGender.MALE]: {
|
|
59
|
+
id: 'openEdxProfile.gender.male',
|
|
60
|
+
description: 'Translation for gender "male" in openEdx profile',
|
|
61
|
+
defaultMessage: 'Male',
|
|
62
|
+
},
|
|
63
|
+
[OpenEdxGender.FEMALE]: {
|
|
64
|
+
id: 'openEdxProfile.gender.female',
|
|
65
|
+
description: 'Translation for gender "female" in openEdx profile',
|
|
66
|
+
defaultMessage: 'Female',
|
|
67
|
+
},
|
|
68
|
+
[OpenEdxGender.OTHER]: {
|
|
69
|
+
id: 'openEdxProfile.gender.other',
|
|
70
|
+
description: 'Translation for gender "other" in openEdx profile',
|
|
71
|
+
defaultMessage: 'Other',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export interface OpenEdxProfile {
|
|
76
|
+
username: Maybe<string>;
|
|
77
|
+
name: Maybe<string>;
|
|
78
|
+
country: Maybe<string>;
|
|
79
|
+
yearOfBirth: Maybe<string>;
|
|
80
|
+
levelOfEducation: Maybe<string>;
|
|
81
|
+
email: Maybe<string>;
|
|
82
|
+
dateJoined: Maybe<string>;
|
|
83
|
+
gender: Maybe<string>;
|
|
84
|
+
language: Maybe<string>;
|
|
85
|
+
favoriteLanguage: Maybe<string>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const parseOpenEdxApiProfile = (
|
|
89
|
+
intl: IntlShape,
|
|
90
|
+
data?: OpenEdxApiProfile,
|
|
91
|
+
): OpenEdxProfile => {
|
|
92
|
+
const [languageCode] = intl.locale.split('-');
|
|
93
|
+
const defaultValues: OpenEdxProfile = {
|
|
94
|
+
username: ' - ',
|
|
95
|
+
name: ' - ',
|
|
96
|
+
email: ' - ',
|
|
97
|
+
language: ' - ',
|
|
98
|
+
country: ' - ',
|
|
99
|
+
levelOfEducation: ' - ',
|
|
100
|
+
gender: ' - ',
|
|
101
|
+
yearOfBirth: ' - ',
|
|
102
|
+
favoriteLanguage: ' - ',
|
|
103
|
+
dateJoined: undefined,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const languageNames = new Intl.DisplayNames([intl.locale], { type: 'language' });
|
|
107
|
+
const parsedData = data
|
|
108
|
+
? {
|
|
109
|
+
username: data.username || defaultValues.username,
|
|
110
|
+
name: data.name || defaultValues.name,
|
|
111
|
+
email: data.email || defaultValues.email,
|
|
112
|
+
yearOfBirth: data.year_of_birth || defaultValues.yearOfBirth,
|
|
113
|
+
dateJoined: data.date_joined || defaultValues.dateJoined,
|
|
114
|
+
levelOfEducation: data.level_of_education
|
|
115
|
+
? intl.formatMessage(levelOfEducationMessages[data.level_of_education])
|
|
116
|
+
: defaultValues.levelOfEducation,
|
|
117
|
+
gender: data.gender
|
|
118
|
+
? intl.formatMessage(genderMessages[data.gender])
|
|
119
|
+
: defaultValues.gender,
|
|
120
|
+
country: data.country
|
|
121
|
+
? countries.getName(data.country, languageCode)
|
|
122
|
+
: defaultValues.country,
|
|
123
|
+
language: data['pref-lang'] ? languageNames.of(data['pref-lang']) : defaultValues.language,
|
|
124
|
+
favoriteLanguage: data.language_proficiencies.length
|
|
125
|
+
? languageNames.of(data.language_proficiencies[0].code)
|
|
126
|
+
: defaultValues.favoriteLanguage,
|
|
127
|
+
}
|
|
128
|
+
: defaultValues;
|
|
129
|
+
|
|
130
|
+
return parsedData;
|
|
131
|
+
};
|
|
@@ -11,7 +11,10 @@ import {
|
|
|
11
11
|
import { IntlProvider } from 'react-intl';
|
|
12
12
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
13
13
|
import fetchMock from 'fetch-mock';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
UserFactory,
|
|
16
|
+
RichieContextFactory as mockRichieContextFactory,
|
|
17
|
+
} from 'utils/test/factories/richie';
|
|
15
18
|
import { AddressFactory } from 'utils/test/factories/joanie';
|
|
16
19
|
import { SessionProvider } from 'contexts/SessionContext';
|
|
17
20
|
import { DashboardTest } from 'widgets/Dashboard/components/DashboardTest';
|
|
@@ -21,13 +24,15 @@ import { resolveAll } from 'utils/resolveAll';
|
|
|
21
24
|
import { createTestQueryClient } from 'utils/test/createTestQueryClient';
|
|
22
25
|
import { expectBannerError } from 'utils/test/expectBanner';
|
|
23
26
|
import { HttpStatusCode } from 'utils/errors/HttpError';
|
|
27
|
+
import { User } from 'types/User';
|
|
28
|
+
import { OpenEdxApiProfileFactory } from 'utils/test/factories/openEdx';
|
|
24
29
|
|
|
25
30
|
import { LearnerDashboardPaths } from 'widgets/Dashboard/utils/learnerRoutesPaths';
|
|
26
31
|
|
|
27
32
|
jest.mock('utils/context', () => ({
|
|
28
33
|
__esModule: true,
|
|
29
34
|
default: mockRichieContextFactory({
|
|
30
|
-
authentication: { backend: 'fonzie', endpoint: 'https://
|
|
35
|
+
authentication: { backend: 'fonzie', endpoint: 'https://endpoint.test' },
|
|
31
36
|
joanie_backend: { endpoint: 'https://joanie.endpoint' },
|
|
32
37
|
}).one(),
|
|
33
38
|
}));
|
|
@@ -37,7 +42,24 @@ jest.mock('utils/indirection/window', () => ({
|
|
|
37
42
|
}));
|
|
38
43
|
|
|
39
44
|
describe('<DashAddressesManagement/>', () => {
|
|
45
|
+
let richieUser: User;
|
|
40
46
|
beforeEach(() => {
|
|
47
|
+
richieUser = UserFactory().one();
|
|
48
|
+
const openEdxProfile = OpenEdxApiProfileFactory({
|
|
49
|
+
username: richieUser.username,
|
|
50
|
+
email: richieUser.email,
|
|
51
|
+
name: richieUser.full_name,
|
|
52
|
+
}).one();
|
|
53
|
+
const { 'pref-lang': prefLang, ...openEdxAccount } = openEdxProfile;
|
|
54
|
+
|
|
55
|
+
fetchMock.get(
|
|
56
|
+
`https://endpoint.test/api/user/v1/accounts/${richieUser.username}`,
|
|
57
|
+
openEdxAccount,
|
|
58
|
+
);
|
|
59
|
+
fetchMock.get(`https://endpoint.test/api/user/v1/preferences/${richieUser.username}`, {
|
|
60
|
+
'pref-lang': prefLang,
|
|
61
|
+
});
|
|
62
|
+
|
|
41
63
|
fetchMock.get('https://joanie.endpoint/api/v1.0/orders/', []);
|
|
42
64
|
fetchMock.get('https://joanie.endpoint/api/v1.0/credit-cards/', []);
|
|
43
65
|
});
|
|
@@ -51,7 +73,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
51
73
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', []);
|
|
52
74
|
await act(async () => {
|
|
53
75
|
render(
|
|
54
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
76
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
55
77
|
<IntlProvider locale="en">
|
|
56
78
|
<SessionProvider>
|
|
57
79
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -73,7 +95,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
73
95
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', addresses);
|
|
74
96
|
await act(async () => {
|
|
75
97
|
render(
|
|
76
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
98
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
77
99
|
<IntlProvider locale="en">
|
|
78
100
|
<SessionProvider>
|
|
79
101
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -96,7 +118,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
96
118
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', addresses);
|
|
97
119
|
await act(async () => {
|
|
98
120
|
render(
|
|
99
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
121
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
100
122
|
<IntlProvider locale="en">
|
|
101
123
|
<SessionProvider>
|
|
102
124
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -142,7 +164,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
142
164
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', addresses);
|
|
143
165
|
await act(async () => {
|
|
144
166
|
render(
|
|
145
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
167
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
146
168
|
<IntlProvider locale="en">
|
|
147
169
|
<SessionProvider>
|
|
148
170
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -194,7 +216,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
194
216
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', addresses);
|
|
195
217
|
await act(async () => {
|
|
196
218
|
render(
|
|
197
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
219
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
198
220
|
<IntlProvider locale="en">
|
|
199
221
|
<SessionProvider>
|
|
200
222
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -218,7 +240,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
218
240
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', addresses);
|
|
219
241
|
await act(async () => {
|
|
220
242
|
render(
|
|
221
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
243
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
222
244
|
<IntlProvider locale="en">
|
|
223
245
|
<SessionProvider>
|
|
224
246
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -245,7 +267,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
245
267
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', addresses);
|
|
246
268
|
await act(async () => {
|
|
247
269
|
render(
|
|
248
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
270
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
249
271
|
<IntlProvider locale="en">
|
|
250
272
|
<SessionProvider>
|
|
251
273
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -270,7 +292,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
270
292
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', []);
|
|
271
293
|
await act(async () => {
|
|
272
294
|
render(
|
|
273
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
295
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
274
296
|
<IntlProvider locale="en">
|
|
275
297
|
<SessionProvider>
|
|
276
298
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -293,7 +315,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
293
315
|
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', [address]);
|
|
294
316
|
await act(async () => {
|
|
295
317
|
render(
|
|
296
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
318
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
297
319
|
<IntlProvider locale="en">
|
|
298
320
|
<SessionProvider>
|
|
299
321
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -323,7 +345,7 @@ describe('<DashAddressesManagement/>', () => {
|
|
|
323
345
|
|
|
324
346
|
await act(async () => {
|
|
325
347
|
render(
|
|
326
|
-
<QueryClientProvider client={createTestQueryClient({ user:
|
|
348
|
+
<QueryClientProvider client={createTestQueryClient({ user: richieUser })}>
|
|
327
349
|
<IntlProvider locale="en">
|
|
328
350
|
<SessionProvider>
|
|
329
351
|
<DashboardTest initialRoute={LearnerDashboardPaths.PREFERENCES} />
|
|
@@ -7,6 +7,7 @@ import { Icon, IconTypeEnum } from 'components/Icon';
|
|
|
7
7
|
import { Spinner } from 'components/Spinner';
|
|
8
8
|
import { useAddressesManagement } from 'hooks/useAddressesManagement';
|
|
9
9
|
import { Address } from 'types/Joanie';
|
|
10
|
+
import { DashboardBox } from 'widgets/Dashboard/components/DashboardBox';
|
|
10
11
|
import { DashboardAddressBox } from './DashboardAddressBox';
|
|
11
12
|
|
|
12
13
|
const messages = defineMessages({
|
|
@@ -72,15 +73,19 @@ export const DashboardAddressesManagement = ({
|
|
|
72
73
|
<FormattedMessage {...messages.emptyList} />
|
|
73
74
|
</p>
|
|
74
75
|
)}
|
|
75
|
-
{addressesList.
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
{addressesList.length > 0 && (
|
|
77
|
+
<DashboardBox.List>
|
|
78
|
+
{addressesList.map((address) => (
|
|
79
|
+
<DashboardAddressBox
|
|
80
|
+
key={address.id}
|
|
81
|
+
address={address}
|
|
82
|
+
edit={(_address) => onClickEdit?.(_address)}
|
|
83
|
+
remove={remove}
|
|
84
|
+
promote={promote}
|
|
85
|
+
/>
|
|
86
|
+
))}
|
|
87
|
+
</DashboardBox.List>
|
|
88
|
+
)}
|
|
84
89
|
<Button color="secondary" fullWidth onClick={() => onClickCreate?.()}>
|
|
85
90
|
<Icon name={IconTypeEnum.PLUS} className="button__icon" />
|
|
86
91
|
<FormattedMessage {...messages.add} />
|