richie-education 3.3.2-dev6 → 3.4.1-dev13
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/main.js +11 -12
- 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/api/joanie.ts +20 -0
- package/js/api/utils.ts +4 -3
- 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/DownloadAgreementButton/index.tsx +51 -0
- package/js/components/DownloadBatchOrderSeatsButton/index.spec.tsx +46 -0
- package/js/components/DownloadBatchOrderSeatsButton/index.tsx +80 -0
- 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 +5 -4
- package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationSingular.tsx +27 -5
- package/js/components/SaleTunnel/SaleTunnelSuccess/index.tsx +1 -1
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +9 -6
- package/js/components/SaleTunnel/_styles.scss +9 -8
- package/js/components/SaleTunnel/index.credential.spec.tsx +50 -1
- package/js/components/SaleTunnel/index.full-process-b2b.spec.tsx +5 -5
- package/js/components/SaleTunnel/index.full-process-b2c.spec.tsx +1 -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/useBatchOrder/index.tsx +21 -1
- package/js/hooks/useCreditCards/index.ts +6 -4
- package/js/hooks/useDashboardAddressForm.tsx +3 -3
- package/js/hooks/useDownloadAgreement/index.spec.tsx +136 -0
- package/js/hooks/useDownloadAgreement/index.tsx +25 -0
- package/js/hooks/useDownloadBatchOrderSeats/index.spec.tsx +132 -0
- package/js/hooks/useDownloadBatchOrderSeats/index.tsx +24 -0
- 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/DashboardBatchOrderLayout/index.spec.tsx +19 -2
- 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/BatchOrderSeatInfoQuote.tsx +112 -0
- package/js/pages/TeacherDashboardOrganizationQuotes/_styles.scss +17 -0
- package/js/pages/TeacherDashboardOrganizationQuotes/index.full-process.spec.tsx +7 -4
- package/js/pages/TeacherDashboardOrganizationQuotes/index.spec.tsx +8 -4
- package/js/pages/TeacherDashboardOrganizationQuotes/index.tsx +39 -26
- 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 +22 -2
- 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/download.ts +3 -1
- 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 +16 -2
- 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/BatchOrderAgreementInfo.tsx +72 -0
- 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/BatchOrder/BatchOrderSeatInfo.spec.tsx +114 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderSeatInfo.tsx +133 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/DashboardBatchOrderSubItems.tsx +17 -1
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/batchOrderSeatInfoMessages.ts +24 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/index.tsx +16 -3
- 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/_styles.scss +6 -2
- 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/Dashboard/utils/teacherDashboardPaths.tsx +2 -2
- 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/Slider/index.tsx +7 -6
- 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 +76 -81
- package/scss/components/_subheader.scss +1 -1
- package/scss/components/templates/richie/slider/_slider.scss +1 -1
- package/scss/objects/_course_glimpses.scss +1 -0
- package/scss/objects/_dashboard.scss +77 -0
- 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/js/utils/download.ts
CHANGED
|
@@ -5,11 +5,13 @@ import { handle } from './errors/handle';
|
|
|
5
5
|
*
|
|
6
6
|
* @param downloadFunction, an api promise that return a File
|
|
7
7
|
* @param newWindow, does it open in a new window or not
|
|
8
|
+
* @param filename, optional filename override; if provided, takes precedence over file.name
|
|
8
9
|
* @returns boolean, true for success
|
|
9
10
|
*/
|
|
10
11
|
export const browserDownloadFromBlob = async (
|
|
11
12
|
downloadFunction: () => Promise<File>,
|
|
12
13
|
newWindow: boolean = false,
|
|
14
|
+
filename?: string,
|
|
13
15
|
) => {
|
|
14
16
|
try {
|
|
15
17
|
const file = await downloadFunction();
|
|
@@ -24,7 +26,7 @@ export const browserDownloadFromBlob = async (
|
|
|
24
26
|
|
|
25
27
|
const $link = document.createElement('a');
|
|
26
28
|
$link.href = url;
|
|
27
|
-
$link.download = file.name;
|
|
29
|
+
$link.download = filename || file.name;
|
|
28
30
|
|
|
29
31
|
const revokeObject = () => {
|
|
30
32
|
// eslint-disable-next-line compat/compat
|
|
@@ -19,12 +19,12 @@ jest.mock('@sentry/browser', () => ({
|
|
|
19
19
|
|
|
20
20
|
describe('handle', () => {
|
|
21
21
|
it('should initialize sentry', () => {
|
|
22
|
-
expect(Sentry.init).
|
|
23
|
-
expect(Sentry.setTag).
|
|
22
|
+
expect(Sentry.init).toHaveBeenCalledTimes(1);
|
|
23
|
+
expect(Sentry.setTag).toHaveBeenCalledTimes(1);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('should report error to sentry', () => {
|
|
27
27
|
handle(new Error('An error for test'));
|
|
28
|
-
expect(Sentry.captureException).
|
|
28
|
+
expect(Sentry.captureException).toHaveBeenCalledTimes(1);
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
MutationFunctionContext,
|
|
3
|
+
UseMutationOptions,
|
|
4
|
+
UseMutationResult,
|
|
5
|
+
} from '@tanstack/react-query';
|
|
2
6
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
7
|
import { HttpError, HttpStatusCode } from 'utils/errors/HttpError';
|
|
4
8
|
|
|
@@ -18,13 +22,14 @@ export function useSessionMutation<TData = unknown, TVariables = void, TContext
|
|
|
18
22
|
error: HttpError,
|
|
19
23
|
variables: TVariables,
|
|
20
24
|
context: TContext | undefined,
|
|
25
|
+
mutationContext: MutationFunctionContext,
|
|
21
26
|
) => {
|
|
22
|
-
if (error.code === HttpStatusCode.UNAUTHORIZED) {
|
|
27
|
+
if (error && error.code === HttpStatusCode.UNAUTHORIZED) {
|
|
23
28
|
await queryClient.invalidateQueries({ queryKey: ['user'], exact: true });
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
if (options?.onError) {
|
|
27
|
-
return options.onError(error, variables, context);
|
|
32
|
+
return options.onError(error, variables, context, mutationContext);
|
|
28
33
|
}
|
|
29
34
|
};
|
|
30
35
|
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
BatchOrderQuote,
|
|
47
47
|
Relation,
|
|
48
48
|
Agreement,
|
|
49
|
+
BatchOrderSeat,
|
|
49
50
|
} from 'types/Joanie';
|
|
50
51
|
import { Payment, PaymentMethod, PaymentProviders } from 'components/PaymentInterfaces/types';
|
|
51
52
|
import { CourseStateFactory } from 'utils/test/factories/richie';
|
|
@@ -176,7 +177,7 @@ export const OrganizationFactory = factory((): Organization => {
|
|
|
176
177
|
contact_phone: faker.phone.number(),
|
|
177
178
|
address: AddressFactory().one(),
|
|
178
179
|
abilities: {
|
|
179
|
-
|
|
180
|
+
can_manage_batch_order_agreement: faker.datatype.boolean(),
|
|
180
181
|
confirm_bank_transfer: faker.datatype.boolean(),
|
|
181
182
|
confirm_quote: faker.datatype.boolean(),
|
|
182
183
|
delete: faker.datatype.boolean(),
|
|
@@ -214,12 +215,15 @@ export const BatchOrderQuoteFactory = factory((): BatchOrderQuote => {
|
|
|
214
215
|
relation: RelationFactory().one(),
|
|
215
216
|
payment_method: faker.helpers.arrayElement(Object.values(PaymentMethod)),
|
|
216
217
|
contract_submitted: faker.datatype.boolean(),
|
|
217
|
-
nb_seats: faker.number.int({ min:
|
|
218
|
+
nb_seats: faker.number.int({ min: 10, max: 100 }),
|
|
219
|
+
seats_owned: faker.number.int({ min: 0, max: 10 }),
|
|
220
|
+
seats_to_own: faker.number.int({ min: 90, max: 100 }),
|
|
218
221
|
available_actions: {
|
|
219
222
|
confirm_quote: false,
|
|
220
223
|
confirm_purchase_order: false,
|
|
221
224
|
confirm_bank_transfer: false,
|
|
222
225
|
submit_for_signature: false,
|
|
226
|
+
download_quote: false,
|
|
223
227
|
next_action: null,
|
|
224
228
|
},
|
|
225
229
|
};
|
|
@@ -551,6 +555,8 @@ export const BatchOrderReadFactory = factory((): BatchOrderRead => {
|
|
|
551
555
|
funding_entity: faker.company.name(),
|
|
552
556
|
funding_amount: faker.number.int({ min: 100, max: 10000 }),
|
|
553
557
|
offering: OfferingBatchOrderFactory().one(),
|
|
558
|
+
seats_owned: faker.number.int({ min: 1, max: 200 }),
|
|
559
|
+
seats_to_own: faker.number.int({ min: 1, max: 200 }),
|
|
554
560
|
};
|
|
555
561
|
});
|
|
556
562
|
|
|
@@ -678,3 +684,11 @@ export const SaleTunnelContextFactory = factory(
|
|
|
678
684
|
setPaymentMode: noop,
|
|
679
685
|
}),
|
|
680
686
|
);
|
|
687
|
+
|
|
688
|
+
export const BatchOrderSeatFactory = factory((): BatchOrderSeat => {
|
|
689
|
+
return {
|
|
690
|
+
id: faker.string.uuid(),
|
|
691
|
+
owner_name: null,
|
|
692
|
+
voucher: faker.string.alphanumeric(10),
|
|
693
|
+
};
|
|
694
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { UserFactory } from 'utils/test/factories/richie';
|
|
3
3
|
import { OrganizationFactory } from 'utils/test/factories/joanie';
|
|
4
4
|
import { DashboardAvatar, DashboardAvatarVariantEnum } from '.';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { Button } from '@openfun/cunningham-react';
|
|
3
3
|
import { DashboardBox } from './index';
|
|
4
4
|
|
|
@@ -25,8 +25,12 @@ export const Default: Story = {
|
|
|
25
25
|
),
|
|
26
26
|
footer: (
|
|
27
27
|
<>
|
|
28
|
-
<Button color="primary">
|
|
29
|
-
|
|
28
|
+
<Button color="brand" variant="primary">
|
|
29
|
+
Delete
|
|
30
|
+
</Button>
|
|
31
|
+
<Button color="brand" variant="primary">
|
|
32
|
+
Update
|
|
33
|
+
</Button>
|
|
30
34
|
</>
|
|
31
35
|
),
|
|
32
36
|
},
|
|
@@ -43,8 +47,12 @@ export const NoHeader: Story = {
|
|
|
43
47
|
),
|
|
44
48
|
footer: (
|
|
45
49
|
<>
|
|
46
|
-
<Button color="primary">
|
|
47
|
-
|
|
50
|
+
<Button color="brand" variant="primary">
|
|
51
|
+
Delete
|
|
52
|
+
</Button>
|
|
53
|
+
<Button color="brand" variant="primary">
|
|
54
|
+
Update
|
|
55
|
+
</Button>
|
|
48
56
|
</>
|
|
49
57
|
),
|
|
50
58
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { createMemoryRouter, Outlet, RouteObject, RouterProvider } from 'react-router';
|
|
3
3
|
import { defineMessages } from 'react-intl';
|
|
4
4
|
import { DashboardBreadcrumbsProvider } from 'widgets/Dashboard/contexts/DashboardBreadcrumbsContext';
|
|
@@ -60,7 +60,8 @@ export const DashboardBreadcrumbs = () => {
|
|
|
60
60
|
<RouterButton
|
|
61
61
|
href={backPath}
|
|
62
62
|
size="nano"
|
|
63
|
-
color="
|
|
63
|
+
color="brand"
|
|
64
|
+
variant="tertiary"
|
|
64
65
|
icon={<span className="material-icons">chevron_left</span>}
|
|
65
66
|
>
|
|
66
67
|
<FormattedMessage {...messages.back} />
|
|
@@ -69,7 +70,7 @@ export const DashboardBreadcrumbs = () => {
|
|
|
69
70
|
|
|
70
71
|
{breadcrumbs.map((breadcrumb) => (
|
|
71
72
|
<li key={breadcrumb.pathname}>
|
|
72
|
-
<RouterButton href={breadcrumb.pathname} size="nano" color="tertiary
|
|
73
|
+
<RouterButton href={breadcrumb.pathname} size="nano" color="brand" variant="tertiary">
|
|
73
74
|
{breadcrumb.name}
|
|
74
75
|
</RouterButton>
|
|
75
76
|
</li>
|
|
@@ -5,7 +5,14 @@ import { DashboardCard } from '.';
|
|
|
5
5
|
describe('<DashboardCard/>', () => {
|
|
6
6
|
it('opens and closes', async () => {
|
|
7
7
|
render(
|
|
8
|
-
<DashboardCard
|
|
8
|
+
<DashboardCard
|
|
9
|
+
header="My header"
|
|
10
|
+
footer={
|
|
11
|
+
<Button color="brand" variant="primary">
|
|
12
|
+
Update
|
|
13
|
+
</Button>
|
|
14
|
+
}
|
|
15
|
+
>
|
|
9
16
|
Content here
|
|
10
17
|
</DashboardCard>,
|
|
11
18
|
);
|
|
@@ -23,7 +30,11 @@ describe('<DashboardCard/>', () => {
|
|
|
23
30
|
<DashboardCard
|
|
24
31
|
defaultExpanded={false}
|
|
25
32
|
header="My header"
|
|
26
|
-
footer={
|
|
33
|
+
footer={
|
|
34
|
+
<Button color="brand" variant="primary">
|
|
35
|
+
Update
|
|
36
|
+
</Button>
|
|
37
|
+
}
|
|
27
38
|
>
|
|
28
39
|
Content here
|
|
29
40
|
</DashboardCard>,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { Button } from '@openfun/cunningham-react';
|
|
3
3
|
import { DashboardBox } from '../DashboardBox';
|
|
4
4
|
import { DashboardCard } from './index';
|
|
@@ -26,7 +26,11 @@ export const Default: Story = {
|
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
28
|
),
|
|
29
|
-
footer:
|
|
29
|
+
footer: (
|
|
30
|
+
<Button color="brand" variant="primary">
|
|
31
|
+
Update
|
|
32
|
+
</Button>
|
|
33
|
+
),
|
|
30
34
|
},
|
|
31
35
|
};
|
|
32
36
|
|
|
@@ -39,8 +43,12 @@ export const WithBoxes: Story = {
|
|
|
39
43
|
header={<>Address used by default</>}
|
|
40
44
|
footer={
|
|
41
45
|
<>
|
|
42
|
-
<Button color="primary">
|
|
43
|
-
|
|
46
|
+
<Button color="brand" variant="primary">
|
|
47
|
+
Remove
|
|
48
|
+
</Button>
|
|
49
|
+
<Button color="brand" variant="primary">
|
|
50
|
+
Edit
|
|
51
|
+
</Button>
|
|
44
52
|
</>
|
|
45
53
|
}
|
|
46
54
|
>
|
|
@@ -64,7 +64,7 @@ export const DashboardCard = ({
|
|
|
64
64
|
<header className="dashboard-card__header">
|
|
65
65
|
<div>{header}</div>
|
|
66
66
|
{expandable && (
|
|
67
|
-
<Button onClick={toggle} color="tertiary" size="small">
|
|
67
|
+
<Button onClick={toggle} color="brand" variant="tertiary" size="small">
|
|
68
68
|
<Icon
|
|
69
69
|
name={IconTypeEnum.CHEVRON_DOWN_OUTLINE}
|
|
70
70
|
data-testid="dashboard-card__header__toggle"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
2
|
+
import { BatchOrderRead } from 'types/Joanie';
|
|
3
|
+
import DownloadAgreementButton from 'components/DownloadAgreementButton';
|
|
4
|
+
import { DashboardSubItem } from 'widgets/Dashboard/components/DashboardItem/DashboardSubItem';
|
|
5
|
+
import { useOrganizationAgreement } from 'hooks/useOrganizationAgreements.tsx';
|
|
6
|
+
import useDateFormat from 'hooks/useDateFormat';
|
|
7
|
+
|
|
8
|
+
const messages = defineMessages({
|
|
9
|
+
title: {
|
|
10
|
+
id: 'batchOrder.agreement.title',
|
|
11
|
+
description: 'Step label for the agreement document in the batch order detail',
|
|
12
|
+
defaultMessage: 'Agreement',
|
|
13
|
+
},
|
|
14
|
+
organizationSignedOn: {
|
|
15
|
+
id: 'batchOrder.agreement.organizationSignedOn',
|
|
16
|
+
description: 'Label displayed once the organization has counter-signed the agreement',
|
|
17
|
+
defaultMessage: 'Signed by the organization on {date}.',
|
|
18
|
+
},
|
|
19
|
+
waitingOrganization: {
|
|
20
|
+
id: 'batchOrder.agreement.waitingOrganization',
|
|
21
|
+
description:
|
|
22
|
+
'Label displayed when the agreement is waiting for the organization counter-signature',
|
|
23
|
+
defaultMessage: 'Waiting for the organization to counter-sign the agreement.',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
interface BatchOrderAgreementInfoProps {
|
|
28
|
+
batchOrder: BatchOrderRead;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const BatchOrderAgreementInfo = ({ batchOrder }: BatchOrderAgreementInfoProps) => {
|
|
32
|
+
const intl = useIntl();
|
|
33
|
+
const formatDate = useDateFormat();
|
|
34
|
+
const {
|
|
35
|
+
item: agreement,
|
|
36
|
+
states: { isFetched, error },
|
|
37
|
+
} = useOrganizationAgreement(batchOrder.contract_id!, {
|
|
38
|
+
organization_id: batchOrder.organization.id,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!isFetched || error || !agreement) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const signedOn = agreement.organization_signed_on;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<DashboardSubItem
|
|
49
|
+
title={intl.formatMessage(messages.title)}
|
|
50
|
+
footer={
|
|
51
|
+
<div className="content">
|
|
52
|
+
{signedOn ? (
|
|
53
|
+
<>
|
|
54
|
+
<p>
|
|
55
|
+
<FormattedMessage
|
|
56
|
+
{...messages.organizationSignedOn}
|
|
57
|
+
values={{ date: formatDate(signedOn) }}
|
|
58
|
+
/>
|
|
59
|
+
</p>
|
|
60
|
+
<DownloadAgreementButton
|
|
61
|
+
organizationId={batchOrder.organization.id}
|
|
62
|
+
agreementId={batchOrder.contract_id!}
|
|
63
|
+
/>
|
|
64
|
+
</>
|
|
65
|
+
) : (
|
|
66
|
+
<FormattedMessage {...messages.waitingOrganization} />
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -48,7 +48,7 @@ export const BatchOrderPaymentManager = ({ batchOrder }: BatchPaymentManagerProp
|
|
|
48
48
|
footer={
|
|
49
49
|
<div className="content">
|
|
50
50
|
<FormattedMessage {...messages.batchOrderPayment} />
|
|
51
|
-
<Button size="small" color="primary" onClick={retryModal.open}>
|
|
51
|
+
<Button size="small" color="brand" variant="primary" onClick={retryModal.open}>
|
|
52
52
|
<FormattedMessage
|
|
53
53
|
{...messages.paymentNeededButton}
|
|
54
54
|
values={{
|
package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/index.tsx
CHANGED
|
@@ -172,7 +172,8 @@ export const BatchOrderPaymentModal = ({ batchOrder, ...props }: Props) => {
|
|
|
172
172
|
hideCloseButton={state === ComponentStates.LOADING}
|
|
173
173
|
actions={
|
|
174
174
|
<Button
|
|
175
|
-
color="
|
|
175
|
+
color="brand"
|
|
176
|
+
variant="primary"
|
|
176
177
|
size="small"
|
|
177
178
|
fullWidth={true}
|
|
178
179
|
onClick={pay}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import fetchMock from 'fetch-mock';
|
|
4
|
+
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
5
|
+
import { BatchOrderReadFactory, BatchOrderSeatFactory } from 'utils/test/factories/joanie';
|
|
6
|
+
import { BatchOrderState } from 'types/Joanie';
|
|
7
|
+
import { HttpStatusCode } from 'utils/errors/HttpError';
|
|
8
|
+
import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
9
|
+
import { render } from 'utils/test/render';
|
|
10
|
+
import { expectBannerError } from 'utils/test/expectBanner';
|
|
11
|
+
import { BatchOrderSeatInfo } from './BatchOrderSeatInfo';
|
|
12
|
+
|
|
13
|
+
jest.mock('utils/context', () => ({
|
|
14
|
+
__esModule: true,
|
|
15
|
+
default: mockRichieContextFactory({
|
|
16
|
+
authentication: { backend: 'fonzie', endpoint: 'https://auth.test' },
|
|
17
|
+
joanie_backend: { endpoint: 'https://joanie.endpoint' },
|
|
18
|
+
}).one(),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
describe('<BatchOrderSeatInfo />', () => {
|
|
22
|
+
setupJoanieSession();
|
|
23
|
+
|
|
24
|
+
const paginatedResponse = (results: object[], count?: number) => ({
|
|
25
|
+
results,
|
|
26
|
+
count: count ?? results.length,
|
|
27
|
+
next: null,
|
|
28
|
+
previous: null,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('renders enrollment progress and seat list, and searches by query param', async () => {
|
|
32
|
+
const ownedSeat = BatchOrderSeatFactory({ owner_name: 'Alice Martin' }).one();
|
|
33
|
+
const voucherSeat = BatchOrderSeatFactory().one();
|
|
34
|
+
const batchOrder = BatchOrderReadFactory({
|
|
35
|
+
state: BatchOrderState.COMPLETED,
|
|
36
|
+
nb_seats: 10,
|
|
37
|
+
seats_owned: 1,
|
|
38
|
+
seats_to_own: 9,
|
|
39
|
+
}).one();
|
|
40
|
+
|
|
41
|
+
fetchMock.get(
|
|
42
|
+
`begin:https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/seats/`,
|
|
43
|
+
paginatedResponse([ownedSeat, voucherSeat], 10),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
render(<BatchOrderSeatInfo batchOrder={batchOrder} />);
|
|
47
|
+
|
|
48
|
+
expect(await screen.findByText('1/10 enrolled participants')).toBeVisible();
|
|
49
|
+
expect(await screen.findByText('Alice Martin')).toBeVisible();
|
|
50
|
+
expect(await screen.findByText(voucherSeat.voucher!)).toBeVisible();
|
|
51
|
+
|
|
52
|
+
const user = userEvent.setup();
|
|
53
|
+
await user.type(screen.getByRole('textbox'), 'Alice');
|
|
54
|
+
|
|
55
|
+
await waitFor(() => {
|
|
56
|
+
const urls = fetchMock
|
|
57
|
+
.calls(`begin:https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/seats/`)
|
|
58
|
+
.map(([url]) => url);
|
|
59
|
+
expect(urls.some((url) => url.includes('query=Alice'))).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('loads more seats when clicking the load more button', async () => {
|
|
64
|
+
const firstPage = BatchOrderSeatFactory().many(10);
|
|
65
|
+
const secondPage = BatchOrderSeatFactory().many(5);
|
|
66
|
+
const batchOrder = BatchOrderReadFactory({
|
|
67
|
+
state: BatchOrderState.COMPLETED,
|
|
68
|
+
nb_seats: 15,
|
|
69
|
+
seats_owned: 15,
|
|
70
|
+
seats_to_own: 0,
|
|
71
|
+
}).one();
|
|
72
|
+
|
|
73
|
+
fetchMock.get(
|
|
74
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/seats/?page=1&page_size=10`,
|
|
75
|
+
{ results: firstPage, count: 15, next: 'next-url', previous: null },
|
|
76
|
+
);
|
|
77
|
+
fetchMock.get(
|
|
78
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/seats/?page=2&page_size=10`,
|
|
79
|
+
{ results: secondPage, count: 15, next: null, previous: 'prev-url' },
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
render(<BatchOrderSeatInfo batchOrder={batchOrder} />);
|
|
83
|
+
|
|
84
|
+
expect(await screen.findByText(firstPage[0].owner_name ?? firstPage[0].voucher!)).toBeVisible();
|
|
85
|
+
expect(screen.queryByText(secondPage[0].owner_name ?? secondPage[0].voucher!)).toBeNull();
|
|
86
|
+
expect(screen.getByRole('button', { name: 'Load 5 more' })).toBeVisible();
|
|
87
|
+
|
|
88
|
+
const user = userEvent.setup();
|
|
89
|
+
await user.click(screen.getByRole('button', { name: 'Load 5 more' }));
|
|
90
|
+
|
|
91
|
+
expect(
|
|
92
|
+
await screen.findByText(secondPage[0].owner_name ?? secondPage[0].voucher!),
|
|
93
|
+
).toBeVisible();
|
|
94
|
+
expect(screen.queryByText('Load 5 more')).toBeNull();
|
|
95
|
+
expect(screen.getByText(firstPage[0].owner_name ?? firstPage[0].voucher!)).toBeVisible();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('shows an error banner when the seats API fails', async () => {
|
|
99
|
+
const batchOrder = BatchOrderReadFactory({
|
|
100
|
+
state: BatchOrderState.COMPLETED,
|
|
101
|
+
nb_seats: 10,
|
|
102
|
+
seats_owned: 1,
|
|
103
|
+
seats_to_own: 9,
|
|
104
|
+
}).one();
|
|
105
|
+
|
|
106
|
+
fetchMock.get(`begin:https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/seats/`, {
|
|
107
|
+
status: HttpStatusCode.INTERNAL_SERVER_ERROR,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
render(<BatchOrderSeatInfo batchOrder={batchOrder} />);
|
|
111
|
+
|
|
112
|
+
await expectBannerError('An error occurred while fetching resources. Please retry later.');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
3
|
+
import { Button, Input } from '@openfun/cunningham-react';
|
|
4
|
+
import { Icon, IconTypeEnum } from 'components/Icon';
|
|
5
|
+
import Banner, { BannerType } from 'components/Banner';
|
|
6
|
+
import { DashboardSubItem } from 'widgets/Dashboard/components/DashboardItem/DashboardSubItem';
|
|
7
|
+
import { useBatchOrderSeats } from 'hooks/useBatchOrder';
|
|
8
|
+
import DownloadBatchOrderSeatsButton from 'components/DownloadBatchOrderSeatsButton';
|
|
9
|
+
import { BatchOrderRead, BatchOrderSeat } from 'types/Joanie';
|
|
10
|
+
import { batchOrderSeatInfoMessages } from './batchOrderSeatInfoMessages';
|
|
11
|
+
|
|
12
|
+
const messages = defineMessages({
|
|
13
|
+
enrollmentManagement: {
|
|
14
|
+
id: 'batchOrder.enrollmentManagement.title',
|
|
15
|
+
description: 'Title for enrollment management section',
|
|
16
|
+
defaultMessage: 'Enrollment',
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const ITEMS_PER_PAGE = 10;
|
|
21
|
+
|
|
22
|
+
interface BatchOrderSeatInfoProps {
|
|
23
|
+
batchOrder: BatchOrderRead;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const BatchOrderSeatInfo = ({ batchOrder }: BatchOrderSeatInfoProps) => {
|
|
27
|
+
const intl = useIntl();
|
|
28
|
+
const [query, setQuery] = useState('');
|
|
29
|
+
const [page, setPage] = useState(1);
|
|
30
|
+
const [allSeats, setAllSeats] = useState<BatchOrderSeat[]>([]);
|
|
31
|
+
|
|
32
|
+
const seatsOwnedCount = batchOrder.seats_owned ?? 0;
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
items: seats,
|
|
36
|
+
meta,
|
|
37
|
+
states,
|
|
38
|
+
} = useBatchOrderSeats(
|
|
39
|
+
{
|
|
40
|
+
batch_order_id: batchOrder.id,
|
|
41
|
+
query: query || undefined,
|
|
42
|
+
page,
|
|
43
|
+
page_size: ITEMS_PER_PAGE,
|
|
44
|
+
},
|
|
45
|
+
{ enabled: !!batchOrder.id },
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (page === 1) {
|
|
50
|
+
setAllSeats(seats);
|
|
51
|
+
} else if (seats.length > 0) {
|
|
52
|
+
setAllSeats((prev) => [...prev, ...seats]);
|
|
53
|
+
}
|
|
54
|
+
}, [seats]);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
setPage(1);
|
|
58
|
+
}, [query]);
|
|
59
|
+
|
|
60
|
+
const totalCount = meta?.pagination?.count ?? 0;
|
|
61
|
+
const remainingCount = Math.min(ITEMS_PER_PAGE, totalCount - allSeats.length);
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
!batchOrder.nb_seats ||
|
|
65
|
+
batchOrder.seats_owned === undefined ||
|
|
66
|
+
batchOrder.seats_to_own === undefined
|
|
67
|
+
) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<DashboardSubItem
|
|
73
|
+
title={intl.formatMessage(messages.enrollmentManagement)}
|
|
74
|
+
footer={
|
|
75
|
+
<div className="content">
|
|
76
|
+
<div className="enrollment-progress">
|
|
77
|
+
<span className="dashboard-item__label">
|
|
78
|
+
{intl.formatMessage(batchOrderSeatInfoMessages.enrolledParticipants, {
|
|
79
|
+
seats_owned: seatsOwnedCount,
|
|
80
|
+
nb_seats: batchOrder.nb_seats,
|
|
81
|
+
})}
|
|
82
|
+
</span>
|
|
83
|
+
<div className="enrollment-progress__bar">
|
|
84
|
+
<div
|
|
85
|
+
className="enrollment-progress__bar__fill"
|
|
86
|
+
style={{ width: `${(seatsOwnedCount / batchOrder.nb_seats) * 100}%` }}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
{states.error && <Banner message={states.error} type={BannerType.ERROR} />}
|
|
91
|
+
<div className="enrollment-nested-section__content">
|
|
92
|
+
<Input
|
|
93
|
+
className="enrollment-search"
|
|
94
|
+
label={intl.formatMessage(batchOrderSeatInfoMessages.searchPlaceholder)}
|
|
95
|
+
value={query}
|
|
96
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
97
|
+
rightIcon={<Icon name={IconTypeEnum.MAGNIFYING_GLASS} size="small" />}
|
|
98
|
+
/>
|
|
99
|
+
{allSeats.length === 0 && query ? (
|
|
100
|
+
<FormattedMessage {...batchOrderSeatInfoMessages.noResults} />
|
|
101
|
+
) : (
|
|
102
|
+
<>
|
|
103
|
+
<ul className="enrollment-list">
|
|
104
|
+
{allSeats.map((seat) => (
|
|
105
|
+
<li key={seat.id}>{seat.owner_name ?? seat.voucher}</li>
|
|
106
|
+
))}
|
|
107
|
+
</ul>
|
|
108
|
+
{remainingCount > 0 && (
|
|
109
|
+
<Button
|
|
110
|
+
className="enrollment-load-more"
|
|
111
|
+
color="brand"
|
|
112
|
+
variant="secondary"
|
|
113
|
+
size="small"
|
|
114
|
+
onClick={() => setPage((p) => p + 1)}
|
|
115
|
+
disabled={states.fetching}
|
|
116
|
+
>
|
|
117
|
+
{intl.formatMessage(batchOrderSeatInfoMessages.loadMore, {
|
|
118
|
+
count: remainingCount,
|
|
119
|
+
})}
|
|
120
|
+
</Button>
|
|
121
|
+
)}
|
|
122
|
+
</>
|
|
123
|
+
)}
|
|
124
|
+
</div>
|
|
125
|
+
<DownloadBatchOrderSeatsButton
|
|
126
|
+
batchOrderId={batchOrder.id}
|
|
127
|
+
productTitle={batchOrder.offering?.product.title ?? ''}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
};
|
package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/DashboardBatchOrderSubItems.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
2
2
|
import { PaymentMethod } from 'components/PaymentInterfaces/types';
|
|
3
|
-
import { BatchOrderRead } from 'types/Joanie';
|
|
3
|
+
import { BatchOrderRead, BatchOrderState } from 'types/Joanie';
|
|
4
4
|
import { DashboardSubItem } from 'widgets/Dashboard/components/DashboardItem/DashboardSubItem';
|
|
5
5
|
import { DashboardSubItemsList } from '../DashboardSubItemsList';
|
|
6
|
+
import { BatchOrderSeatInfo } from './BatchOrderSeatInfo';
|
|
7
|
+
import { BatchOrderAgreementInfo } from './BatchOrderAgreementInfo';
|
|
6
8
|
|
|
7
9
|
const messages = defineMessages({
|
|
8
10
|
stepCompany: {
|
|
@@ -144,6 +146,12 @@ const DashboardItemField = ({
|
|
|
144
146
|
export const DashboardBatchOrderSubItems = ({ batchOrder }: { batchOrder: BatchOrderRead }) => {
|
|
145
147
|
const intl = useIntl();
|
|
146
148
|
|
|
149
|
+
const displaySeatsInfo =
|
|
150
|
+
batchOrder.state === BatchOrderState.COMPLETED &&
|
|
151
|
+
!!batchOrder.nb_seats &&
|
|
152
|
+
batchOrder.seats_owned !== undefined &&
|
|
153
|
+
batchOrder.seats_to_own !== undefined;
|
|
154
|
+
|
|
147
155
|
const items = [
|
|
148
156
|
<DashboardSubItem
|
|
149
157
|
key="company"
|
|
@@ -312,5 +320,13 @@ export const DashboardBatchOrderSubItems = ({ batchOrder }: { batchOrder: BatchO
|
|
|
312
320
|
);
|
|
313
321
|
}
|
|
314
322
|
|
|
323
|
+
if (batchOrder.contract_id) {
|
|
324
|
+
items.push(<BatchOrderAgreementInfo key="agreement" batchOrder={batchOrder} />);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (displaySeatsInfo) {
|
|
328
|
+
items.push(<BatchOrderSeatInfo key="enrollment-management" batchOrder={batchOrder} />);
|
|
329
|
+
}
|
|
330
|
+
|
|
315
331
|
return <DashboardSubItemsList subItems={items} />;
|
|
316
332
|
};
|
package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/batchOrderSeatInfoMessages.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl';
|
|
2
|
+
|
|
3
|
+
export const batchOrderSeatInfoMessages = defineMessages({
|
|
4
|
+
enrolledParticipants: {
|
|
5
|
+
id: 'batchOrder.enrollmentManagement.enrolledParticipants',
|
|
6
|
+
description: 'Progress label showing enrolled participants out of total seats',
|
|
7
|
+
defaultMessage: '{seats_owned}/{nb_seats} enrolled participants',
|
|
8
|
+
},
|
|
9
|
+
searchPlaceholder: {
|
|
10
|
+
id: 'batchOrder.enrollmentManagement.searchPlaceholder',
|
|
11
|
+
description: 'Placeholder for the seat search input (student name or voucher)',
|
|
12
|
+
defaultMessage: 'Student name',
|
|
13
|
+
},
|
|
14
|
+
noResults: {
|
|
15
|
+
id: 'batchOrder.enrollmentManagement.noResults',
|
|
16
|
+
description: 'Message shown when the student search returns no results',
|
|
17
|
+
defaultMessage: 'No student matches your search.',
|
|
18
|
+
},
|
|
19
|
+
loadMore: {
|
|
20
|
+
id: 'batchOrder.enrollmentManagement.loadMore',
|
|
21
|
+
description: 'Button to load more seats',
|
|
22
|
+
defaultMessage: 'Load {count} more',
|
|
23
|
+
},
|
|
24
|
+
});
|