richie-education 3.2.1-dev9 → 3.2.2-dev26
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/i18n/locales/ar-SA.json +29 -1
- package/i18n/locales/es-ES.json +29 -1
- package/i18n/locales/fa-IR.json +29 -1
- package/i18n/locales/fr-CA.json +29 -1
- package/i18n/locales/fr-FR.json +29 -1
- package/i18n/locales/ko-KR.json +29 -1
- package/i18n/locales/pt-PT.json +29 -1
- package/i18n/locales/ru-RU.json +29 -1
- package/i18n/locales/vi-VN.json +29 -1
- package/js/api/joanie.ts +144 -0
- package/js/components/PaymentInterfaces/types.ts +7 -0
- package/js/components/PaymentScheduleGrid/index.tsx +4 -2
- package/js/components/SaleTunnel/AddressSelector/index.spec.tsx +9 -2
- package/js/components/SaleTunnel/GenericSaleTunnel.tsx +33 -0
- package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationGroup.tsx +253 -0
- package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationSingular.tsx +314 -0
- package/js/components/SaleTunnel/SaleTunnelInformation/StepContent.tsx +528 -0
- package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +47 -261
- package/js/components/SaleTunnel/SaleTunnelSuccess/index.tsx +25 -11
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +54 -6
- package/js/components/SaleTunnel/_styles.scss +55 -0
- package/js/components/SaleTunnel/index.full-process-b2b.spec.tsx +356 -0
- package/js/components/SaleTunnel/{index.full-process.spec.tsx → index.full-process-b2c.spec.tsx} +4 -1
- package/js/components/SaleTunnel/index.spec.tsx +130 -1
- package/js/hooks/useBatchOrder/index.tsx +36 -0
- package/js/hooks/useContractArchive/index.ts +2 -0
- package/js/hooks/useOfferingOrganizations/index.tsx +38 -0
- package/js/hooks/useOrganizationAgreements.tsx/index.tsx +66 -0
- package/js/hooks/useOrganizationQuotes/index.tsx +56 -0
- package/js/hooks/usePaymentPlan.tsx +2 -1
- package/js/hooks/useTeacherPendingAgreementsCount/index.ts +34 -0
- package/js/pages/DashboardBatchOrderLayout/_styles.scss +5 -0
- package/js/pages/DashboardBatchOrderLayout/index.spec.tsx +78 -0
- package/js/pages/DashboardBatchOrderLayout/index.tsx +45 -0
- package/js/pages/DashboardBatchOrders/index.spec.tsx +237 -0
- package/js/pages/DashboardBatchOrders/index.tsx +84 -0
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardCourseContractsLayout/index.tsx +0 -1
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +3 -1
- package/js/pages/TeacherDashboardOrganizationAgreements/AgreementActionsBar.tsx +49 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/BulkAgreementContractButton.tsx +79 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/OrganizationAgreementFrame.tsx +71 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/SignOrganizationAgreementButton.tsx +60 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/hooks/useAgreementsAbilities.tsx +8 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/hooks/useHasAgreementToDownload.tsx +27 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/hooks/useTeacherAgreementsToSign.tsx +32 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/index.spec.tsx +433 -0
- package/js/pages/TeacherDashboardOrganizationAgreements/index.tsx +130 -0
- package/js/pages/TeacherDashboardOrganizationAgreementsLayout/index.tsx +25 -0
- package/js/pages/TeacherDashboardOrganizationCourseLoader/index.spec.tsx +9 -0
- package/js/pages/TeacherDashboardOrganizationQuotes/_styles.scss +40 -0
- package/js/pages/TeacherDashboardOrganizationQuotes/index.full-process.spec.tsx +194 -0
- package/js/pages/TeacherDashboardOrganizationQuotes/index.spec.tsx +144 -0
- package/js/pages/TeacherDashboardOrganizationQuotes/index.tsx +521 -0
- package/js/pages/TeacherDashboardOrganizationQuotesLayout/index.tsx +26 -0
- 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 +216 -1
- package/js/utils/AbilitiesHelper/agreementAbilities.ts +14 -0
- package/js/utils/AbilitiesHelper/index.ts +7 -0
- package/js/utils/AbilitiesHelper/types.ts +12 -3
- package/js/utils/ObjectHelper/index.ts +20 -0
- package/js/utils/OrderHelper/index.ts +10 -0
- package/js/utils/errors/HttpError.ts +1 -0
- package/js/utils/test/factories/joanie.ts +156 -1
- package/js/widgets/Dashboard/components/DashboardBatchOrderLoader/_styles.scss +14 -0
- package/js/widgets/Dashboard/components/DashboardBatchOrderLoader/index.tsx +32 -0
- package/js/widgets/Dashboard/components/DashboardCard/index.spec.tsx +18 -0
- package/js/widgets/Dashboard/components/DashboardCard/index.stories.tsx +25 -2
- package/js/widgets/Dashboard/components/DashboardCard/index.tsx +4 -2
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/BatchOrderPaymentManager.tsx +88 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/index.tsx +216 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/DashboardBatchOrderSubItems.tsx +316 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/index.spec.tsx +27 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/index.tsx +175 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +5 -2
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrganizationBlock/index.tsx +4 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/_styles.scss +5 -0
- package/js/widgets/Dashboard/components/DashboardItem/_styles.scss +43 -0
- package/js/widgets/Dashboard/components/DashboardSidebar/components/AgreementNavLink/index.spec.tsx +214 -0
- package/js/widgets/Dashboard/components/DashboardSidebar/components/AgreementNavLink/index.tsx +47 -0
- package/js/widgets/Dashboard/components/LearnerDashboardSidebar/index.tsx +1 -0
- package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.spec.tsx +21 -3
- package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.tsx +9 -0
- package/js/widgets/Dashboard/utils/learnerRoutes.tsx +30 -0
- package/js/widgets/Dashboard/utils/learnerRoutesPaths.tsx +12 -0
- package/js/widgets/Dashboard/utils/teacherDashboardPaths.tsx +12 -0
- package/js/widgets/Dashboard/utils/teacherRoutes.tsx +17 -0
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +8 -2
- package/package.json +4 -1
- package/scss/colors/_theme.scss +1 -1
- package/scss/components/_index.scss +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JoanieUserProfile } from 'types/User';
|
|
2
|
-
import { Contract } from 'types/Joanie';
|
|
2
|
+
import { Agreement, Contract } from 'types/Joanie';
|
|
3
3
|
|
|
4
4
|
export enum JoanieUserProfileActions {
|
|
5
5
|
ACCESS_TEACHER_DASHBOARD = 'access_teacher_dashboard',
|
|
@@ -9,6 +9,10 @@ export enum ContractActions {
|
|
|
9
9
|
SIGN = 'sign',
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export enum AgreementActions {
|
|
13
|
+
SIGN = 'sign',
|
|
14
|
+
}
|
|
15
|
+
|
|
12
16
|
const JOANIE_PROFILE_KEYS = [
|
|
13
17
|
'abilities',
|
|
14
18
|
'full_name',
|
|
@@ -30,7 +34,12 @@ export const isContractEntity = (entity: Entity): entity is Contract => {
|
|
|
30
34
|
return CONTRACT_KEYS.every((key) => entity.hasOwnProperty(key));
|
|
31
35
|
};
|
|
32
36
|
|
|
37
|
+
export function isAgreementEntity(entity: any): entity is Agreement {
|
|
38
|
+
const AGREEMENT_KEYS = ['batch_order', 'abilities', 'id'];
|
|
39
|
+
return AGREEMENT_KEYS.every((key) => entity.hasOwnProperty(key));
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
// further entities and entity actions can be added here
|
|
34
43
|
// like Entity = JoanieUserProfileEntity | CourseEntity
|
|
35
|
-
export type Entity = JoanieUserProfile | Contract;
|
|
36
|
-
export type Actions = JoanieUserProfileActions | ContractActions;
|
|
44
|
+
export type Entity = JoanieUserProfile | Contract | Agreement;
|
|
45
|
+
export type Actions = JoanieUserProfileActions | ContractActions | AgreementActions;
|
|
@@ -11,4 +11,24 @@ export class ObjectHelper {
|
|
|
11
11
|
keys.forEach((key) => delete cleanedObject[key]);
|
|
12
12
|
return cleanedObject;
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
static removeEmptyFields<T>(obj: T): T | undefined {
|
|
16
|
+
if (Array.isArray(obj)) {
|
|
17
|
+
const arr = obj.map((v) => this.removeEmptyFields(v)).filter((v) => v !== undefined);
|
|
18
|
+
return arr.length > 0 ? (arr as unknown as T) : undefined;
|
|
19
|
+
}
|
|
20
|
+
if (obj && typeof obj === 'object') {
|
|
21
|
+
const cleanedEntries = Object.entries(obj)
|
|
22
|
+
.map(([k, v]) => [k, this.removeEmptyFields(v)])
|
|
23
|
+
.filter(([, v]) => {
|
|
24
|
+
if (v === undefined || v === null) return false;
|
|
25
|
+
if (typeof v === 'string' && v.trim() === '') return false;
|
|
26
|
+
if (Array.isArray(v) && v.length === 0) return false;
|
|
27
|
+
if (typeof v === 'object' && Object.keys(v).length === 0) return false;
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
return cleanedEntries.length > 0 ? (Object.fromEntries(cleanedEntries) as T) : undefined;
|
|
31
|
+
}
|
|
32
|
+
return obj;
|
|
33
|
+
}
|
|
14
34
|
}
|
|
@@ -104,4 +104,14 @@ export class OrderHelper {
|
|
|
104
104
|
if (!order) return true;
|
|
105
105
|
return PURCHASABLE_ORDER_STATES.includes(order.state);
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
static isFreeWithVoucher(order?: Order) {
|
|
109
|
+
if (!order) return false;
|
|
110
|
+
return order.total === 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static isFreeFromBatchOrder(order?: Order) {
|
|
114
|
+
if (!order) return false;
|
|
115
|
+
return order.from_batch_order === true;
|
|
116
|
+
}
|
|
107
117
|
}
|
|
@@ -38,8 +38,16 @@ import {
|
|
|
38
38
|
TargetCourse,
|
|
39
39
|
UserLight,
|
|
40
40
|
PaymentPlan,
|
|
41
|
+
OfferingBatchOrder,
|
|
42
|
+
BatchOrderRead,
|
|
43
|
+
BatchOrder,
|
|
44
|
+
BatchOrderState,
|
|
45
|
+
OrganizationQuote,
|
|
46
|
+
BatchOrderQuote,
|
|
47
|
+
Relation,
|
|
48
|
+
Agreement,
|
|
41
49
|
} from 'types/Joanie';
|
|
42
|
-
import { Payment, PaymentProviders } from 'components/PaymentInterfaces/types';
|
|
50
|
+
import { Payment, PaymentMethod, PaymentProviders } from 'components/PaymentInterfaces/types';
|
|
43
51
|
import { CourseStateFactory } from 'utils/test/factories/richie';
|
|
44
52
|
import { FactoryHelper } from 'utils/test/factories/helper';
|
|
45
53
|
import { JoanieUserApiAbilityActions, JoanieUserProfile } from 'types/User';
|
|
@@ -166,6 +174,51 @@ export const OrganizationFactory = factory((): Organization => {
|
|
|
166
174
|
};
|
|
167
175
|
});
|
|
168
176
|
|
|
177
|
+
export const RelationFactory = factory((): Relation => {
|
|
178
|
+
return {
|
|
179
|
+
id: faker.string.uuid(),
|
|
180
|
+
product: {
|
|
181
|
+
id: faker.string.uuid(),
|
|
182
|
+
title: faker.commerce.productName(),
|
|
183
|
+
},
|
|
184
|
+
course: {
|
|
185
|
+
id: faker.string.uuid(),
|
|
186
|
+
title: faker.lorem.words({ min: 2, max: 5 }),
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
export const BatchOrderQuoteFactory = factory((): BatchOrderQuote => {
|
|
192
|
+
return {
|
|
193
|
+
id: faker.string.uuid(),
|
|
194
|
+
owner_name: faker.person.firstName(),
|
|
195
|
+
organization_id: faker.string.uuid(),
|
|
196
|
+
state: faker.helpers.arrayElement(Object.values(BatchOrderState)),
|
|
197
|
+
company_name: faker.company.name(),
|
|
198
|
+
relation: RelationFactory().one(),
|
|
199
|
+
payment_method: faker.helpers.arrayElement(Object.values(PaymentMethod)),
|
|
200
|
+
contract_submitted: faker.datatype.boolean(),
|
|
201
|
+
nb_seats: faker.number.int({ min: 1, max: 100 }),
|
|
202
|
+
available_actions: {
|
|
203
|
+
confirm_quote: false,
|
|
204
|
+
confirm_purchase_order: false,
|
|
205
|
+
confirm_bank_transfer: false,
|
|
206
|
+
submit_for_signature: false,
|
|
207
|
+
next_action: null,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
export const OrganizationQuoteFactory = factory((): OrganizationQuote => {
|
|
213
|
+
return {
|
|
214
|
+
id: faker.string.uuid(),
|
|
215
|
+
organization_signed_on: faker.date.past().toISOString(),
|
|
216
|
+
has_purchase_order: faker.datatype.boolean(),
|
|
217
|
+
batch_order: BatchOrderQuoteFactory().one(),
|
|
218
|
+
total: faker.number.float({ min: 1, max: 100 }),
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
|
|
169
222
|
export const OrganizationLightFactory = factory((): OrganizationLight => {
|
|
170
223
|
return {
|
|
171
224
|
code: faker.string.alphanumeric(5),
|
|
@@ -173,6 +226,17 @@ export const OrganizationLightFactory = factory((): OrganizationLight => {
|
|
|
173
226
|
};
|
|
174
227
|
});
|
|
175
228
|
|
|
229
|
+
export const AgreementFactory = factory((): Agreement => {
|
|
230
|
+
return {
|
|
231
|
+
id: faker.string.uuid(),
|
|
232
|
+
batch_order: BatchOrderQuoteFactory().one(),
|
|
233
|
+
abilities: {
|
|
234
|
+
sign: faker.datatype.boolean(),
|
|
235
|
+
},
|
|
236
|
+
organization_signed_on: faker.date.past().toISOString(),
|
|
237
|
+
};
|
|
238
|
+
});
|
|
239
|
+
|
|
176
240
|
export const CertificationDefinitionFactory = factory((): CertificateDefinition => {
|
|
177
241
|
return {
|
|
178
242
|
id: faker.string.uuid(),
|
|
@@ -336,6 +400,16 @@ export const OfferingFactory = factory((): Offering => {
|
|
|
336
400
|
};
|
|
337
401
|
});
|
|
338
402
|
|
|
403
|
+
export const OfferingBatchOrderFactory = factory((): OfferingBatchOrder => {
|
|
404
|
+
return {
|
|
405
|
+
product: {
|
|
406
|
+
id: faker.string.uuid(),
|
|
407
|
+
title: faker.string.alphanumeric(5),
|
|
408
|
+
},
|
|
409
|
+
course: CourseListItemFactory().one(),
|
|
410
|
+
};
|
|
411
|
+
});
|
|
412
|
+
|
|
339
413
|
export const CourseListItemFactory = factory((): CourseListItem => {
|
|
340
414
|
return {
|
|
341
415
|
id: faker.string.uuid(),
|
|
@@ -367,6 +441,7 @@ export const PaymentPlanFactory = factory((): PaymentPlan => {
|
|
|
367
441
|
price: faker.number.int({ min: 1, max: 1000, multipleOf: 10 }),
|
|
368
442
|
discount: undefined,
|
|
369
443
|
discounted_price: undefined,
|
|
444
|
+
from_batch_order: false,
|
|
370
445
|
};
|
|
371
446
|
});
|
|
372
447
|
|
|
@@ -391,6 +466,78 @@ export const OrderLiteFactory = factory((): OrderLite => {
|
|
|
391
466
|
};
|
|
392
467
|
});
|
|
393
468
|
|
|
469
|
+
export const BatchOrderFactory = factory((): BatchOrder => {
|
|
470
|
+
return {
|
|
471
|
+
id: faker.string.uuid(),
|
|
472
|
+
offering_id: faker.string.uuid(),
|
|
473
|
+
company_name: faker.company.name(),
|
|
474
|
+
identification_number: faker.string.alphanumeric(14),
|
|
475
|
+
vat_registration: faker.string.alphanumeric(11),
|
|
476
|
+
address: faker.location.streetAddress(),
|
|
477
|
+
postcode: faker.location.zipCode(),
|
|
478
|
+
city: faker.location.city(),
|
|
479
|
+
country: faker.location.countryCode(),
|
|
480
|
+
administrative_lastname: faker.person.lastName(),
|
|
481
|
+
administrative_firstname: faker.person.firstName(),
|
|
482
|
+
administrative_profession: faker.person.jobTitle(),
|
|
483
|
+
administrative_email: faker.internet.email(),
|
|
484
|
+
administrative_telephone: faker.phone.number(),
|
|
485
|
+
signatory_lastname: faker.person.lastName(),
|
|
486
|
+
signatory_firstname: faker.person.firstName(),
|
|
487
|
+
signatory_profession: faker.person.jobTitle(),
|
|
488
|
+
signatory_email: faker.internet.email(),
|
|
489
|
+
signatory_telephone: faker.phone.number(),
|
|
490
|
+
nb_seats: faker.number.int({ min: 1, max: 200 }),
|
|
491
|
+
payment_method: faker.helpers.arrayElement([
|
|
492
|
+
PaymentMethod.CARD_PAYMENT,
|
|
493
|
+
PaymentMethod.BANK_TRANSFER,
|
|
494
|
+
PaymentMethod.PURCHASE_ORDER,
|
|
495
|
+
]),
|
|
496
|
+
funding_entity: faker.company.name(),
|
|
497
|
+
funding_amount: faker.number.int({ min: 100, max: 10000 }),
|
|
498
|
+
organization_id: faker.string.uuid(),
|
|
499
|
+
};
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
export const BatchOrderReadFactory = factory((): BatchOrderRead => {
|
|
503
|
+
return {
|
|
504
|
+
id: faker.string.uuid(),
|
|
505
|
+
owner: faker.internet.email(),
|
|
506
|
+
total: faker.number.int({ min: 100, max: 5000 }),
|
|
507
|
+
currency: faker.finance.currencyCode(),
|
|
508
|
+
organization: OrganizationFactory().one(),
|
|
509
|
+
main_invoice_reference: faker.string.alphanumeric(10),
|
|
510
|
+
contract_id: faker.string.alphanumeric(12),
|
|
511
|
+
company_name: faker.company.name(),
|
|
512
|
+
identification_number: faker.string.alphanumeric(14),
|
|
513
|
+
vat_registration: faker.string.alphanumeric(11),
|
|
514
|
+
address: faker.location.streetAddress(),
|
|
515
|
+
postcode: faker.location.zipCode(),
|
|
516
|
+
city: faker.location.city(),
|
|
517
|
+
country: faker.location.countryCode(),
|
|
518
|
+
nb_seats: faker.number.int({ min: 1, max: 200 }),
|
|
519
|
+
payment_method: faker.helpers.arrayElement([
|
|
520
|
+
PaymentMethod.CARD_PAYMENT,
|
|
521
|
+
PaymentMethod.BANK_TRANSFER,
|
|
522
|
+
PaymentMethod.PURCHASE_ORDER,
|
|
523
|
+
]),
|
|
524
|
+
state: faker.helpers.arrayElement([BatchOrderState.PENDING, BatchOrderState.COMPLETED]),
|
|
525
|
+
administrative_lastname: faker.person.lastName(),
|
|
526
|
+
administrative_firstname: faker.person.firstName(),
|
|
527
|
+
administrative_profession: faker.person.jobTitle(),
|
|
528
|
+
administrative_email: faker.internet.email(),
|
|
529
|
+
administrative_telephone: faker.phone.number(),
|
|
530
|
+
signatory_lastname: faker.person.lastName(),
|
|
531
|
+
signatory_firstname: faker.person.firstName(),
|
|
532
|
+
signatory_profession: faker.person.jobTitle(),
|
|
533
|
+
signatory_email: faker.internet.email(),
|
|
534
|
+
signatory_telephone: faker.phone.number(),
|
|
535
|
+
funding_entity: faker.company.name(),
|
|
536
|
+
funding_amount: faker.number.int({ min: 100, max: 10000 }),
|
|
537
|
+
offering: OfferingBatchOrderFactory().one(),
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
|
|
394
541
|
export const NestedCertificateOrderFactory = factory((): NestedCertificateOrder => {
|
|
395
542
|
return {
|
|
396
543
|
id: faker.string.uuid(),
|
|
@@ -494,6 +641,11 @@ export const SaleTunnelContextFactory = factory(
|
|
|
494
641
|
props: {} as SaleTunnelProps,
|
|
495
642
|
billingAddress: undefined,
|
|
496
643
|
setBillingAddress: noop,
|
|
644
|
+
batchOrder: undefined,
|
|
645
|
+
setBatchOrder: noop,
|
|
646
|
+
batchOrderFormMethods: undefined,
|
|
647
|
+
setBatchOrderFormMethods: noop,
|
|
648
|
+
validateBatchOrder: noop,
|
|
497
649
|
setCreditCard: noop,
|
|
498
650
|
setHasWaivedWithdrawalRight: noop,
|
|
499
651
|
hasWaivedWithdrawalRight: false,
|
|
@@ -503,5 +655,8 @@ export const SaleTunnelContextFactory = factory(
|
|
|
503
655
|
runSubmitCallbacks: () => new Promise((resolve) => resolve()),
|
|
504
656
|
nextStep: noop,
|
|
505
657
|
setVoucherCode: noop,
|
|
658
|
+
setSchedule: noop,
|
|
659
|
+
needsPayment: true,
|
|
660
|
+
setNeedsPayment: noop,
|
|
506
661
|
}),
|
|
507
662
|
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useParams } from 'react-router';
|
|
2
|
+
import { defineMessages, FormattedMessage } from 'react-intl';
|
|
3
|
+
import { Spinner } from 'components/Spinner';
|
|
4
|
+
import { useBatchOrder } from 'hooks/useBatchOrder';
|
|
5
|
+
import { DashboardItemBatchOrder } from '../DashboardItem/BatchOrder';
|
|
6
|
+
|
|
7
|
+
const messages = defineMessages({
|
|
8
|
+
loading: {
|
|
9
|
+
defaultMessage: 'Loading order ...',
|
|
10
|
+
description: 'Message displayed while loading an order',
|
|
11
|
+
id: 'components.DashboardOrderLoader.loading',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const DashboardBatchOrderLoader = () => {
|
|
16
|
+
const params = useParams<{ batchOrderId: string }>();
|
|
17
|
+
const { item: batchOrder, states } = useBatchOrder(params.batchOrderId);
|
|
18
|
+
const fetching = states.isPending;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
{fetching && !batchOrder && (
|
|
23
|
+
<Spinner aria-labelledby="loading-courses-data">
|
|
24
|
+
<span id="loading-courses-data">
|
|
25
|
+
<FormattedMessage {...messages.loading} />
|
|
26
|
+
</span>
|
|
27
|
+
</Spinner>
|
|
28
|
+
)}
|
|
29
|
+
{batchOrder && <DashboardItemBatchOrder batchOrder={batchOrder} showDetails />}
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -17,4 +17,22 @@ describe('<DashboardCard/>', () => {
|
|
|
17
17
|
fireEvent.click(screen.getByTestId('dashboard-card__header__toggle'));
|
|
18
18
|
expect(screen.getByTestId('dashboard-card__wrapper').style.height).toBe('0px');
|
|
19
19
|
});
|
|
20
|
+
|
|
21
|
+
it('is not expanded', async () => {
|
|
22
|
+
render(
|
|
23
|
+
<DashboardCard
|
|
24
|
+
defaultExpanded={false}
|
|
25
|
+
header="My header"
|
|
26
|
+
footer={<Button color="primary">Update</Button>}
|
|
27
|
+
>
|
|
28
|
+
Content here
|
|
29
|
+
</DashboardCard>,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(screen.getByTestId('dashboard-card__wrapper').style.height).toBe('0px');
|
|
33
|
+
const card = screen.getByText('My header').closest('.dashboard-card');
|
|
34
|
+
expect(card).toHaveClass('dashboard-card--closed');
|
|
35
|
+
fireEvent.click(screen.getByTestId('dashboard-card__header__toggle'));
|
|
36
|
+
expect(card).toHaveClass('dashboard-card--opened');
|
|
37
|
+
});
|
|
20
38
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { Button } from '@openfun/cunningham-react';
|
|
3
|
-
import Input from 'components/Form/Input';
|
|
4
3
|
import { DashboardBox } from '../DashboardBox';
|
|
5
4
|
import { DashboardCard } from './index';
|
|
6
5
|
|
|
@@ -18,7 +17,15 @@ type Story = StoryObj<typeof DashboardCard>;
|
|
|
18
17
|
export const Default: Story = {
|
|
19
18
|
args: {
|
|
20
19
|
header: 'Billing Addresses',
|
|
21
|
-
children:
|
|
20
|
+
children: (
|
|
21
|
+
<div>
|
|
22
|
+
<div>
|
|
23
|
+
<div>Home</div>
|
|
24
|
+
<strong>Pierre Léger</strong>
|
|
25
|
+
<p>21 rue du pavillon - 78130 Chapter ( France )</p>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
),
|
|
22
29
|
footer: <Button color="primary">Update</Button>,
|
|
23
30
|
},
|
|
24
31
|
};
|
|
@@ -47,3 +54,19 @@ export const WithBoxes: Story = {
|
|
|
47
54
|
),
|
|
48
55
|
},
|
|
49
56
|
};
|
|
57
|
+
|
|
58
|
+
export const NotExpanded: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
header: 'Not expanded',
|
|
61
|
+
defaultExpanded: false,
|
|
62
|
+
children: (
|
|
63
|
+
<div>
|
|
64
|
+
<div>
|
|
65
|
+
<div>Home</div>
|
|
66
|
+
<strong>Pierre Léger</strong>
|
|
67
|
+
<p>21 rue du pavillon - 78130 Chapter ( France )</p>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
@@ -9,6 +9,7 @@ interface Props {
|
|
|
9
9
|
expandable?: boolean;
|
|
10
10
|
fullWidth?: boolean;
|
|
11
11
|
className?: string;
|
|
12
|
+
defaultExpanded?: boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const DashboardCard = ({
|
|
@@ -18,10 +19,11 @@ export const DashboardCard = ({
|
|
|
18
19
|
className,
|
|
19
20
|
expandable = true,
|
|
20
21
|
fullWidth = false,
|
|
22
|
+
defaultExpanded = true,
|
|
21
23
|
}: PropsWithChildren<Props>) => {
|
|
22
|
-
const [opened, setOpened] = useState(
|
|
24
|
+
const [opened, setOpened] = useState(defaultExpanded);
|
|
23
25
|
const expandableRef = useRef<HTMLDivElement>(null);
|
|
24
|
-
const [wrapperHeight, setWrapperHeight] = useState('auto');
|
|
26
|
+
const [wrapperHeight, setWrapperHeight] = useState(defaultExpanded ? 'auto' : '0');
|
|
25
27
|
|
|
26
28
|
const toggle = () => {
|
|
27
29
|
if (opened) {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Button, useModal } from '@openfun/cunningham-react';
|
|
3
|
+
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
|
4
|
+
import { useBatchOrder } from 'hooks/useBatchOrder';
|
|
5
|
+
import { BatchOrderRead, BatchOrderState } from 'types/Joanie';
|
|
6
|
+
import { DashboardSubItem } from 'widgets/Dashboard/components/DashboardItem/DashboardSubItem';
|
|
7
|
+
import { DashboardSubItemsList } from '../../DashboardSubItemsList';
|
|
8
|
+
import { BatchOrderPaymentModal } from '.';
|
|
9
|
+
|
|
10
|
+
const messages = defineMessages({
|
|
11
|
+
batchOrderPayment: {
|
|
12
|
+
id: 'components.ProductCertificateFooter.batchOrderPayment',
|
|
13
|
+
description: 'Label before the payment button',
|
|
14
|
+
defaultMessage: 'We are waiting for your payment to finalize the batch order.',
|
|
15
|
+
},
|
|
16
|
+
paymentProcessing: {
|
|
17
|
+
id: 'components.ProductCertificateFooter.paymentProcessing',
|
|
18
|
+
description: 'Button label for the payment is processing message',
|
|
19
|
+
defaultMessage: 'Payment processing...',
|
|
20
|
+
},
|
|
21
|
+
paymentNeededButton: {
|
|
22
|
+
id: 'components.ProductCertificateFooter.paymentNeededButton',
|
|
23
|
+
description: 'Button label for the payment needed message',
|
|
24
|
+
defaultMessage: 'Pay {amount}',
|
|
25
|
+
},
|
|
26
|
+
paymentSectionTitle: {
|
|
27
|
+
id: 'batchOrder.payment.sectionTitle',
|
|
28
|
+
description: 'Title for the payment section in dashboard',
|
|
29
|
+
defaultMessage: 'Payment required',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
interface BatchPaymentManagerProps {
|
|
34
|
+
batchOrder: BatchOrderRead;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const BatchOrderPaymentManager = ({ batchOrder }: BatchPaymentManagerProps) => {
|
|
38
|
+
const intl = useIntl();
|
|
39
|
+
const retryModal = useModal();
|
|
40
|
+
const batchOrderQuery = useBatchOrder(batchOrder.id);
|
|
41
|
+
const processingPayment = batchOrder.state === BatchOrderState.PROCESS_PAYMENT;
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (batchOrderQuery.item) {
|
|
45
|
+
batchOrderQuery.methods.invalidate();
|
|
46
|
+
}
|
|
47
|
+
}, [batchOrderQuery.item]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<DashboardSubItemsList
|
|
51
|
+
subItems={[
|
|
52
|
+
<DashboardSubItem
|
|
53
|
+
title={intl.formatMessage(messages.paymentSectionTitle)}
|
|
54
|
+
footer={
|
|
55
|
+
<div className="content">
|
|
56
|
+
<FormattedMessage {...messages.batchOrderPayment} />
|
|
57
|
+
<Button
|
|
58
|
+
size="small"
|
|
59
|
+
color="primary"
|
|
60
|
+
onClick={retryModal.open}
|
|
61
|
+
disabled={processingPayment}
|
|
62
|
+
>
|
|
63
|
+
{processingPayment ? (
|
|
64
|
+
<FormattedMessage {...messages.paymentProcessing} />
|
|
65
|
+
) : (
|
|
66
|
+
<FormattedMessage
|
|
67
|
+
{...messages.paymentNeededButton}
|
|
68
|
+
values={{
|
|
69
|
+
amount: intl.formatNumber(batchOrder.total ?? 0, {
|
|
70
|
+
style: 'currency',
|
|
71
|
+
currency: batchOrder.currency ?? 'EUR',
|
|
72
|
+
}),
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
</Button>
|
|
77
|
+
<BatchOrderPaymentModal
|
|
78
|
+
{...retryModal}
|
|
79
|
+
batchOrder={batchOrder}
|
|
80
|
+
onClose={batchOrderQuery.methods.invalidate}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
}
|
|
84
|
+
/>,
|
|
85
|
+
]}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
};
|