From 8edb62253fc70148a65196ef3716a45ec77f6e33 Mon Sep 17 00:00:00 2001
From: irfanuddinahmad <34648393+irfanuddinahmad@users.noreply.github.com>
Date: Mon, 3 Jul 2023 15:56:17 +0500
Subject: [PATCH 001/124] feat: Added column for course product line in learner
credit table (#996)
Co-authored-by: IrfanUddinAhmad
---
.../LearnerCreditAllocationTable.jsx | 7 +++
.../learner-credit-management/data/utils.js | 1 +
.../LearnerCreditAllocationTable.test.jsx | 50 +++++++++++++++++++
src/utils.js | 7 +++
4 files changed, 65 insertions(+)
create mode 100644 src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index 081eff5946..12eada0677 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -7,6 +7,7 @@ import moment from 'moment';
import TableTextFilter from './TableTextFilter';
import EmailAddressTableCell from './EmailAddressTableCell';
+import { getCourseProductLineText } from '../../utils';
export const PAGE_SIZE = 20;
export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
@@ -52,6 +53,11 @@ const LearnerCreditAllocationTable = ({
Cell: ({ row }) => moment(row.values.enrollmentDate).format('MMMM DD, YYYY'),
disableFilters: true,
},
+ {
+ Header: 'Product',
+ accessor: 'courseProductLine',
+ Cell: ({ row }) => getCourseProductLineText(row.values.courseProductLine),
+ },
]}
initialTableOptions={{
getRowId: row => row?.uuid?.toString(),
@@ -89,6 +95,7 @@ LearnerCreditAllocationTable.propTypes = {
courseTitle: PropTypes.string.isRequired,
courseListPrice: PropTypes.number.isRequired,
enrollmentDate: PropTypes.string.isRequired,
+ courseProductLine: PropTypes.string.isRequired,
})),
itemCount: PropTypes.number.isRequired,
pageCount: PropTypes.number.isRequired,
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 0ba0628302..7a9101ba16 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -58,6 +58,7 @@ export const transformUtilizationTableResults = results => results.map(result =>
courseTitle: result.courseTitle,
courseListPrice: result.courseListPrice,
enrollmentDate: result.enrollmentDate,
+ courseProductLine: result.courseProductLine,
uuid: uuidv4(),
}));
diff --git a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
new file mode 100644
index 0000000000..00ebf8f3fc
--- /dev/null
+++ b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import {
+ screen,
+ render,
+} from '@testing-library/react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+
+import LearnerCreditAllocationTable from '../LearnerCreditAllocationTable';
+
+const LearnerCreditAllocationTableWrapper = (props) => (
+
+
+
+);
+
+describe(' ', () => {
+ it('renders with table data', () => {
+ const props = {
+ enterpriseUUID: 'test-enterprise-id',
+ isLoading: false,
+ tableData: {
+ results: [{
+ userEmail: 'test@example.com',
+ courseTitle: 'course-title',
+ courseListPrice: 100,
+ enrollmentDate: '2-2-23',
+ courseProductLine: 'OCM',
+ }],
+ itemCount: 1,
+ pageCount: 1,
+ },
+ fetchTableData: jest.fn(),
+ };
+ props.fetchTableData.mockReturnValue(props.tableData);
+
+ render( );
+
+ expect(screen.getByText('Open', { exact: false }));
+ expect(screen.getByText(props.tableData.results[0].userEmail.toString(), {
+ exact: false,
+ }));
+ expect(screen.getByText(props.tableData.results[0].courseTitle.toString(), {
+ exact: false,
+ }));
+ expect(screen.getByText(props.tableData.results[0].courseListPrice.toString(), {
+ exact: false,
+ }));
+ expect(screen.getByText('February', { exact: false }));
+ });
+});
diff --git a/src/utils.js b/src/utils.js
index 6fe486e35c..f92dff63b7 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -400,6 +400,12 @@ const pollAsync = async (pollFunc, timeout, interval, checkFunc) => {
return false;
};
+const getCourseProductLineText = (courseProductLine) => {
+ let courseProductLineText = '';
+ courseProductLineText = courseProductLine === 'OCM' ? 'Open Courses' : courseProductLine;
+ return courseProductLineText;
+};
+
export {
camelCaseDict,
camelCaseDictArray,
@@ -433,4 +439,5 @@ export {
capitalizeFirstLetter,
pollAsync,
isNotValidNumberString,
+ getCourseProductLineText,
};
From f0ea4b015a1fa9a88a96bc572ffbd68e7972b24c Mon Sep 17 00:00:00 2001
From: Zaman Afzal
Date: Mon, 10 Jul 2023 16:30:52 +0500
Subject: [PATCH 002/124] refactor: fix the subscription table label value on
LPR (#1002)
---
src/components/Admin/licenses/LicenseAllocationHeader.jsx | 3 ++-
.../__snapshots__/LicenseAllocationHeader.test.jsx.snap | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/components/Admin/licenses/LicenseAllocationHeader.jsx b/src/components/Admin/licenses/LicenseAllocationHeader.jsx
index c24001e321..b7ac4bbdb8 100644
--- a/src/components/Admin/licenses/LicenseAllocationHeader.jsx
+++ b/src/components/Admin/licenses/LicenseAllocationHeader.jsx
@@ -14,6 +14,7 @@ const LicenseAllocationHeader = () => {
// don't show alert if the enterprise already has subsidy requests enabled
const isBrowseAndRequestFeatureAlertShown = subsidyRequestConfiguration?.subsidyType
=== SUPPORTED_SUBSIDY_TYPES.license && !subsidyRequestConfiguration?.subsidyRequestsEnabled;
+ const activatedAndAssigned = (subscription.licenses?.activated ?? 0) + (subscription.licenses?.assigned ?? 0);
return (
<>
{isBrowseAndRequestFeatureAlertShown && }
@@ -25,7 +26,7 @@ const LicenseAllocationHeader = () => {
Activated: {subscription.licenses?.activated}
{' of '}
- {subscription.licenses?.assigned} assigned
+ {activatedAndAssigned} assigned
>
diff --git a/src/components/Admin/licenses/__snapshots__/LicenseAllocationHeader.test.jsx.snap b/src/components/Admin/licenses/__snapshots__/LicenseAllocationHeader.test.jsx.snap
index 04f310158d..8ca7a7337b 100644
--- a/src/components/Admin/licenses/__snapshots__/LicenseAllocationHeader.test.jsx.snap
+++ b/src/components/Admin/licenses/__snapshots__/LicenseAllocationHeader.test.jsx.snap
@@ -28,7 +28,7 @@ exports[`LicenseAllocationHeader renders without crashing 1`] = `
Activated:
1
of
- 1
+ 2
assigned
From 107403e6ca96e5e6a1356889efde972dd34fd86d Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Tue, 11 Jul 2023 13:26:35 -0700
Subject: [PATCH 003/124] test: add test coverage for sidebar component (#1005)
* test: add test coverage for sidebar component
* fix: removed exact option per reviewer comment
---
src/containers/Sidebar/Sidebar.test.jsx | 45 ++++++++++++++++++++++---
1 file changed, 41 insertions(+), 4 deletions(-)
diff --git a/src/containers/Sidebar/Sidebar.test.jsx b/src/containers/Sidebar/Sidebar.test.jsx
index a77fe435d0..6ba0e531aa 100644
--- a/src/containers/Sidebar/Sidebar.test.jsx
+++ b/src/containers/Sidebar/Sidebar.test.jsx
@@ -45,6 +45,7 @@ const initialState = {
enableCodeManagementScreen: true,
enableSubscriptionManagementScreen: true,
enableAnalyticsScreen: true,
+ enableReportingConfigScreenLink: true,
},
};
@@ -269,7 +270,7 @@ describe(' ', () => {
});
render( );
- const subscriptionManagementLink = screen.queryByRole('link', { name: 'Subscription Management' }, { exact: false });
+ const subscriptionManagementLink = screen.queryByRole('link', { name: 'Subscription Management' });
expect(subscriptionManagementLink).toBeNull();
});
@@ -283,8 +284,40 @@ describe(' ', () => {
},
});
render( );
- const subscriptionManagementLink = screen.getByRole('link', { name: 'Subscription Management' }, { exact: false });
+ const subscriptionManagementLink = screen.getByRole('link', { name: 'Subscription Management' });
expect(subscriptionManagementLink).toBeInTheDocument();
+ expect(subscriptionManagementLink).toHaveAttribute('href', '/test-enterprise-slug/admin/subscriptions');
+ });
+
+ it('renders correctly when enableReportingConfigScreen is false', () => {
+ const store = mockStore({
+ sidebar: {
+ ...initialState.sidebar,
+ },
+ portalConfiguration: {
+ enableReportingConfigScreen: false,
+ },
+ });
+ features.REPORTING_CONFIGURATIONS = true;
+ render( );
+ const enableReportingConfigScreenLink = screen.queryByRole('link', { name: 'Reporting Configurations' });
+ expect(enableReportingConfigScreenLink).toBeNull();
+ });
+
+ it('renders correctly when enableReportingConfigScreen is enabled', async () => {
+ const store = mockStore({
+ sidebar: {
+ ...initialState.sidebar,
+ },
+ portalConfiguration: {
+ enableReportingConfigScreen: true,
+ },
+ });
+ features.REPORTING_CONFIGURATIONS = true;
+ render( );
+ const enableReportingConfigScreenLink = screen.getByRole('link', { name: 'Reporting Configurations' });
+ expect(enableReportingConfigScreenLink).toBeInTheDocument();
+ expect(enableReportingConfigScreenLink).toHaveAttribute('href', '/test-enterprise-slug/admin/reporting');
});
it('renders settings link if the settings page has visible tabs.', () => {
@@ -298,7 +331,9 @@ describe(' ', () => {
features.SETTINGS_PAGE = true;
render( );
- expect(screen.getByRole('link', { name: 'Settings' })).toBeInTheDocument();
+ const settingsLink = screen.getByRole('link', { name: 'Settings' });
+ expect(settingsLink).toBeInTheDocument();
+ expect(settingsLink).toHaveAttribute('href', '/test-enterprise-slug/admin/settings');
});
it('renders manage learner credit link if the canManageLearnerCredit = true.', () => {
@@ -310,7 +345,9 @@ describe(' ', () => {
});
render( );
- expect(screen.getByRole('link', { name: 'Learner Credit Management' })).toBeInTheDocument();
+ const enableLearnerCreditLink = screen.getByRole('link', { name: 'Learner Credit Management' });
+ expect(enableLearnerCreditLink).toBeInTheDocument();
+ expect(enableLearnerCreditLink).toHaveAttribute('href', '/test-enterprise-slug/admin/learner-credit');
});
it('hides manage learner credit link if the canManageLearnerCredit = false.', () => {
From a209dd019b545f8b7cea55d9f0b1d8739a6a1e45 Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Tue, 11 Jul 2023 15:59:16 -0700
Subject: [PATCH 004/124] refactor: Replace useWindowSize with Paragon's
useWindowSize (#1006)
* refactor: Replace useWindowSize with Paragon's useWindowSize
* refactor: applied changes per reviewer comment
---
.../IconWithTooltip/IconWithTooltip.test.jsx | 9 +++--
src/components/IconWithTooltip/index.jsx | 9 +++--
.../forms/tests/ValidatedFormControl.test.tsx | 4 +--
src/hooks/index.js | 1 -
src/hooks/useWindowSize.jsx | 35 -------------------
5 files changed, 10 insertions(+), 48 deletions(-)
delete mode 100644 src/hooks/useWindowSize.jsx
diff --git a/src/components/IconWithTooltip/IconWithTooltip.test.jsx b/src/components/IconWithTooltip/IconWithTooltip.test.jsx
index 6b7c996a98..c682c796fd 100644
--- a/src/components/IconWithTooltip/IconWithTooltip.test.jsx
+++ b/src/components/IconWithTooltip/IconWithTooltip.test.jsx
@@ -3,9 +3,6 @@ import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import IconWithTooltip from './index';
-import useWindowSize from '../../hooks/useWindowSize';
-
-jest.mock('./../../hooks/useWindowSize');
const defaultAltText = 'infoooo';
const defaultTooltipText = 'Tooool';
@@ -17,7 +14,8 @@ const DEFAULT_PROPS = {
describe(' ', () => {
it('renders the icon passed to it with alt text', () => {
- useWindowSize.mockReturnValue({ width: 800 });
+ global.innerWidth = 800;
+ global.dispatchEvent(new Event('resize'));
const { container } = render( );
expect(container.querySelector(`[data-icon=${faInfoCircle.iconName}]`)).toBeTruthy();
});
@@ -26,7 +24,8 @@ describe(' ', () => {
{ windowSize: 700, expectedLocation: 'top' },
].forEach((data) => {
it(`renders the tooltip on the ${data.expectedLocation} for ${data.windowSize}px screen`, () => {
- useWindowSize.mockReturnValue({ width: data.windowSize });
+ global.innerWidth = data.windowSize;
+ global.dispatchEvent(new Event('resize'));
const { container } = render( );
const icon = container.querySelector(`[data-icon=${faInfoCircle.iconName}]`);
expect(icon).toBeTruthy();
diff --git a/src/components/IconWithTooltip/index.jsx b/src/components/IconWithTooltip/index.jsx
index 9797f5b4c4..67c99fe4fb 100644
--- a/src/components/IconWithTooltip/index.jsx
+++ b/src/components/IconWithTooltip/index.jsx
@@ -1,18 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { OverlayTrigger, Tooltip } from '@edx/paragon';
-import useWindowSize from '../../hooks/useWindowSize';
+import { OverlayTrigger, Tooltip, useWindowSize } from '@edx/paragon';
const IconWithTooltip = ({
icon, altText, tooltipText, placementSm = 'right', placementLg = 'top', trigger = ['hover', 'focus'], breakpoint = 768, iconClassNames = 'ml-1',
}) => {
- const windowSize = useWindowSize();
- const placement = windowSize.width >= breakpoint ? placementSm : placementLg;
+ const { width } = useWindowSize();
+ const placement = width >= breakpoint ? placementSm : placementLg;
return (
= breakpoint ? placementSm : placementLg}
+ placement={placement}
data-testid={`tooltip-${placement}`}
overlay={(
diff --git a/src/components/forms/tests/ValidatedFormControl.test.tsx b/src/components/forms/tests/ValidatedFormControl.test.tsx
index e3df1207c9..3328a45474 100644
--- a/src/components/forms/tests/ValidatedFormControl.test.tsx
+++ b/src/components/forms/tests/ValidatedFormControl.test.tsx
@@ -28,8 +28,8 @@ const ValidatedFormControlWrapper = ({
formFields: { [formId]: formValue },
};
if (formError) {
- contextValue =
- { ...contextValue,
+ contextValue = {
+ ...contextValue,
errorMap: { [formId]: [formError] },
showErrors: true,
};
diff --git a/src/hooks/index.js b/src/hooks/index.js
index a82dfa94bc..9f02607bb4 100644
--- a/src/hooks/index.js
+++ b/src/hooks/index.js
@@ -1,3 +1,2 @@
export { default as useInterval } from './useInterval';
-export { default as useWindowSize } from './useWindowSize';
export { default as useOnMount } from './useOnMount';
diff --git a/src/hooks/useWindowSize.jsx b/src/hooks/useWindowSize.jsx
deleted file mode 100644
index 002a74e29a..0000000000
--- a/src/hooks/useWindowSize.jsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { useState, useEffect } from 'react';
-
-// Hook
-function useWindowSize() {
- // Initialize state with undefined width/height so server and client renders match
- // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
- const [windowSize, setWindowSize] = useState({
- width: undefined,
- height: undefined,
- });
-
- useEffect(() => {
- // Handler to call on window resize
- function handleResize() {
- // Set window width/height to state
- setWindowSize({
- width: window.innerWidth,
- height: window.innerHeight,
- });
- }
-
- // Add event listener
- window.addEventListener('resize', handleResize);
-
- // Call handler right away so state gets updated with initial window size
- handleResize();
-
- // Remove event listener on cleanup
- return () => window.removeEventListener('resize', handleResize);
- }, []); // Empty array ensures that effect is only run on mount
-
- return windowSize;
-}
-
-export default useWindowSize;
From af86f42eba87779a03d7e12891affb80ad68a84d Mon Sep 17 00:00:00 2001
From: jajjibhai008
Date: Mon, 10 Jul 2023 18:25:46 +0500
Subject: [PATCH 005/124] fix: do not require change password to change
reporting configuration
---
.../ReportingConfig/ReportingConfigForm.jsx | 6 ++--
.../ReportingConfigForm.test.jsx | 32 +++++++++++++++++++
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/src/components/ReportingConfig/ReportingConfigForm.jsx b/src/components/ReportingConfig/ReportingConfigForm.jsx
index c2c9d8c804..4e662c6a11 100644
--- a/src/components/ReportingConfig/ReportingConfigForm.jsx
+++ b/src/components/ReportingConfig/ReportingConfigForm.jsx
@@ -55,7 +55,7 @@ class ReportingConfigForm extends React.Component {
* @param {FormData} formData
* @param {[String]} requiredFields
*/
- validateReportingForm = (formData, requiredFields) => {
+ validateReportingForm = (config, formData, requiredFields) => {
const invalidFields = requiredFields
.filter(field => !formData.get(field))
.reduce((prevFields, currField) => ({ ...prevFields, [currField]: true }), {});
@@ -63,7 +63,7 @@ class ReportingConfigForm extends React.Component {
// Password is conditionally required only when pgp key will not be present
// and delivery method is email
if (!formData.get('pgpEncryptionKey') && formData.get('deliveryMethod') === 'email') {
- if (!formData.get('encryptedPassword')) {
+ if (!formData.get('encryptedPassword') && !config?.encryptedPassword) {
invalidFields.encryptedPassword = true;
}
}
@@ -131,7 +131,7 @@ class ReportingConfigForm extends React.Component {
requiredFields = config ? [...REQUIRED_SFTP_FIELDS] : [...REQUIRED_NEW_SFTP_FEILDS];
}
// validate the form
- const invalidFields = this.validateReportingForm(formData, requiredFields);
+ const invalidFields = this.validateReportingForm(config, formData, requiredFields);
// if there are invalid fields, reflect that in the UI
if (!isEmpty(invalidFields)) {
this.setState((state) => ({
diff --git a/src/components/ReportingConfig/ReportingConfigForm.test.jsx b/src/components/ReportingConfig/ReportingConfigForm.test.jsx
index 1e29207089..0263df7fa0 100644
--- a/src/components/ReportingConfig/ReportingConfigForm.test.jsx
+++ b/src/components/ReportingConfig/ReportingConfigForm.test.jsx
@@ -384,6 +384,38 @@ describe(' ', () => {
wrapper.find('.form-control').forEach(input => input.simulate('blur'));
expect(wrapper.find('input#encryptedPassword').hasClass('is-invalid')).toBeTruthy();
});
+ it('Password is not required while updating if it is already present and delivery method is email', async () => {
+ const updateConfigMock = jest.fn().mockResolvedValue();
+ const initialConfig = {
+ ...defaultConfig,
+ };
+ initialConfig.encryptedPassword = 'some_pass';
+ initialConfig.pgpEncryptionKey = '';
+ initialConfig.hourOfDay = 4;
+ const requiredFields = ['hourOfDay', 'emailRaw'];
+ const wrapper = mount(
+ ,
+ );
+
+ const updatedFormData = new FormData();
+ updatedFormData.append('deliveryMethod', 'email');
+ updatedFormData.append('pgpEncryptionKey', '');
+ updatedFormData.append('encryptedPassword', '');
+
+ const invalidFields = await wrapper.instance().validateReportingForm(
+ initialConfig,
+ updatedFormData,
+ requiredFields,
+ );
+ expect('encryptedPassword' in invalidFields).toBe(false);
+ });
it('Submit enterprise uuid upon report config creation', async () => {
const wrapper = mount((
Date: Thu, 13 Jul 2023 15:11:24 -0600
Subject: [PATCH 006/124] fix: updating deprecated paragon components (#1007)
* fix: removing deprecated components
* fix: reverting the changes to form
* fix: test file fixes
---
.../Admin/__snapshots__/Admin.test.jsx.snap | 48 +++---
src/components/Admin/index.jsx | 30 ++--
.../CourseSearchResults.jsx | 25 +--
.../CourseSearchResults.test.jsx | 53 +++----
.../CodeSearchResults.test.jsx.snap | 91 ++++++-----
src/components/CodeSearchResults/index.jsx | 29 ++--
.../CompletedLearnersTable.test.jsx.snap | 41 ++---
src/components/Coupon/Coupon.test.jsx | 142 +++++-------------
src/components/CouponDetails/index.jsx | 41 ++---
...rnersForInactiveCoursesTable.test.jsx.snap | 41 ++---
.../EnrolledLearnersTable.test.jsx.snap | 41 ++---
.../EnrollmentsTable.test.jsx.snap | 41 ++---
.../LearnerActivityTable.test.jsx.snap | 41 ++---
.../PlotlyAnalytics/PlotlyAnalyticsPage.jsx | 16 +-
.../ReduxFormCheckbox.test.jsx.snap | 38 +++--
src/components/ReduxFormCheckbox/index.jsx | 26 ++--
.../RegisteredLearnersTable.test.jsx.snap | 41 ++---
.../RequestCodesPage/RequestCodesForm.jsx | 17 +--
src/components/StatusAlert/_StatusAlert.scss | 12 --
src/components/StatusAlert/index.jsx | 66 --------
src/components/TableComponent/index.jsx | 29 ++--
.../CouponDetails/CouponDetails.test.jsx | 22 +--
src/index.scss | 1 -
23 files changed, 427 insertions(+), 505 deletions(-)
delete mode 100644 src/components/StatusAlert/_StatusAlert.scss
delete mode 100644 src/components/StatusAlert/index.jsx
diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
index 082fe5233c..13fb8a3fa2 100644
--- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
@@ -6332,35 +6332,43 @@ exports[` renders correctly with error state 1`] = `
className="col"
>
+
+
+
+
+
-
-
-
-
- Unable to load overview
-
- Try refreshing your screen (Network Error)
+ Hey, nice to see you
+
+ Try refreshing your screen
+ Network Error
+
diff --git a/src/components/Admin/index.jsx b/src/components/Admin/index.jsx
index a1d9ef7b85..4f95e3e94a 100644
--- a/src/components/Admin/index.jsx
+++ b/src/components/Admin/index.jsx
@@ -1,11 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
-import { Icon } from '@edx/paragon';
+import { Alert, Icon } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
import { Link } from 'react-router-dom';
import Hero from '../Hero';
-import StatusAlert from '../StatusAlert';
import EnrollmentsTable from '../EnrollmentsTable';
import RegisteredLearnersTable from '../RegisteredLearnersTable';
import EnrolledLearnersTable from '../EnrolledLearnersTable';
@@ -250,24 +250,26 @@ class Admin extends React.Component {
renderErrorMessage() {
return (
-
+
+ Hey, nice to see you
+ Try refreshing your screen {this.props.error.message}
+
);
}
renderCsvErrorMessage(message) {
return (
-
+ icon={Error}
+ >
+ Unable to generate CSV report
+ Please try again. {message}
+
);
}
diff --git a/src/components/BulkEnrollmentPage/CourseSearchResults.jsx b/src/components/BulkEnrollmentPage/CourseSearchResults.jsx
index 673e7cbef9..970cdedc1d 100644
--- a/src/components/BulkEnrollmentPage/CourseSearchResults.jsx
+++ b/src/components/BulkEnrollmentPage/CourseSearchResults.jsx
@@ -5,10 +5,10 @@ import React, {
} from 'react';
import PropTypes from 'prop-types';
import { connectStateResults } from 'react-instantsearch-dom';
-import { DataTable, Skeleton } from '@edx/paragon';
+import { Alert, DataTable, Skeleton } from '@edx/paragon';
+import { Error, ErrorOutline } from '@edx/paragon/icons';
import { SearchContext, SearchPagination } from '@edx/frontend-enterprise-catalog-search';
-import StatusAlert from '../StatusAlert';
import { CourseNameCell, FormattedDateCell } from './table/CourseSearchResultsCells';
import { BulkEnrollContext } from './BulkEnrollmentContext';
@@ -136,20 +136,21 @@ export const BaseCourseSearchResults = (props) => {
if (!isSearchStalled && error) {
return (
-
+
+ {ERROR_MESSAGE} {error.message}
+
);
}
if (!isSearchStalled && searchResults?.nbHits === 0) {
return (
-
+ {NO_DATA_MESSAGE}
+
);
}
diff --git a/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx b/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx
index c33376dac5..dd69617aa0 100644
--- a/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx
+++ b/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx
@@ -1,22 +1,20 @@
import React from 'react';
import { Provider } from 'react-redux';
-import { mount } from 'enzyme';
-import { screen, waitFor, within } from '@testing-library/react';
-import '@testing-library/jest-dom/extend-expect';
import configureMockStore from 'redux-mock-store';
import userEvent from '@testing-library/user-event';
import thunk from 'redux-thunk';
-import { SearchContext, SearchPagination } from '@edx/frontend-enterprise-catalog-search';
-import { Skeleton } from '@edx/paragon';
+import {
+ render, screen, waitFor, within,
+} from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+import { SearchContext } from '@edx/frontend-enterprise-catalog-search';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import StatusAlert from '../StatusAlert';
import BulkEnrollContextProvider from './BulkEnrollmentContext';
import {
BaseCourseSearchResults, NO_DATA_MESSAGE, TABLE_HEADERS,
} from './CourseSearchResults';
import { renderWithRouter } from '../test/testUtils';
-
import '../../../__mocks__/react-instantsearch-dom';
const mockStore = configureMockStore([thunk]);
@@ -97,21 +95,15 @@ const CourseSearchWrapper = ({ value = { refinements }, props = defaultProps })
describe(' ', () => {
it('renders search results', () => {
- const wrapper = mount( );
-
- // 5 header columns: selection, Course name, Partner, Course Date, and enrollment
- const tableHeaderCells = wrapper.find('TableHeaderCell');
- expect(tableHeaderCells.length).toBe(4);
- expect(tableHeaderCells.at(1).prop('Header')).toBe(TABLE_HEADERS.courseName);
- expect(tableHeaderCells.at(2).prop('Header')).toBe(TABLE_HEADERS.partnerName);
- expect(tableHeaderCells.at(3).prop('Header')).toBe(TABLE_HEADERS.courseAvailability);
+ render( );
+ screen.getByRole('columnheader', { name: TABLE_HEADERS.courseName });
+ screen.getByRole('columnheader', { name: TABLE_HEADERS.partnerName });
+ screen.getByRole('columnheader', { name: TABLE_HEADERS.courseAvailability });
- // 5 table cells: selection, course name, partner, start date, and enrollment
- const tableCells = wrapper.find('TableCell');
- expect(tableCells.length).toBe(8); // 2 rows x 4 columns
- expect(tableCells.at(1).text()).toBe(testCourseName);
- expect(tableCells.at(2).text()).toBe('edX');
- expect(tableCells.at(3).text()).toBe('Sep 10, 2020 - Sep 10, 2030');
+ expect(screen.getAllByRole('cell')).toHaveLength(8);
+ screen.getByRole('cell', { name: testCourseName });
+ screen.getByRole('cell', { name: 'Sep 10, 2020 - Sep 10, 2030' });
+ expect(screen.getAllByRole('cell', { name: 'edX' })).toHaveLength(2);
});
it('renders popover with course description', async () => {
renderWithRouter( );
@@ -123,17 +115,19 @@ describe(' ', () => {
});
});
it('displays search pagination', () => {
- const wrapper = mount( );
- expect(wrapper.find(SearchPagination)).toHaveLength(1);
+ renderWithRouter( );
+ expect(screen.getByText('Navigate Right'));
+ expect(screen.getByText('Navigate Left'));
});
it('returns an error message if there\'s an error', () => {
const errorMsg = 'It did not work';
- const wrapper = mount( );
- expect(wrapper.text()).toContain(errorMsg);
+ const expectedError = `An error occured while retrieving data ${errorMsg}`;
+ renderWithRouter( );
+ expect(screen.getByText(expectedError));
});
it('renders a loading state when loading algolia results', () => {
- const wrapper = mount( );
- expect(wrapper.find(Skeleton)).toHaveLength(1);
+ renderWithRouter( );
+ expect(screen.getByText('Loading...'));
});
it('shows selection options when at least one course is selected', () => {
renderWithRouter( );
@@ -142,10 +136,9 @@ describe(' ', () => {
expect(screen.getByText('1 selected (1 shown below)', { exact: false })).toBeInTheDocument();
});
it('renders a message when there are no results', () => {
- const wrapper = mount( );
- expect(wrapper.find(StatusAlert)).toHaveLength(1);
- expect(wrapper.text()).toContain(NO_DATA_MESSAGE);
+ expect(screen.getByText(NO_DATA_MESSAGE));
});
});
diff --git a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
index d411e7d841..8395dc41f8 100644
--- a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
+++ b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
@@ -55,30 +55,35 @@ exports[` basic rendering should render empty table data 1`
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
@@ -135,42 +140,50 @@ exports[` basic rendering should render error 1`] = `
Close search results
+
+
+
+
+
-
-
-
-
- Unable to load data
-
- Try refreshing your screen (Network Error)
+ Unable to load data
+
+ Try refreshing your screen
+ Network Error
+
diff --git a/src/components/CodeSearchResults/index.jsx b/src/components/CodeSearchResults/index.jsx
index 8dbee143c4..d6fe3ebea3 100644
--- a/src/components/CodeSearchResults/index.jsx
+++ b/src/components/CodeSearchResults/index.jsx
@@ -1,10 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { TransitionReplace } from '@edx/paragon';
+import { Alert, TransitionReplace } from '@edx/paragon';
+import { CheckCircle } from '@edx/paragon/icons';
import { updateUrl } from '../../utils';
-import StatusAlert from '../StatusAlert';
import CodeSearchResultsHeading from './CodeSearchResultsHeading';
import CodeSearchResultsTable from './CodeSearchResultsTable';
@@ -57,14 +57,15 @@ class CodeSearchResults extends React.Component {
});
};
- renderSuccessMessage = options => (
- (
+
+ >
+ {message}
+
);
render() {
@@ -82,12 +83,12 @@ class CodeSearchResults extends React.Component {
searchQuery={searchQuery}
onClose={onClose}
/>
- {isCodeReminderSuccessful && this.renderSuccessMessage({
- message: `A reminder was successfully sent to ${searchQuery}.`,
- })}
- {isCodeRevokeSuccessful && this.renderSuccessMessage({
- message: 'Successfully revoked code(s)',
- })}
+ {isCodeReminderSuccessful && this.renderSuccessMessage(
+ `A reminder was successfully sent to ${searchQuery}.`,
+ )}
+ {isCodeRevokeSuccessful && this.renderSuccessMessage(
+ 'Successfully revoked code(s)',
+ )}
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
diff --git a/src/components/Coupon/Coupon.test.jsx b/src/components/Coupon/Coupon.test.jsx
index 3d61077d8a..e0a7bd0730 100644
--- a/src/components/Coupon/Coupon.test.jsx
+++ b/src/components/Coupon/Coupon.test.jsx
@@ -1,16 +1,18 @@
import React from 'react';
import { Provider } from 'react-redux';
-import renderer from 'react-test-renderer';
+import {
+ fireEvent, render, screen, waitFor,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import configureMockStore from 'redux-mock-store';
+import renderer from 'react-test-renderer';
import thunk from 'redux-thunk';
-import { shallow, mount } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { MULTI_USE } from '../../data/constants/coupons';
import Coupon from './index';
-import CouponDetails from '../CouponDetails';
const enterpriseId = 'test-enterprise';
const mockStore = configureMockStore([thunk]);
@@ -59,152 +61,88 @@ const CouponWrapper = props => (
);
describe(' ', () => {
- let wrapper;
-
- const simulateCouponClick = () => {
- wrapper.find(Coupon).find('.metadata').simulate('click');
- };
-
- const simulateCouponKeyDown = (key) => {
- wrapper.find(Coupon).find('.metadata').simulate('keydown', { key });
- };
-
describe('renders correctly', () => {
it('with max uses', () => {
- const tree = renderer
- .create((
-
- ))
- .toJSON();
- expect(tree).toMatchSnapshot();
+ const coupon = renderer.create(( )).toJSON();
+ expect(coupon).toMatchSnapshot();
});
- it('without max uses', () => {
- const tree = renderer
- .create((
+ it("without max uses", () => {
+ const coupon = renderer
+ .create(
- ))
+ )
.toJSON();
- expect(tree).toMatchSnapshot();
+ expect(coupon).toMatchSnapshot();
});
- it('with error state', () => {
- const tree = renderer
- .create((
+ it("with error state", () => {
+ const coupon = renderer
+ .create(
- ))
+ )
.toJSON();
- expect(tree).toMatchSnapshot();
+ expect(coupon).toMatchSnapshot();
});
});
describe('expands and collapses correctly', () => {
- it('expands on click of collapsed coupon', () => {
+ it('expands on click of collapsed coupon', async () => {
const mockOnExpand = jest.fn();
- wrapper = mount((
-
- ));
-
- simulateCouponClick();
- expect(mockOnExpand).toBeCalledTimes(1);
- expect(wrapper.find(Coupon).find(CouponDetails).prop('isExpanded')).toBeTruthy();
+ render( );
+ await waitFor(() => {
+ userEvent.click(screen.getByText(initialCouponData.title));
+ expect(mockOnExpand).toBeCalledTimes(1);
+ });
});
- it('collapses on click of expanded coupon', () => {
+ it('collapses on click of expanded coupon', async () => {
const mockOnCollapse = jest.fn();
- wrapper = mount((
-
- ));
-
- simulateCouponClick();
- expect(wrapper.find(Coupon).find(CouponDetails).prop('isExpanded')).toBeTruthy();
-
- simulateCouponClick();
- expect(mockOnCollapse).toBeCalledTimes(1);
- expect(wrapper.find(Coupon).find(CouponDetails).prop('isExpanded')).toBeFalsy();
+ render( );
+ await waitFor(() => {
+ userEvent.click(screen.getByText(initialCouponData.title));
+ expect(mockOnCollapse).toBeCalledTimes(1);
+ });
});
- it('expands on keydown of collapsed coupon', () => {
+ it('expands on keydown of expanded coupon', () => {
const mockOnExpand = jest.fn();
- wrapper = mount((
-
- ));
-
- simulateCouponKeyDown('Enter');
+ render( );
+ fireEvent.keyDown(screen.getByRole('button'), { key: 'Enter', code: 'Enter', charCode: 13 });
expect(mockOnExpand).toBeCalledTimes(1);
- expect(wrapper.find(Coupon).find(CouponDetails).prop('isExpanded')).toBeTruthy();
});
it('collapses on keydown of expanded coupon', () => {
const mockOnCollapse = jest.fn();
- wrapper = mount((
-
- ));
+ render( );
- simulateCouponKeyDown('Enter');
- expect(wrapper.find(Coupon).find(CouponDetails).prop('isExpanded')).toBeTruthy();
-
- simulateCouponKeyDown('Enter');
+ fireEvent.keyDown(screen.getByRole('button'), { key: 'Enter', code: 'Enter', charCode: 13 });
+ fireEvent.keyDown(screen.getAllByRole('button')[0], { key: 'Enter', code: 'Enter', charCode: 13 });
expect(mockOnCollapse).toBeCalledTimes(1);
- expect(wrapper.find(Coupon).find(CouponDetails).prop('isExpanded')).toBeFalsy();
});
it('does not handle unknown keydown event', () => {
const mockOnExpandOrOnCollapse = jest.fn();
- wrapper = mount((
+ render(
- ));
-
- simulateCouponKeyDown('A');
+ );
+ fireEvent.keyDown(screen.getByRole('button'), { key: 'A', code: 'KeyA' });
expect(mockOnExpandOrOnCollapse).toBeCalledTimes(0);
});
});
-
- it('sets state correctly on setCouponOpacity()', () => {
- wrapper = shallow( );
- const instance = wrapper.find(Coupon).dive().instance();
-
- expect(instance.state.dimmed).toBeFalsy();
- instance.setCouponOpacity(true);
- expect(instance.state.dimmed).toBeTruthy();
- });
-
- it('sets state correctly for isExpanded prop on componentDidMount', () => {
- wrapper = mount( );
- expect(wrapper.find(Coupon).instance().state.isExpanded).toBeTruthy();
- });
-
- it('correctly handles isExpanded prop change', () => {
- wrapper = mount( );
- expect(wrapper.find(Coupon).instance().state.isExpanded).toBeFalsy();
-
- wrapper.setProps({
- isExpanded: true,
- });
-
- expect(wrapper.find(Coupon).instance().state.isExpanded).toBeTruthy();
- });
});
diff --git a/src/components/CouponDetails/index.jsx b/src/components/CouponDetails/index.jsx
index ee3f543eaa..fff0b0c661 100644
--- a/src/components/CouponDetails/index.jsx
+++ b/src/components/CouponDetails/index.jsx
@@ -2,15 +2,15 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
- Button, CheckBox, Icon,
+ Alert, Button, CheckBox, Icon,
} from '@edx/paragon';
+import { CheckCircle, Error } from '@edx/paragon/icons';
import TableContainer from '../../containers/TableContainer';
import DownloadCsvButton from '../../containers/DownloadCsvButton';
import CodeAssignmentModal from '../../containers/CodeAssignmentModal';
import CodeReminderModal from '../../containers/CodeReminderModal';
import CodeRevokeModal from '../../containers/CodeRevokeModal';
-import StatusAlert from '../StatusAlert';
import EcommerceApiService from '../../data/services/EcommerceApiService';
import { updateUrl } from '../../utils';
@@ -475,12 +475,13 @@ class CouponDetails extends React.Component {
renderErrorMessage({ title, message }) {
return (
-
+
+ {title}
+ {message}
+
);
}
@@ -491,15 +492,16 @@ class CouponDetails extends React.Component {
} = this.props;
return (
- 0 || couponOverviewError })}
- iconClassName="fa fa-check"
- title={title}
- message={message}
onClose={this.resetCodeActionStatus}
dismissible
- />
+ >
+ {title}
+ {message}
+
);
}
@@ -510,12 +512,13 @@ class CouponDetails extends React.Component {
} = this.props;
return (
- 0 || couponOverviewError })}
- title={title}
- message={message}
- />
+ >
+ {title}
+ {message}
+
);
}
diff --git a/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap b/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap
index 347187a232..ea62dd2bae 100644
--- a/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap
+++ b/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap
@@ -2,30 +2,35 @@
exports[`EnrolledLearnersForInactiveCoursesTable renders empty state correctly 1`] = `
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
diff --git a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap
index d0e5bbe03d..301e26406f 100644
--- a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap
+++ b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap
@@ -2,30 +2,35 @@
exports[`EnrolledLearnersTable renders empty state correctly 1`] = `
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
diff --git a/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap b/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap
index 4422854152..589f3003a1 100644
--- a/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap
+++ b/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap
@@ -2,30 +2,35 @@
exports[`EnrollmentsTable renders empty state correctly 1`] = `
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
diff --git a/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap b/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap
index 4f4a143990..37df5114a7 100644
--- a/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap
+++ b/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap
@@ -490,30 +490,35 @@ exports[`LearnerActivityTable renders active learners table correctly 1`] = `
exports[`LearnerActivityTable renders empty state correctly 1`] = `
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
diff --git a/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx b/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx
index 3204c2ad00..a28f0eae3b 100644
--- a/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx
+++ b/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx
@@ -3,8 +3,9 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
+import { Alert } from '@edx/paragon';
+import { CheckCircle, Error } from '@edx/paragon/icons';
import Hero from '../Hero';
-import StatusAlert from '../StatusAlert';
import PlotlyAnalyticsCharts from './PlotlyAnalyticsCharts';
const PAGE_TITLE = 'Analytics';
@@ -24,14 +25,15 @@ const PlotlyAnalyticsPage = ({ enterpriseId }) => {
const renderStatusMessage = () => (
status && status.visible && (
- setSuccessStatus({ visible: false })}
dismissible
- />
+ >
+ {status.title}
+ {status.message}
+
)
);
diff --git a/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap b/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap
index bb52ae5727..89d00e3afa 100644
--- a/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap
+++ b/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap
@@ -1,39 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[` renders checked correctly 1`] = `
-
+
`;
exports[` renders unchecked correctly 1`] = `
-
+
`;
diff --git a/src/components/ReduxFormCheckbox/index.jsx b/src/components/ReduxFormCheckbox/index.jsx
index f6971d15d9..fd67ca15b8 100644
--- a/src/components/ReduxFormCheckbox/index.jsx
+++ b/src/components/ReduxFormCheckbox/index.jsx
@@ -1,35 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Form, ValidationFormGroup } from '@edx/paragon';
+import { Form } from '@edx/paragon';
const ReduxFormCheckbox = (props) => {
const {
id,
label,
- helptext,
+ helpText,
input,
defaultChecked,
} = props;
return (
-
-
+
-
+ description={helpText}
+ >
+ {label}
+
+
);
};
ReduxFormCheckbox.defaultProps = {
- helptext: null,
+ helpText: null,
defaultChecked: false,
};
@@ -39,7 +37,7 @@ ReduxFormCheckbox.propTypes = {
input: PropTypes.shape({
checked: PropTypes.bool,
}).isRequired,
- helptext: PropTypes.string,
+ helpText: PropTypes.string,
defaultChecked: PropTypes.bool,
};
diff --git a/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap b/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap
index d9248f4dbc..c66304e864 100644
--- a/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap
+++ b/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap
@@ -2,30 +2,35 @@
exports[`RegisteredLearnersTable renders empty state correctly 1`] = `
+
+
+
+
+
-
-
-
-
- There are no results.
-
+ There are no results.
diff --git a/src/components/RequestCodesPage/RequestCodesForm.jsx b/src/components/RequestCodesPage/RequestCodesForm.jsx
index 2baab95c3e..50fdb226ce 100644
--- a/src/components/RequestCodesPage/RequestCodesForm.jsx
+++ b/src/components/RequestCodesPage/RequestCodesForm.jsx
@@ -2,10 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { Link, Redirect } from 'react-router-dom';
-import { Button, Icon } from '@edx/paragon';
+import { Alert, Button, Icon } from '@edx/paragon';
import RenderField from '../RenderField';
-import StatusAlert from '../StatusAlert';
import {
isRequired, isValidEmail, isNotValidNumberString, maxLength512,
@@ -14,7 +13,6 @@ import {
class RequestCodesForm extends React.Component {
constructor(props) {
super(props);
-
this.renderRedirect = this.renderRedirect.bind(this);
}
@@ -29,13 +27,14 @@ class RequestCodesForm extends React.Component {
const { error: { message } } = this.props;
return (
-
+ variant="danger"
+ icon={Error}
+ >
+ Unable to request more codes
+ Try refreshing your screen {message}
+
);
}
diff --git a/src/components/StatusAlert/_StatusAlert.scss b/src/components/StatusAlert/_StatusAlert.scss
deleted file mode 100644
index 53582680ea..0000000000
--- a/src/components/StatusAlert/_StatusAlert.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-.alert {
- .icon {
- .fa {
- font-size: $font-size-lg;
- }
- }
-
- .title {
- font-weight: 600;
- display: block;
- }
-}
diff --git a/src/components/StatusAlert/index.jsx b/src/components/StatusAlert/index.jsx
deleted file mode 100644
index 290f9b6c5d..0000000000
--- a/src/components/StatusAlert/index.jsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from 'react';
-import classNames from 'classnames';
-import PropTypes from 'prop-types';
-import { StatusAlert as Alert, Icon } from '@edx/paragon';
-
-const StatusAlert = (props) => {
- const {
- alertType,
- className,
- iconClassName,
- title,
- message,
- dismissible,
- onClose,
- } = props;
-
- return (
-
- {iconClassName && (
-
-
-
- )}
-
- {title && (
- {title}
- )}
- {message}
-
-
- )}
- onClose={onClose}
- open
- />
- );
-};
-
-StatusAlert.propTypes = {
- alertType: PropTypes.string.isRequired,
- message: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
- className: PropTypes.string,
- iconClassName: PropTypes.string,
- title: PropTypes.string,
- dismissible: PropTypes.bool,
- onClose: PropTypes.func,
-};
-
-StatusAlert.defaultProps = {
- className: '',
- iconClassName: undefined,
- title: null,
- dismissible: false,
- onClose: () => {},
-};
-
-export default StatusAlert;
diff --git a/src/components/TableComponent/index.jsx b/src/components/TableComponent/index.jsx
index 5a45062feb..6a79d6b060 100644
--- a/src/components/TableComponent/index.jsx
+++ b/src/components/TableComponent/index.jsx
@@ -2,12 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-import { Pagination, Table } from '@edx/paragon';
+import { Alert, Pagination, Table } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
import 'font-awesome/css/font-awesome.css';
import TableLoadingSkeleton from './TableLoadingSkeleton';
import TableLoadingOverlay from '../TableLoadingOverlay';
-import StatusAlert from '../StatusAlert';
import { updateUrl } from '../../utils';
class TableComponent extends React.Component {
@@ -124,22 +125,24 @@ class TableComponent extends React.Component {
renderErrorMessage() {
return (
-
+
+ Unable to load data
+ Try refreshing your screen {this.props.error.message}
+
);
}
renderEmptyDataMessage() {
return (
-
+
+ There are no results.
+
);
}
diff --git a/src/containers/CouponDetails/CouponDetails.test.jsx b/src/containers/CouponDetails/CouponDetails.test.jsx
index 1975c9b362..0499565dbb 100644
--- a/src/containers/CouponDetails/CouponDetails.test.jsx
+++ b/src/containers/CouponDetails/CouponDetails.test.jsx
@@ -10,7 +10,7 @@ import { mount } from 'enzyme';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
-import { StatusAlert } from '@edx/paragon';
+import { Alert } from '@edx/paragon';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { SINGLE_USE, MULTI_USE, ONCE_PER_CUSTOMER } from '../../data/constants/coupons';
@@ -341,8 +341,8 @@ describe('CouponDetails container', () => {
/>
));
- expect(wrapper.find(StatusAlert).prop('alertType')).toEqual('danger');
- wrapper.find(StatusAlert).find('.alert-dialog .btn').simulate('click'); // Retry fetching coupon overview data
+ expect(wrapper.find(Alert).prop('variant')).toEqual('danger');
+ wrapper.find(Alert).find('.btn').simulate('click'); // Retry fetching coupon overview data
expect(spy).toBeCalledTimes(1);
expect(spy).toBeCalledWith({ coupon_id: initialCouponData.id });
@@ -507,13 +507,13 @@ describe('CouponDetails container', () => {
wrapper.update();
// success status alert
- const statusAlert = wrapper.find(StatusAlert);
- expect(statusAlert.prop('alertType')).toEqual('success');
+ const statusAlert = wrapper.find(Alert);
+ expect(statusAlert.prop('variant')).toEqual('success');
expect(statusAlert.text()).toContain(SUCCESS_MESSAGES.assign);
- statusAlert.find('.alert-dialog .btn').simulate('click');
+ statusAlert.find('.btn-tertiary').simulate('click');
// after alert is dismissed
- expect(wrapper.find(StatusAlert)).toHaveLength(0);
+ expect(wrapper.find(Alert)).toHaveLength(0);
expect(wrapper.find('CouponDetails').text()).toContain(COUPON_FILTERS.unredeemed.label);
// fetches overview data for coupon
@@ -532,8 +532,8 @@ describe('CouponDetails container', () => {
wrapper.update();
// success status alert
- const statusAlert = wrapper.find(StatusAlert);
- expect(statusAlert.prop('alertType')).toEqual('success');
+ const statusAlert = wrapper.find(Alert);
+ expect(statusAlert.prop('variant')).toEqual('success');
expect(statusAlert.text()).toContain(SUCCESS_MESSAGES.revoke);
// fetches overview data for coupon
@@ -552,8 +552,8 @@ describe('CouponDetails container', () => {
wrapper.update();
// success status alert
- const statusAlert = wrapper.find(StatusAlert);
- expect(statusAlert.prop('alertType')).toEqual('success');
+ const statusAlert = wrapper.find(Alert);
+ expect(statusAlert.prop('variant')).toEqual('success');
expect(statusAlert.text()).toContain(SUCCESS_MESSAGES.remind);
// does not fetch overview data for coupon
diff --git a/src/index.scss b/src/index.scss
index 8b689cfd02..e7f2a1fa57 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -19,7 +19,6 @@ $fa-font-path: "~font-awesome/fonts";
@import "./components/RequestCodesPage/RequestCodesPage";
@import "./components/Sidebar/Sidebar";
@import "./components/subscriptions/styles/";
-@import "./components/StatusAlert/StatusAlert";
@import "./components/LoadingMessage/LoadingMessage";
@import "./components/TableComponent/TableComponent";
@import "./components/TableLoadingOverlay/TableLoadingOverlay";
From 71de4bc001eb9cf09e9118c0d495b250af9df2ee Mon Sep 17 00:00:00 2001
From: Zaman Afzal
Date: Mon, 17 Jul 2023 13:01:30 +0500
Subject: [PATCH 007/124] refactor: sort enterprise enrollments ordering by
default on last_activity_date (#1008)
---
src/components/EnrollmentsTable/index.jsx | 2 ++
src/components/TableComponent/index.jsx | 10 ++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/components/EnrollmentsTable/index.jsx b/src/components/EnrollmentsTable/index.jsx
index c1f0fb29b7..cb9d84acb5 100644
--- a/src/components/EnrollmentsTable/index.jsx
+++ b/src/components/EnrollmentsTable/index.jsx
@@ -109,6 +109,8 @@ const EnrollmentsTable = () => {
fetchMethod={EnterpriseDataApiService.fetchCourseEnrollments}
columns={enrollmentTableColumns}
formatData={formatEnrollmentData}
+ defaultSortIndex={8}
+ defaultSortType="desc"
tableSortable
/>
);
diff --git a/src/components/TableComponent/index.jsx b/src/components/TableComponent/index.jsx
index 6a79d6b060..e8639e92a0 100644
--- a/src/components/TableComponent/index.jsx
+++ b/src/components/TableComponent/index.jsx
@@ -54,6 +54,8 @@ class TableComponent extends React.Component {
id,
loading,
enterpriseId,
+ defaultSortIndex,
+ defaultSortType,
} = this.props;
const sortByColumn = (column, direction) => {
@@ -77,8 +79,8 @@ class TableComponent extends React.Component {
let sortColumn;
if (tableSortable) {
- sortDirection = ordering && ordering.indexOf('-') !== -1 ? 'desc' : 'asc';
- sortColumn = (ordering && ordering.replace('-', '')) || columnConfig[0].key;
+ sortDirection = defaultSortType || (ordering && ordering.indexOf('-') !== -1 ? 'desc' : 'asc');
+ sortColumn = (ordering && ordering.replace('-', '')) || columnConfig[defaultSortIndex].key;
}
return (
@@ -172,6 +174,8 @@ TableComponent.propTypes = {
columns: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
formatData: PropTypes.func.isRequired,
tableSortable: PropTypes.bool,
+ defaultSortIndex: PropTypes.number,
+ defaultSortType: PropTypes.string,
// Props expected from TableContainer / redux store
enterpriseId: PropTypes.string.isRequired,
@@ -191,6 +195,8 @@ TableComponent.propTypes = {
TableComponent.defaultProps = {
className: null,
+ defaultSortIndex: 0,
+ defaultSortType: undefined,
tableSortable: false,
data: undefined,
ordering: undefined,
From 70536da79f8803f45d02ca3946529cc06da4cd4e Mon Sep 17 00:00:00 2001
From: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
Date: Fri, 21 Jul 2023 10:59:25 +0500
Subject: [PATCH 008/124] feat: upgraded to node v18, added .nvmrc and updated
workflows (#977)
* feat: upgraded to node v18, added .nvmrc and updated workflows
* refactor: updated packages
* refactor: updated packages
* refactor: updated packages
* refactor: updated frontend-build
* refactor: updated packages
* refactor: updated packages
* refactor: updated packages and resolved typescript issue
* refactor: updated enterprise, platform packages
* refactor: updated packages and snapshots
* refactor: updated packages & lockfileversion workflow
* refactor: updated postcss
* refactor: pinned node version to 18.15
* refactor: reverted frontend-build version & updated packages
* refactor: updated frontend-build
* refactor: updated frontend-build to v12.9.0.alpha.1
* refactor: updated packages and snapshots
* refactor: updated testing-library dom & jest-dom packages
* refactor: updated packages and snapshots
* refactor: updated nvmrc & removed babel.config
---------
Co-authored-by: Adam Stankiewicz
---
.github/workflows/ci.yml | 13 +-
.github/workflows/lockfileversion-check.yml | 2 +-
.nvmrc | 2 +-
babel.config.js | 6 -
package-lock.json | 23149 +-----------------
package.json | 18 +-
6 files changed, 608 insertions(+), 22582 deletions(-)
delete mode 100644 babel.config.js
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7252f910ad..5c8d85d29a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,18 +9,15 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
- strategy:
- matrix:
- node: [16]
- npm: [8.5.x]
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
+ - name: Setup Nodejs Env
+ run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- name: Setup Nodejs
- uses: actions/setup-node@v1
+ uses: actions/setup-node@v3
with:
- node-version: ${{ matrix.node }}
- - run: npm install -g npm@${{ matrix.npm }}
+ node-version: ${{ env.NODE_VER }}
- name: Validate no uncommitted package-lock changes
run: make validate-no-uncommitted-package-lock-changes
- name: Install dependencies
diff --git a/.github/workflows/lockfileversion-check.yml b/.github/workflows/lockfileversion-check.yml
index 901adddf67..916dcb40d2 100644
--- a/.github/workflows/lockfileversion-check.yml
+++ b/.github/workflows/lockfileversion-check.yml
@@ -10,4 +10,4 @@ on:
jobs:
version-check:
- uses: edx/.github/.github/workflows/lockfileversion-check.yml@master
+ uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master
diff --git a/.nvmrc b/.nvmrc
index b6a7d89c68..3c032078a4 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-16
+18
diff --git a/babel.config.js b/babel.config.js
deleted file mode 100644
index 224dba2c2e..0000000000
--- a/babel.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* eslint-disable import/no-extraneous-dependencies */
-const { getBaseConfig } = require('@edx/frontend-build');
-
-const config = getBaseConfig('babel');
-
-module.exports = config;
diff --git a/package-lock.json b/package-lock.json
index 6547468085..7cb996d04f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,7 +1,7 @@
{
"name": "frontend-app-admin-portal",
"version": "0.1.0",
- "lockfileVersion": 2,
+ "lockfileVersion": 3,
"requires": true,
"packages": {
"": {
@@ -11,11 +11,11 @@
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
- "@edx/frontend-enterprise-catalog-search": "3.1.5",
- "@edx/frontend-enterprise-hotjar": "1.2.0",
- "@edx/frontend-enterprise-logistration": "2.1.0",
- "@edx/frontend-enterprise-utils": "2.1.0",
- "@edx/frontend-platform": "2.4.0",
+ "@edx/frontend-enterprise-catalog-search": "4.2.0",
+ "@edx/frontend-enterprise-hotjar": "1.3.0",
+ "@edx/frontend-enterprise-logistration": "3.2.0",
+ "@edx/frontend-enterprise-utils": "3.2.0",
+ "@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
"@fortawesome/fontawesome-svg-core": "1.2.35",
"@fortawesome/free-brands-svg-icons": "5.15.3",
@@ -69,8 +69,8 @@
"@edx/browserslist-config": "1.0.0",
"@edx/frontend-build": "^12.9.0-alpha.1",
"@faker-js/faker": "^7.6.0",
- "@testing-library/dom": "7.31.2",
- "@testing-library/jest-dom": "5.11.9",
+ "@testing-library/dom": "9.3.1",
+ "@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "11.2.7",
"@testing-library/react-hooks": "5.0.3",
"@testing-library/user-event": "12.8.3",
@@ -81,7 +81,7 @@
"identity-obj-proxy": "3.0.0",
"jest-canvas-mock": "^2.4.0",
"jest-localstorage-mock": "^2.4.22",
- "postcss": "8.1.0",
+ "postcss": "8.4.24",
"react-dev-utils": "11.0.4",
"react-test-renderer": "16.13.1",
"resize-observer-polyfill": "1.5.1",
@@ -97,6 +97,12 @@
"webpack-merge": "5.4.0"
}
},
+ "node_modules/@adobe/css-tools": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz",
+ "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==",
+ "dev": true
+ },
"node_modules/@algolia/cache-browser-local-storage": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.8.3.tgz",
@@ -233,7 +239,6 @@
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.21.0.tgz",
"integrity": "sha512-xi7CxyS8XjSyiwUGCfwf+brtJxjW1/ZTcBUkP10xawIEXLX5HzLn+3aXkgxozcP2UhRhtKTmQurw9Uaes7jZrA==",
- "dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
"commander": "^4.0.1",
@@ -306,33 +311,6 @@
"url": "https://opencollective.com/babel"
}
},
- "node_modules/@babel/eslint-parser": {
- "version": "7.21.8",
- "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.8.tgz",
- "integrity": "sha512-HLhI+2q+BP3sf78mFUZNCGc10KEmoUqtUT1OCdMZsN+qr4qFeLUod62/zAnF3jNQstwyasDkZnVXwfK2Bml7MQ==",
- "peer": true,
- "dependencies": {
- "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
- "eslint-visitor-keys": "^2.1.0",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
- },
- "peerDependencies": {
- "@babel/core": ">=7.11.0",
- "eslint": "^7.5.0 || ^8.0.0"
- }
- },
- "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
- "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
- "peer": true,
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@babel/generator": {
"version": "7.22.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz",
@@ -1798,7 +1776,6 @@
"version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz",
"integrity": "sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==",
- "dev": true,
"dependencies": {
"@babel/compat-data": "^7.21.4",
"@babel/helper-compilation-targets": "^7.21.4",
@@ -1952,12 +1929,12 @@
}
},
"node_modules/@babel/runtime-corejs3": {
- "version": "7.20.1",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz",
- "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.22.5.tgz",
+ "integrity": "sha512-TNPDN6aBFaUox2Lu+H/Y1dKKQgr4ucz/FGyCz67RVYLsBpVpUFf1dDngzg+Od8aqbrqwyztkaZjtWCZEUOT8zA==",
"dependencies": {
- "core-js-pure": "^3.25.1",
- "regenerator-runtime": "^0.13.10"
+ "core-js-pure": "^3.30.2",
+ "regenerator-runtime": "^0.13.11"
},
"engines": {
"node": ">=6.9.0"
@@ -2142,7 +2119,6 @@
"version": "4.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.0.0-alpha.1.tgz",
"integrity": "sha512-+Hx8r6z+DdwryqluA0MF7S/RCurakzQs4AfNufWu2BLcIaf/Jot19NfcxS4Dzo8WUWiBL+lo4/42fclHHrvk9Q==",
- "dev": true,
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
@@ -2159,7 +2135,6 @@
"version": "12.9.0-alpha.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.9.0-alpha.1.tgz",
"integrity": "sha512-boHfp7yzUn5JptWGi2jK/EMaIlJ4nzjZBp7PW19vkNokbuQ3ansJsRvMscFg83tw0lXRTKCPN1y0uKg92AMizg==",
- "dev": true,
"dependencies": {
"@babel/cli": "7.21.0",
"@babel/core": "7.21.4",
@@ -2232,14 +2207,12 @@
"node_modules/@edx/frontend-build/node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
- "dev": true
+ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
},
"node_modules/@edx/frontend-build/node_modules/@types/jest": {
"version": "26.0.24",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz",
"integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==",
- "dev": true,
"dependencies": {
"jest-diff": "^26.0.0",
"pretty-format": "^26.0.0"
@@ -2249,7 +2222,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -2264,7 +2236,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -2273,7 +2244,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -2289,7 +2259,6 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
"integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==",
- "dev": true,
"dependencies": {
"source-map": "~0.6.0"
},
@@ -2301,7 +2270,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2310,7 +2278,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -2321,14 +2288,12 @@
"node_modules/@edx/frontend-build/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/@edx/frontend-build/node_modules/commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
- "dev": true,
"engines": {
"node": ">= 12"
}
@@ -2337,7 +2302,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "dev": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0",
@@ -2353,7 +2317,6 @@
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
"integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
- "dev": true,
"dependencies": {
"icss-utils": "^5.1.0",
"loader-utils": "^2.0.0",
@@ -2381,7 +2344,6 @@
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -2396,7 +2358,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
"integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "dev": true,
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.0.1",
@@ -2412,7 +2373,6 @@
"version": "5.1.15",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz",
"integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
- "dev": true,
"dependencies": {
"cssnano-preset-default": "^5.2.14",
"lilconfig": "^2.0.3",
@@ -2433,7 +2393,6 @@
"version": "5.2.14",
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
"integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
- "dev": true,
"dependencies": {
"css-declaration-sorter": "^6.3.1",
"cssnano-utils": "^3.1.0",
@@ -2476,7 +2435,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
"integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
- "dev": true,
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2488,7 +2446,6 @@
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
"integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
- "dev": true,
"engines": {
"node": ">= 10.14.2"
}
@@ -2497,7 +2454,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "dev": true,
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
@@ -2511,7 +2467,6 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "dev": true,
"dependencies": {
"domelementtype": "^2.2.0"
},
@@ -2526,7 +2481,6 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "dev": true,
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
@@ -2540,7 +2494,6 @@
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
- "dev": true,
"engines": {
"node": ">=10"
}
@@ -2549,7 +2502,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-7.1.1.tgz",
"integrity": "sha512-xw/19VqHDkXALtBOJAnnrSU/AZDIQRXczAmJyp0lZv6SH2aBLzUTl96W1MVryJZ7okZ+djZS4Gj4KlZ0xP7deA==",
- "dev": true,
"dependencies": {
"dotenv-defaults": "^2.0.2"
},
@@ -2564,7 +2516,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "dev": true,
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
@@ -2573,7 +2524,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -2585,7 +2535,6 @@
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
"integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==",
- "dev": true,
"engines": {
"node": ">= 0.4.0"
}
@@ -2594,7 +2543,6 @@
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "dev": true,
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
@@ -2614,7 +2562,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
"integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
- "dev": true,
"dependencies": {
"duplexer": "^0.1.2"
},
@@ -2629,7 +2576,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -2638,7 +2584,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
"integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
- "dev": true,
"dependencies": {
"camel-case": "^4.1.2",
"clean-css": "^5.2.2",
@@ -2659,7 +2604,6 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
"integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
- "dev": true,
"dependencies": {
"@types/html-minifier-terser": "^6.0.0",
"html-minifier-terser": "^6.0.2",
@@ -2682,7 +2626,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
- "dev": true,
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -2701,7 +2644,6 @@
"version": "9.0.19",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
"integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==",
- "dev": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -2711,7 +2653,6 @@
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
"integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "dev": true,
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^26.6.2",
@@ -2726,7 +2667,6 @@
"version": "26.3.0",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
"integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true,
"engines": {
"node": ">= 10.14.2"
}
@@ -2735,7 +2675,6 @@
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz",
"integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==",
- "dev": true,
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0",
@@ -2756,7 +2695,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -2768,7 +2706,6 @@
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
- "dev": true,
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
@@ -2785,7 +2722,6 @@
"version": "8.4.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2809,7 +2745,6 @@
"version": "8.2.4",
"resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
"integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
- "dev": true,
"dependencies": {
"postcss-selector-parser": "^6.0.9",
"postcss-value-parser": "^4.2.0"
@@ -2822,7 +2757,6 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
"integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"caniuse-api": "^3.0.0",
@@ -2840,7 +2774,6 @@
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
"integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"postcss-value-parser": "^4.2.0"
@@ -2856,7 +2789,6 @@
"version": "9.1.4",
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.4.tgz",
"integrity": "sha512-4A7WEG3iIyKwfpxL5bkuSlHoHHGRTHl0212Z3uvpwJPyVfZJlkZAQNNgVC+oogrJgksDnfKyuuMbG6HafZPW8Q==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2884,7 +2816,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
"integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
- "dev": true,
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2896,7 +2827,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
"integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
- "dev": true,
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2908,7 +2838,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
"integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
- "dev": true,
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2920,7 +2849,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
"integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
- "dev": true,
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2932,7 +2860,6 @@
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
"integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0",
"stylehacks": "^5.1.1"
@@ -2948,7 +2875,6 @@
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
"integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"caniuse-api": "^3.0.0",
@@ -2966,7 +2892,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
"integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -2981,7 +2906,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
"integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
- "dev": true,
"dependencies": {
"colord": "^2.9.1",
"cssnano-utils": "^3.1.0",
@@ -2998,7 +2922,6 @@
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
"integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"cssnano-utils": "^3.1.0",
@@ -3015,7 +2938,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
"integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
- "dev": true,
"dependencies": {
"postcss-selector-parser": "^6.0.5"
},
@@ -3030,7 +2952,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
"integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
- "dev": true,
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -3042,7 +2963,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
"integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3057,7 +2977,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
"integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3072,7 +2991,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
"integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3087,7 +3005,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
"integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3102,7 +3019,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
"integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3117,7 +3033,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
"integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"postcss-value-parser": "^4.2.0"
@@ -3133,7 +3048,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
"integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
- "dev": true,
"dependencies": {
"normalize-url": "^6.0.1",
"postcss-value-parser": "^4.2.0"
@@ -3149,7 +3063,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
"integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3164,7 +3077,6 @@
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
"integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
- "dev": true,
"dependencies": {
"cssnano-utils": "^3.1.0",
"postcss-value-parser": "^4.2.0"
@@ -3180,7 +3092,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
"integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"caniuse-api": "^3.0.0"
@@ -3196,7 +3107,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
"integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3211,7 +3121,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
"integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.2.0",
"svgo": "^2.7.0"
@@ -3227,7 +3136,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
"integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
- "dev": true,
"dependencies": {
"postcss-selector-parser": "^6.0.5"
},
@@ -3242,7 +3150,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
"integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
- "dev": true,
"dependencies": {
"lodash": "^4.17.20",
"renderkid": "^3.0.0"
@@ -3252,7 +3159,6 @@
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
"integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==",
- "dev": true,
"dependencies": {
"@babel/code-frame": "^7.16.0",
"address": "^1.1.2",
@@ -3287,7 +3193,6 @@
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz",
"integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==",
- "dev": true,
"dependencies": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
@@ -3326,7 +3231,6 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
"integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
- "dev": true,
"engines": {
"node": ">= 12.13.0"
}
@@ -3335,7 +3239,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
- "dev": true,
"dependencies": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
@@ -3353,7 +3256,6 @@
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -3368,7 +3270,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -3377,7 +3278,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
"integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
- "dev": true,
"dependencies": {
"css-select": "^4.1.3",
"dom-converter": "^0.2.0",
@@ -3390,7 +3290,6 @@
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz",
"integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -3399,7 +3298,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -3408,7 +3306,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
"integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
- "dev": true,
"dependencies": {
"browserslist": "^4.21.4",
"postcss-selector-parser": "^6.0.4"
@@ -3424,7 +3321,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -3436,7 +3332,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -3445,7 +3340,6 @@
"version": "5.15.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
- "dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -3462,14 +3356,12 @@
"node_modules/@edx/frontend-build/node_modules/terser/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"node_modules/@edx/frontend-build/node_modules/webpack-merge": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
"integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
- "dev": true,
"dependencies": {
"clone-deep": "^4.0.1",
"wildcard": "^2.0.0"
@@ -3479,17 +3371,17 @@
}
},
"node_modules/@edx/frontend-enterprise-catalog-search": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-catalog-search/-/frontend-enterprise-catalog-search-3.1.5.tgz",
- "integrity": "sha512-CDthbmT5PGbY7NfQ7+VZifr3NJEvFjARfpZP3p1Yxthxjy3dYxQCozC3BaZJBlozeO3xYf6g3a8BfZ7HepHMYA==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-catalog-search/-/frontend-enterprise-catalog-search-4.2.0.tgz",
+ "integrity": "sha512-rg/kix5IcQZFNaV7v4qCyZQJYm7ilXyjBIbnszY9GaK75crAKcezoRYruS36TNeYLuUo2ri8NwrZ73OxFuInwg==",
"dependencies": {
- "@edx/frontend-enterprise-utils": "^2.2.0",
+ "@edx/frontend-enterprise-utils": "^3.2.0",
"classnames": "2.2.5",
"lodash.debounce": "4.0.8",
"prop-types": "15.7.2"
},
"peerDependencies": {
- "@edx/frontend-platform": "^1.9.6 || ^2.0.0",
+ "@edx/frontend-platform": "^4.0.1",
"@edx/paragon": "^19.15.0 || ^20.0.0",
"@fortawesome/free-solid-svg-icons": "^5.8.1",
"@fortawesome/react-fontawesome": "^0.1.4",
@@ -3499,46 +3391,15 @@
"react-router-dom": "^5.2.0"
}
},
- "node_modules/@edx/frontend-enterprise-catalog-search/node_modules/@edx/frontend-enterprise-utils": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-2.2.0.tgz",
- "integrity": "sha512-5cAFO0vk4RIbprdIqi6Fq1nJcZHC+1wH3APSj8R+yIrJW1oWY4z6lnqHjnbsmyFQdECs3WCMsA43bRTLPgdnOw==",
- "dependencies": {
- "@testing-library/react": "11.2.6",
- "history": "4.10.1"
- },
- "peerDependencies": {
- "@edx/frontend-platform": "^1.9.6 || ^2.0.0",
- "react": "^16.12.0",
- "react-dom": "^16.12.0",
- "react-router-dom": "^5.2.0"
- }
- },
- "node_modules/@edx/frontend-enterprise-catalog-search/node_modules/@testing-library/react": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz",
- "integrity": "sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==",
- "dependencies": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^7.28.1"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "react": "*",
- "react-dom": "*"
- }
- },
"node_modules/@edx/frontend-enterprise-catalog-search/node_modules/classnames": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
"integrity": "sha512-DTt3GhOUDKhh4ONwIJW4lmhyotQmV2LjNlGK/J2hkwUcqcbKkCLAdJPtxQnxnlc7SR3f1CEXCyMmc7WLUsWbNA=="
},
"node_modules/@edx/frontend-enterprise-hotjar": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-hotjar/-/frontend-enterprise-hotjar-1.2.0.tgz",
- "integrity": "sha512-VkoRaUcCePKIRn0j4ODR7aTu5iyKtNjssAfr7m7NHrr/d0YjpFPUtNMqxxjsOZ5rySOgpdb6ibQ9TpbyNJ5qlQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-hotjar/-/frontend-enterprise-hotjar-1.3.0.tgz",
+ "integrity": "sha512-50xawVceXsj7Olr5u2UnmBOnyOLj8WHRdr2Y9/vR21U/Kd3Qa/bj8eSx8p4HqSlFbVMcKwfgszcBZx76nmaldw==",
"peerDependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
@@ -3546,35 +3407,53 @@
}
},
"node_modules/@edx/frontend-enterprise-logistration": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-logistration/-/frontend-enterprise-logistration-2.1.0.tgz",
- "integrity": "sha512-AP6aUYnLsy+lH3Hz0OGpJtMOTx5+BAeVMdIFtXXo17HlcqR+TFNrmdNN6ZIIomgsSAFmBNknVyrrcLhD/wJktg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-logistration/-/frontend-enterprise-logistration-3.2.0.tgz",
+ "integrity": "sha512-iM7EceyAMGWnCQTVIzRhMxVHfSY/KdkiX7Whb1e7SbL/e7dB2MtP/MYiAfmk2qDGubzDcfdHVaxevQxdP6l5pg==",
"dependencies": {
- "@edx/frontend-enterprise-utils": "^2.1.0",
+ "@edx/frontend-enterprise-utils": "^3.2.0",
"prop-types": "15.7.2"
},
"peerDependencies": {
- "@edx/frontend-platform": "^1.9.6 || ^2.0.0",
+ "@edx/frontend-platform": "^4.0.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.2.0"
}
},
"node_modules/@edx/frontend-enterprise-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-2.1.0.tgz",
- "integrity": "sha512-lfGwpfOnNO7oe3p8YUXhjzA2kDYz0eYZk4eeY+SYib6oRXDE+nbZXd+IU9jhd/YyZnPAAAFRY3JYwNQ26Go+0Q==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-3.2.0.tgz",
+ "integrity": "sha512-3lXRu2QTiOaIF4wljNLjeoUSRFseDnMbw/+/RmK1dAplqvoi632fX9y0lMm0q8Nnr8FP9XyxxAoOYeDQwGIQ+w==",
"dependencies": {
"@testing-library/react": "11.2.6",
"history": "4.10.1"
},
"peerDependencies": {
- "@edx/frontend-platform": "^1.9.6 || ^2.0.0",
+ "@edx/frontend-platform": "^4.0.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.2.0"
}
},
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/@testing-library/dom": {
+ "version": "7.31.2",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
+ "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
+ "dependencies": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^4.2.0",
+ "aria-query": "^4.2.2",
+ "chalk": "^4.1.0",
+ "dom-accessibility-api": "^0.5.6",
+ "lz-string": "^1.4.4",
+ "pretty-format": "^26.6.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@edx/frontend-enterprise-utils/node_modules/@testing-library/react": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz",
@@ -3591,18 +3470,99 @@
"react-dom": "*"
}
},
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/@types/aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+ "dependencies": {
+ "@babel/runtime": "^7.10.2",
+ "@babel/runtime-corejs3": "^7.10.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@edx/frontend-platform": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.4.0.tgz",
- "integrity": "sha512-i5z6cg+A+OLHgpvqPzU2jd7VM2soHsOAb8Jk6t4ddAtN1gLDad/pFhty3+aBEPIsnTuGDmrtUl5Yl/hP8Muc+w==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.0.1.tgz",
+ "integrity": "sha512-79uo/iQJ7P1tIY0MHTQ874W/NrNkcDe4BFC/0AvKF4DNrkqq9AkUFKqQ3hvd8E9C1EK189KlcAmo6FFQ67IFXg==",
"dependencies": {
"@cospired/i18n-iso-languages": "2.2.0",
- "@formatjs/intl-pluralrules": "^4.3.3",
- "@formatjs/intl-relativetimeformat": "^10.0.1",
- "axios": "0.26.1",
- "axios-cache-adapter": "2.7.3",
+ "@formatjs/intl-pluralrules": "4.3.3",
+ "@formatjs/intl-relativetimeformat": "10.0.1",
+ "axios": "0.27.2",
+ "axios-cache-interceptor": "0.10.7",
"form-urlencoded": "4.1.4",
- "glob": "7.2.0",
+ "glob": "7.2.3",
"history": "4.10.1",
"i18n-iso-countries": "4.3.1",
"jwt-decode": "3.1.2",
@@ -3629,46 +3589,18 @@
"redux": "^4.0.4"
}
},
- "node_modules/@edx/frontend-platform/node_modules/axios": {
- "version": "0.26.1",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
- "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+ "node_modules/@edx/new-relic-source-map-webpack-plugin": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.0.2.tgz",
+ "integrity": "sha512-jwu9WjRtEbv0rvPHGCnhbQbbv6+DTADShj43NQVsuAyD823znjutgoHnlc+9HIOiYiIxjz/wIMcGVwjrTnMceQ==",
"dependencies": {
- "follow-redirects": "^1.14.8"
+ "@newrelic/publish-sourcemap": "^5.0.1"
}
},
- "node_modules/@edx/frontend-platform/node_modules/glob": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@edx/new-relic-source-map-webpack-plugin": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.0.2.tgz",
- "integrity": "sha512-jwu9WjRtEbv0rvPHGCnhbQbbv6+DTADShj43NQVsuAyD823znjutgoHnlc+9HIOiYiIxjz/wIMcGVwjrTnMceQ==",
- "dev": true,
- "dependencies": {
- "@newrelic/publish-sourcemap": "^5.0.1"
- }
- },
- "node_modules/@edx/paragon": {
- "version": "20.39.2",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.39.2.tgz",
- "integrity": "sha512-SvJskMG+hjRAoteR+dhjXIFAAgMk4IgnDA4gvBomxOk/D+zV+E3mebEoslX2Qx+krRLSwmfQLWhWYn4qlps/5w==",
+ "node_modules/@edx/paragon": {
+ "version": "20.39.2",
+ "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.39.2.tgz",
+ "integrity": "sha512-SvJskMG+hjRAoteR+dhjXIFAAgMk4IgnDA4gvBomxOk/D+zV+E3mebEoslX2Qx+krRLSwmfQLWhWYn4qlps/5w==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
@@ -3791,7 +3723,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@edx/typescript-config/-/typescript-config-1.0.1.tgz",
"integrity": "sha512-w0g3nIX9oEch8Rip8q8sb/nrurGEHA1BEjK/I1LAQwA44K4FPMWvyvabmZErrdTJ9sXcZL10aWD3bat1obV8Bg==",
- "dev": true,
"peerDependencies": {
"typescript": "^4.9.4"
}
@@ -4512,12 +4443,12 @@
}
},
"node_modules/@jest/expect-utils": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz",
- "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
+ "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
"dev": true,
"dependencies": {
- "jest-get-type": "^29.2.0"
+ "jest-get-type": "^29.4.3"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -4926,12 +4857,12 @@
}
},
"node_modules/@jest/schemas": {
- "version": "29.0.0",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz",
- "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==",
+ "version": "29.4.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
+ "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
"dev": true,
"dependencies": {
- "@sinclair/typebox": "^0.24.1"
+ "@sinclair/typebox": "^0.25.16"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -5260,15 +5191,6 @@
"integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
"optional": true
},
- "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
- "version": "5.1.1-v1",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
- "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
- "peer": true,
- "dependencies": {
- "eslint-scope": "5.1.1"
- }
- },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -5392,9 +5314,9 @@
}
},
"node_modules/@sinclair/typebox": {
- "version": "0.24.51",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
- "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==",
+ "version": "0.25.24",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
+ "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
"dev": true
},
"node_modules/@sinonjs/commons": {
@@ -5417,7 +5339,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz",
"integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -5463,7 +5384,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz",
"integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -5479,7 +5399,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz",
"integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -5495,7 +5414,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz",
"integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -5511,7 +5429,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz",
"integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -5527,7 +5444,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz",
"integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==",
- "dev": true,
"engines": {
"node": ">=12"
},
@@ -5543,7 +5459,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz",
"integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==",
- "dev": true,
"dependencies": {
"@svgr/babel-plugin-add-jsx-attribute": "^6.5.1",
"@svgr/babel-plugin-remove-jsx-attribute": "*",
@@ -5569,7 +5484,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz",
"integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==",
- "dev": true,
"dependencies": {
"@babel/core": "^7.19.6",
"@svgr/babel-preset": "^6.5.1",
@@ -5589,7 +5503,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz",
"integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==",
- "dev": true,
"dependencies": {
"@babel/types": "^7.20.0",
"entities": "^4.4.0"
@@ -5606,7 +5519,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz",
"integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==",
- "dev": true,
"dependencies": {
"@babel/core": "^7.19.6",
"@svgr/babel-preset": "^6.5.1",
@@ -5628,7 +5540,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz",
"integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==",
- "dev": true,
"dependencies": {
"cosmiconfig": "^7.0.1",
"deepmerge": "^4.2.2",
@@ -5649,7 +5560,6 @@
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz",
"integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==",
- "dev": true,
"dependencies": {
"@babel/core": "^7.19.6",
"@babel/plugin-transform-react-constant-elements": "^7.18.12",
@@ -5669,27 +5579,29 @@
}
},
"node_modules/@testing-library/dom": {
- "version": "7.31.2",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
- "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz",
+ "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==",
+ "dev": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
- "@types/aria-query": "^4.2.0",
- "aria-query": "^4.2.2",
+ "@types/aria-query": "^5.0.1",
+ "aria-query": "5.1.3",
"chalk": "^4.1.0",
- "dom-accessibility-api": "^0.5.6",
- "lz-string": "^1.4.4",
- "pretty-format": "^26.6.2"
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "pretty-format": "^27.0.2"
},
"engines": {
- "node": ">=10"
+ "node": ">=14"
}
},
"node_modules/@testing-library/dom/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5704,6 +5616,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5719,6 +5632,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5729,20 +5643,55 @@
"node_modules/@testing-library/dom/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"node_modules/@testing-library/dom/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
},
+ "node_modules/@testing-library/dom/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@testing-library/dom/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
"node_modules/@testing-library/dom/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5751,17 +5700,18 @@
}
},
"node_modules/@testing-library/jest-dom": {
- "version": "5.11.9",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz",
- "integrity": "sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ==",
+ "version": "5.16.5",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz",
+ "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==",
"dev": true,
"dependencies": {
+ "@adobe/css-tools": "^4.0.1",
"@babel/runtime": "^7.9.2",
"@types/testing-library__jest-dom": "^5.9.1",
- "aria-query": "^4.2.2",
+ "aria-query": "^5.0.0",
"chalk": "^3.0.0",
- "css": "^3.0.0",
"css.escape": "^1.5.1",
+ "dom-accessibility-api": "^0.5.6",
"lodash": "^4.17.15",
"redent": "^3.0.0"
},
@@ -5882,6 +5832,114 @@
}
}
},
+ "node_modules/@testing-library/react/node_modules/@testing-library/dom": {
+ "version": "7.31.2",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
+ "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^4.2.0",
+ "aria-query": "^4.2.2",
+ "chalk": "^4.1.0",
+ "dom-accessibility-api": "^0.5.6",
+ "lz-string": "^1.4.4",
+ "pretty-format": "^26.6.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/@types/aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==",
+ "dev": true
+ },
+ "node_modules/@testing-library/react/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.10.2",
+ "@babel/runtime-corejs3": "^7.10.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/@testing-library/react/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@testing-library/user-event": {
"version": "12.8.3",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz",
@@ -5915,9 +5973,10 @@
}
},
"node_modules/@types/aria-query": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz",
+ "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==",
+ "dev": true
},
"node_modules/@types/babel__core": {
"version": "7.1.20",
@@ -6122,9 +6181,9 @@
}
},
"node_modules/@types/jest": {
- "version": "29.2.3",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.3.tgz",
- "integrity": "sha512-6XwoEbmatfyoCjWRX7z0fKMmgYKe9+/HrviJ5k0X/tjJWHGAezZOfYaxqQKuzG/TvQyr+ktjm4jgbk0s4/oF2w==",
+ "version": "29.5.2",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.2.tgz",
+ "integrity": "sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==",
"dev": true,
"dependencies": {
"expect": "^29.0.0",
@@ -6144,12 +6203,12 @@
}
},
"node_modules/@types/jest/node_modules/pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+ "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.0.0",
+ "@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
"react-is": "^18.0.0"
},
@@ -6295,8 +6354,7 @@
"node_modules/@types/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
- "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
- "dev": true
+ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw=="
},
"node_modules/@types/serve-index": {
"version": "1.9.1",
@@ -6339,9 +6397,9 @@
"integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ=="
},
"node_modules/@types/testing-library__jest-dom": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz",
- "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==",
+ "version": "5.14.6",
+ "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz",
+ "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==",
"dev": true,
"dependencies": {
"@types/jest": "*"
@@ -6437,7 +6495,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz",
"integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==",
- "dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.59.9",
@@ -6471,7 +6528,6 @@
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
"integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -6486,7 +6542,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz",
"integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==",
- "dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.59.9",
"@typescript-eslint/types": "5.59.9",
@@ -6513,7 +6568,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz",
"integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==",
- "dev": true,
"dependencies": {
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/visitor-keys": "5.59.9"
@@ -6530,7 +6584,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz",
"integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==",
- "dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "5.59.9",
"@typescript-eslint/utils": "5.59.9",
@@ -6557,7 +6610,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz",
"integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==",
- "dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -6570,7 +6622,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz",
"integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==",
- "dev": true,
"dependencies": {
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/visitor-keys": "5.59.9",
@@ -6597,7 +6648,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -6606,7 +6656,6 @@
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "dev": true,
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
@@ -6626,7 +6675,6 @@
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
"integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -6641,7 +6689,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -6650,7 +6697,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz",
"integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==",
- "dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9",
@@ -6676,7 +6722,6 @@
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
"integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -6691,7 +6736,6 @@
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz",
"integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==",
- "dev": true,
"dependencies": {
"@typescript-eslint/types": "5.59.9",
"eslint-visitor-keys": "^3.3.0"
@@ -7197,15 +7241,11 @@
}
},
"node_modules/aria-query": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+ "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
"dependencies": {
- "@babel/runtime": "^7.10.2",
- "@babel/runtime-corejs3": "^7.10.2"
- },
- "engines": {
- "node": ">=6.0"
+ "deep-equal": "^2.0.5"
}
},
"node_modules/arr-diff": {
@@ -7495,24 +7535,12 @@
}
},
"node_modules/axios": {
- "version": "0.21.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
- "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
- "peer": true,
- "dependencies": {
- "follow-redirects": "^1.14.0"
- }
- },
- "node_modules/axios-cache-adapter": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/axios-cache-adapter/-/axios-cache-adapter-2.7.3.tgz",
- "integrity": "sha512-A+ZKJ9lhpjthOEp4Z3QR/a9xC4du1ALaAsejgRGrH9ef6kSDxdFrhRpulqsh9khsEnwXxGfgpUuDp1YXMNMEiQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
- "cache-control-esm": "1.0.0",
- "md5": "^2.2.1"
- },
- "peerDependencies": {
- "axios": "~0.21.1"
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
}
},
"node_modules/axios-cache-interceptor": {
@@ -7540,6 +7568,19 @@
"axios": ">= 0.9.0"
}
},
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
@@ -8218,7 +8259,6 @@
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
- "devOptional": true,
"dependencies": {
"fast-json-stable-stringify": "2.x"
},
@@ -8297,11 +8337,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/cache-control-esm": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/cache-control-esm/-/cache-control-esm-1.0.0.tgz",
- "integrity": "sha512-Fa3UV4+eIk4EOih8FTV6EEsVKO0W5XWtNs6FC3InTfVz+EjurjPfDXY5wZDo/lxjDxg5RjNcurLyxEJBcEUx9g=="
- },
"node_modules/cache-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.4.tgz",
@@ -8444,14 +8479,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/charenc": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
- "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
- "engines": {
- "node": "*"
- }
- },
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -8774,11 +8801,6 @@
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
},
- "node_modules/colorette": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
- "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="
- },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -8960,9 +8982,9 @@
}
},
"node_modules/core-js-pure": {
- "version": "3.26.1",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz",
- "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==",
+ "version": "3.31.0",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.31.0.tgz",
+ "integrity": "sha512-/AnE9Y4OsJZicCzIe97JP5XoPKQJfTuEG43aEVLFJGOJpyqELod+pE6LEl63DfG1Mp8wX97LDaDpy1GmLEUxlg==",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
@@ -8978,7 +9000,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "dev": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -9003,29 +9024,10 @@
"node": ">= 8"
}
},
- "node_modules/crypt": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
- "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/css": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
- "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
- "dev": true,
- "dependencies": {
- "inherits": "^2.0.4",
- "source-map": "^0.6.1",
- "source-map-resolve": "^0.6.0"
- }
- },
- "node_modules/css-declaration-sorter": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
- "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==",
+ "node_modules/css-declaration-sorter": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
+ "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==",
"engines": {
"node": "^10 || ^12 || >=14"
},
@@ -9061,30 +9063,6 @@
"webpack": "^4.27.0 || ^5.0.0"
}
},
- "node_modules/css-loader/node_modules/postcss": {
- "version": "8.4.19",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
- "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
"node_modules/css-loader/node_modules/semver": {
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
@@ -9109,6 +9087,7 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dev": true,
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
@@ -9124,7 +9103,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
- "dev": true,
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
@@ -9137,7 +9115,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -9159,15 +9136,6 @@
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
"dev": true
},
- "node_modules/css/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -9189,7 +9157,6 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
"integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
- "dev": true,
"dependencies": {
"css-tree": "^1.1.2"
},
@@ -9348,9 +9315,9 @@
"integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA=="
},
"node_modules/decode-uri-component": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
- "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
@@ -9677,9 +9644,9 @@
"integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA=="
},
"node_modules/diff-sequences": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz",
- "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==",
+ "version": "29.4.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
+ "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
"dev": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -10109,34 +10076,44 @@
}
},
"node_modules/es-abstract": {
- "version": "1.20.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
- "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+ "version": "1.21.2",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
+ "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
"dependencies": {
+ "array-buffer-byte-length": "^1.0.0",
+ "available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
+ "es-set-tostringtag": "^2.0.1",
"es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
"function.prototype.name": "^1.1.5",
- "get-intrinsic": "^1.1.3",
+ "get-intrinsic": "^1.2.0",
"get-symbol-description": "^1.0.0",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
"has": "^1.0.3",
"has-property-descriptors": "^1.0.0",
+ "has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
- "internal-slot": "^1.0.3",
+ "internal-slot": "^1.0.5",
+ "is-array-buffer": "^3.0.2",
"is-callable": "^1.2.7",
"is-negative-zero": "^2.0.2",
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
"is-string": "^1.0.7",
+ "is-typed-array": "^1.1.10",
"is-weakref": "^1.0.2",
- "object-inspect": "^1.12.2",
+ "object-inspect": "^1.12.3",
"object-keys": "^1.1.1",
"object.assign": "^4.1.4",
"regexp.prototype.flags": "^1.4.3",
"safe-regex-test": "^1.0.0",
- "string.prototype.trimend": "^1.0.5",
- "string.prototype.trimstart": "^1.0.5",
- "unbox-primitive": "^1.0.2"
+ "string.prototype.trim": "^1.2.7",
+ "string.prototype.trimend": "^1.0.6",
+ "string.prototype.trimstart": "^1.0.6",
+ "typed-array-length": "^1.0.4",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.9"
},
"engines": {
"node": ">= 0.4"
@@ -10179,6 +10156,19 @@
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz",
"integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg=="
},
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+ "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3",
+ "has": "^1.0.3",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es-shim-unscopables": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
@@ -10404,7 +10394,6 @@
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz",
"integrity": "sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==",
- "dev": true,
"dependencies": {
"eslint-config-airbnb-base": "^15.0.0"
},
@@ -10533,14 +10522,6 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
- "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
- "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
- "dependencies": {
- "deep-equal": "^2.0.5"
- }
- },
"node_modules/eslint-plugin-react": {
"version": "7.32.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz",
@@ -11104,28 +11085,28 @@
}
},
"node_modules/expect": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz",
- "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
+ "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
"dev": true,
"dependencies": {
- "@jest/expect-utils": "^29.3.1",
- "jest-get-type": "^29.2.0",
- "jest-matcher-utils": "^29.3.1",
- "jest-message-util": "^29.3.1",
- "jest-util": "^29.3.1"
+ "@jest/expect-utils": "^29.5.0",
+ "jest-get-type": "^29.4.3",
+ "jest-matcher-utils": "^29.5.0",
+ "jest-message-util": "^29.5.0",
+ "jest-util": "^29.5.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/expect/node_modules/@jest/types": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz",
- "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
+ "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.0.0",
+ "@jest/schemas": "^29.4.3",
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
@@ -11137,9 +11118,9 @@
}
},
"node_modules/expect/node_modules/@types/yargs": {
- "version": "17.0.13",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz",
- "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==",
+ "version": "17.0.24",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
+ "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
"dev": true,
"dependencies": {
"@types/yargs-parser": "*"
@@ -11177,10 +11158,16 @@
}
},
"node_modules/expect/node_modules/ci-info": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.1.tgz",
- "integrity": "sha512-up5ggbaDqOqJ4UqLKZ2naVkyqSJQgJi5lwD6b6mM748ysrghDBX0bx/qJTUHzw7zu6Mq4gycviSF5hJnwceD8w==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
"engines": {
"node": ">=8"
}
@@ -11213,12 +11200,12 @@
}
},
"node_modules/expect/node_modules/jest-util": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz",
- "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
+ "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
"dev": true,
"dependencies": {
- "@jest/types": "^29.3.1",
+ "@jest/types": "^29.5.0",
"@types/node": "*",
"chalk": "^4.0.0",
"ci-info": "^3.2.0",
@@ -12174,6 +12161,20 @@
"node": ">=4"
}
},
+ "node_modules/globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dependencies": {
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/globby": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
@@ -12218,12 +12219,6 @@
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "peer": true
- },
"node_modules/growly": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
@@ -12545,9 +12540,9 @@
}
},
"node_modules/html-webpack-plugin/node_modules/json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"peer": true,
"dependencies": {
"minimist": "^1.2.0"
@@ -13719,22 +13714,6 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
- "node_modules/isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
- "dependencies": {
- "isarray": "1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/isobject/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- },
"node_modules/istanbul-lib-coverage": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
@@ -14291,15 +14270,15 @@
}
},
"node_modules/jest-diff": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz",
- "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
+ "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0",
- "diff-sequences": "^29.3.1",
- "jest-get-type": "^29.2.0",
- "pretty-format": "^29.3.1"
+ "diff-sequences": "^29.4.3",
+ "jest-get-type": "^29.4.3",
+ "pretty-format": "^29.5.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -14364,12 +14343,12 @@
}
},
"node_modules/jest-diff/node_modules/pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+ "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.0.0",
+ "@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
"react-is": "^18.0.0"
},
@@ -14539,9 +14518,9 @@
}
},
"node_modules/jest-get-type": {
- "version": "29.2.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz",
- "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==",
+ "version": "29.4.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
+ "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
"dev": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -14782,15 +14761,15 @@
}
},
"node_modules/jest-matcher-utils": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz",
- "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
+ "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0",
- "jest-diff": "^29.3.1",
- "jest-get-type": "^29.2.0",
- "pretty-format": "^29.3.1"
+ "jest-diff": "^29.5.0",
+ "jest-get-type": "^29.4.3",
+ "pretty-format": "^29.5.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -14855,12 +14834,12 @@
}
},
"node_modules/jest-matcher-utils/node_modules/pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+ "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.0.0",
+ "@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
"react-is": "^18.0.0"
},
@@ -14899,18 +14878,18 @@
}
},
"node_modules/jest-message-util": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz",
- "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
+ "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.12.13",
- "@jest/types": "^29.3.1",
+ "@jest/types": "^29.5.0",
"@types/stack-utils": "^2.0.0",
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
"micromatch": "^4.0.4",
- "pretty-format": "^29.3.1",
+ "pretty-format": "^29.5.0",
"slash": "^3.0.0",
"stack-utils": "^2.0.3"
},
@@ -14919,12 +14898,12 @@
}
},
"node_modules/jest-message-util/node_modules/@jest/types": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz",
- "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
+ "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.0.0",
+ "@jest/schemas": "^29.4.3",
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
@@ -14936,9 +14915,9 @@
}
},
"node_modules/jest-message-util/node_modules/@types/yargs": {
- "version": "17.0.13",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz",
- "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==",
+ "version": "17.0.24",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
+ "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
"dev": true,
"dependencies": {
"@types/yargs-parser": "*"
@@ -15003,12 +14982,12 @@
}
},
"node_modules/jest-message-util/node_modules/pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
+ "version": "29.5.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+ "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
"dependencies": {
- "@jest/schemas": "^29.0.0",
+ "@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
"react-is": "^18.0.0"
},
@@ -16063,19 +16042,10 @@
"node": ">=8"
}
},
- "node_modules/jiti": {
- "version": "1.18.2",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
- "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
- "peer": true,
- "bin": {
- "jiti": "bin/jiti.js"
- }
- },
"node_modules/jquery": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz",
- "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==",
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz",
+ "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==",
"peer": true
},
"node_modules/js-sdsl": {
@@ -16314,20 +16284,6 @@
"node": ">=10"
}
},
- "node_modules/line-column": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz",
- "integrity": "sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==",
- "dependencies": {
- "isarray": "^1.0.0",
- "isobject": "^2.0.0"
- }
- },
- "node_modules/line-column/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -16478,9 +16434,9 @@
}
},
"node_modules/lz-string": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
- "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"bin": {
"lz-string": "bin/bin.js"
}
@@ -16522,8 +16478,7 @@
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "devOptional": true
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"node_modules/makeerror": {
"version": "1.0.12",
@@ -16560,21 +16515,6 @@
"css-mediaquery": "^0.1.2"
}
},
- "node_modules/md5": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
- "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
- "dependencies": {
- "charenc": "0.0.2",
- "crypt": "0.0.2",
- "is-buffer": "~1.1.6"
- }
- },
- "node_modules/md5/node_modules/is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
"node_modules/mdast-util-definitions": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz",
@@ -16634,8 +16574,7 @@
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
- "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
- "dev": true
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
},
"node_modules/mdurl": {
"version": "1.0.1",
@@ -16819,9 +16758,9 @@
}
},
"node_modules/mini-css-extract-plugin/node_modules/json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"peer": true,
"dependencies": {
"minimist": "^1.2.0"
@@ -17004,8 +16943,7 @@
"node_modules/natural-compare-lite": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
- "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
- "dev": true
+ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="
},
"node_modules/nearley": {
"version": "2.20.1",
@@ -17348,9 +17286,9 @@
"integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA=="
},
"node_modules/object-inspect": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
- "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -17444,15 +17382,16 @@
}
},
"node_modules/object.getownpropertydescriptors": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz",
- "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==",
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz",
+ "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==",
"peer": true,
"dependencies": {
"array.prototype.reduce": "^1.0.5",
"call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.21.2",
+ "safe-array-concat": "^1.0.0"
},
"engines": {
"node": ">= 0.8"
@@ -18031,28 +17970,36 @@
}
},
"node_modules/postcss": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.1.0.tgz",
- "integrity": "sha512-d3RppIo1DI66oHxA1vdckr5qciQbMIrHvyzuvp2cLJHOLwJHg7X9ncrfw2Ri6Sgiwv/GoXtOwEHJ9E9VSRxXWQ==",
+ "version": "8.4.24",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
+ "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"dependencies": {
- "colorette": "^1.2.1",
- "line-column": "^1.0.2",
- "nanoid": "^3.1.12",
- "source-map": "^0.6.1"
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
- },
- "funding": {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
}
},
"node_modules/postcss-loader": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
"integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==",
- "dev": true,
"dependencies": {
"cosmiconfig": "^7.0.0",
"klona": "^2.0.5",
@@ -18074,7 +18021,6 @@
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -18144,7 +18090,6 @@
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-3.7.2.tgz",
"integrity": "sha512-GurrGedCKvOTe1QrifI+XpDKXA3bJky1v8KiOa/TYYHs1bfJOxI53GIRvVSqLJLly7e1WcNMz8KMESTN01vbZQ==",
- "dev": true,
"dependencies": {
"rtlcss": "^3.5.0"
},
@@ -18172,14 +18117,6 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
- "node_modules/postcss/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/prebuild-install": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
@@ -18449,33 +18386,6 @@
"node": ">=10"
}
},
- "node_modules/purgecss/node_modules/postcss": {
- "version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -20096,29 +20006,6 @@
"node": ">=12"
}
},
- "node_modules/resolve-url-loader/node_modules/postcss": {
- "version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
"node_modules/resolve-url-loader/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -20185,7 +20072,6 @@
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz",
"integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==",
- "dev": true,
"dependencies": {
"find-up": "^5.0.0",
"picocolors": "^1.0.0",
@@ -20196,56 +20082,52 @@
"rtlcss": "bin/rtlcss.js"
}
},
- "node_modules/rtlcss/node_modules/postcss": {
- "version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
- "dev": true,
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"funding": [
{
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
},
{
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
},
{
- "type": "github",
- "url": "https://github.com/sponsors/ai"
+ "type": "consulting",
+ "url": "https://feross.org/support"
}
],
"dependencies": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
+ "queue-microtask": "^1.2.2"
}
},
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
+ "node_modules/safe-array-concat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz",
+ "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==",
+ "peer": true,
"dependencies": {
- "queue-microtask": "^1.2.2"
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.0",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-array-concat/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "peer": true
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -20500,34 +20382,10 @@
"node": ">=0.10.0"
}
},
- "node_modules/sanitize-html/node_modules/postcss": {
- "version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
"node_modules/sass": {
"version": "1.62.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.62.0.tgz",
"integrity": "sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==",
- "dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -20544,7 +20402,6 @@
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
"integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==",
- "dev": true,
"dependencies": {
"klona": "^2.0.4",
"neo-async": "^2.6.2"
@@ -21030,16 +20887,6 @@
"node": ">=6"
}
},
- "node_modules/snake-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
- "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
- "peer": true,
- "dependencies": {
- "dot-case": "^3.0.4",
- "tslib": "^2.0.3"
- }
- },
"node_modules/snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -21325,17 +21172,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/source-map-resolve": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
- "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
- "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
- "dev": true,
- "dependencies": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0"
- }
- },
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@@ -21465,8 +21301,7 @@
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
- "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
- "dev": true
+ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility"
},
"node_modules/stack-utils": {
"version": "2.0.6",
@@ -21678,7 +21513,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
"integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
- "dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -21776,7 +21610,6 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.2.tgz",
"integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==",
- "dev": true,
"engines": {
"node": ">= 12.13.0"
},
@@ -21916,7 +21749,6 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
"integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
- "dev": true,
"dependencies": {
"@trysound/sax": "0.2.0",
"commander": "^7.2.0",
@@ -21937,7 +21769,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "dev": true,
"engines": {
"node": ">= 10"
}
@@ -21946,7 +21777,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
"integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "dev": true,
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.0.1",
@@ -21962,7 +21792,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "dev": true,
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
@@ -21976,7 +21805,6 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "dev": true,
"dependencies": {
"domelementtype": "^2.2.0"
},
@@ -21991,7 +21819,6 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "dev": true,
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
@@ -22005,7 +21832,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "dev": true,
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
@@ -22403,7 +22229,6 @@
"version": "26.5.6",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
"integrity": "sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==",
- "devOptional": true,
"dependencies": {
"bs-logger": "0.x",
"buffer-from": "1.x",
@@ -22431,7 +22256,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "devOptional": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
@@ -22443,7 +22267,6 @@
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "devOptional": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -22493,7 +22316,6 @@
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
- "dev": true,
"dependencies": {
"tslib": "^1.8.1"
},
@@ -22507,8 +22329,7 @@
"node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
@@ -22563,6 +22384,19 @@
"node": ">= 0.6"
}
},
+ "node_modules/typed-array-length": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+ "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "is-typed-array": "^1.1.9"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
@@ -23508,7 +23342,6 @@
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz",
"integrity": "sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==",
- "devOptional": true,
"dependencies": {
"@types/bonjour": "^3.5.9",
"@types/connect-history-api-fallback": "^1.3.5",
@@ -23567,7 +23400,6 @@
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "devOptional": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -23583,7 +23415,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "devOptional": true,
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
@@ -23594,20 +23425,17 @@
"node_modules/webpack-dev-server/node_modules/colorette": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
- "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
- "devOptional": true
+ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
},
"node_modules/webpack-dev-server/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "devOptional": true
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/webpack-dev-server/node_modules/open": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
"integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
- "devOptional": true,
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
@@ -23624,7 +23452,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "devOptional": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -23639,7 +23466,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
"integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
- "devOptional": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.8.0",
@@ -23658,7 +23484,6 @@
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
- "devOptional": true,
"engines": {
"node": ">=10.0.0"
},
@@ -24023,155 +23848,6 @@
"@edx/frontend-platform": "<4.1.0"
}
},
- "packages/frontend-platform-shim/node_modules/@babel/cli": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.21.5.tgz",
- "integrity": "sha512-TOKytQ9uQW9c4np8F+P7ZfPINy5Kv+pizDIUwSVH8X5zHgYHV4AA8HE5LA450xXeu4jEfmUckTYvv1I4S26M/g==",
- "peer": true,
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.17",
- "commander": "^4.0.1",
- "convert-source-map": "^1.1.0",
- "fs-readdir-recursive": "^1.1.0",
- "glob": "^7.2.0",
- "make-dir": "^2.1.0",
- "slash": "^2.0.0"
- },
- "bin": {
- "babel": "bin/babel.js",
- "babel-external-helpers": "bin/babel-external-helpers.js"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "optionalDependencies": {
- "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
- "chokidar": "^3.4.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@babel/core": {
- "version": "7.21.8",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz",
- "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==",
- "peer": true,
- "dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.21.4",
- "@babel/generator": "^7.21.5",
- "@babel/helper-compilation-targets": "^7.21.5",
- "@babel/helper-module-transforms": "^7.21.5",
- "@babel/helpers": "^7.21.5",
- "@babel/parser": "^7.21.8",
- "@babel/template": "^7.20.7",
- "@babel/traverse": "^7.21.5",
- "@babel/types": "^7.21.5",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.2",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "packages/frontend-platform-shim/node_modules/@babel/preset-env": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz",
- "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==",
- "peer": true,
- "dependencies": {
- "@babel/compat-data": "^7.21.5",
- "@babel/helper-compilation-targets": "^7.21.5",
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-validator-option": "^7.21.0",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7",
- "@babel/plugin-proposal-async-generator-functions": "^7.20.7",
- "@babel/plugin-proposal-class-properties": "^7.18.6",
- "@babel/plugin-proposal-class-static-block": "^7.21.0",
- "@babel/plugin-proposal-dynamic-import": "^7.18.6",
- "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
- "@babel/plugin-proposal-json-strings": "^7.18.6",
- "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
- "@babel/plugin-proposal-numeric-separator": "^7.18.6",
- "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
- "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
- "@babel/plugin-proposal-optional-chaining": "^7.21.0",
- "@babel/plugin-proposal-private-methods": "^7.18.6",
- "@babel/plugin-proposal-private-property-in-object": "^7.21.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-class-properties": "^7.12.13",
- "@babel/plugin-syntax-class-static-block": "^7.14.5",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
- "@babel/plugin-syntax-import-assertions": "^7.20.0",
- "@babel/plugin-syntax-import-meta": "^7.10.4",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.10.4",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
- "@babel/plugin-syntax-top-level-await": "^7.14.5",
- "@babel/plugin-transform-arrow-functions": "^7.21.5",
- "@babel/plugin-transform-async-to-generator": "^7.20.7",
- "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
- "@babel/plugin-transform-block-scoping": "^7.21.0",
- "@babel/plugin-transform-classes": "^7.21.0",
- "@babel/plugin-transform-computed-properties": "^7.21.5",
- "@babel/plugin-transform-destructuring": "^7.21.3",
- "@babel/plugin-transform-dotall-regex": "^7.18.6",
- "@babel/plugin-transform-duplicate-keys": "^7.18.9",
- "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
- "@babel/plugin-transform-for-of": "^7.21.5",
- "@babel/plugin-transform-function-name": "^7.18.9",
- "@babel/plugin-transform-literals": "^7.18.9",
- "@babel/plugin-transform-member-expression-literals": "^7.18.6",
- "@babel/plugin-transform-modules-amd": "^7.20.11",
- "@babel/plugin-transform-modules-commonjs": "^7.21.5",
- "@babel/plugin-transform-modules-systemjs": "^7.20.11",
- "@babel/plugin-transform-modules-umd": "^7.18.6",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5",
- "@babel/plugin-transform-new-target": "^7.18.6",
- "@babel/plugin-transform-object-super": "^7.18.6",
- "@babel/plugin-transform-parameters": "^7.21.3",
- "@babel/plugin-transform-property-literals": "^7.18.6",
- "@babel/plugin-transform-regenerator": "^7.21.5",
- "@babel/plugin-transform-reserved-words": "^7.18.6",
- "@babel/plugin-transform-shorthand-properties": "^7.18.6",
- "@babel/plugin-transform-spread": "^7.20.7",
- "@babel/plugin-transform-sticky-regex": "^7.18.6",
- "@babel/plugin-transform-template-literals": "^7.18.9",
- "@babel/plugin-transform-typeof-symbol": "^7.18.9",
- "@babel/plugin-transform-unicode-escapes": "^7.21.5",
- "@babel/plugin-transform-unicode-regex": "^7.18.6",
- "@babel/preset-modules": "^0.1.5",
- "@babel/types": "^7.21.5",
- "babel-plugin-polyfill-corejs2": "^0.3.3",
- "babel-plugin-polyfill-corejs3": "^0.6.0",
- "babel-plugin-polyfill-regenerator": "^0.4.1",
- "core-js-compat": "^3.25.1",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"packages/frontend-platform-shim/node_modules/@cospired/i18n-iso-languages": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@cospired/i18n-iso-languages/-/i18n-iso-languages-4.1.0.tgz",
@@ -24180,92 +23856,10 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
- "packages/frontend-platform-shim/node_modules/@edx/eslint-config": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-3.2.0.tgz",
- "integrity": "sha512-X2o34xr3KqmQSV/vJVv6k4FxUKYwbBATHTtTHLTYQvM9PVoM3WbKQP9tl6Z057pRErKzshJcks+4ENzDyhr11Q==",
- "peer": true,
- "peerDependencies": {
- "eslint": "^6.8.0 || ^7.0.0 || ^8.0.0",
- "eslint-config-airbnb": "^18.0.1 || ^19.0.0",
- "eslint-plugin-import": "^2.20.0",
- "eslint-plugin-jsx-a11y": "^6.2.3",
- "eslint-plugin-react": "^7.18.0",
- "eslint-plugin-react-hooks": "^1.7.0 || ^4.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@edx/frontend-build": {
- "version": "12.8.40",
- "resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.8.40.tgz",
- "integrity": "sha512-11U9VCHLuozbot93VycbSVeCwvcXtCp235154wgExSR5tPoAhqh/OJ1MSlZXUIzVv5xWMxZI/DCPUdZ76+96Nw==",
- "peer": true,
- "dependencies": {
- "@babel/cli": "7.21.5",
- "@babel/core": "7.21.8",
- "@babel/eslint-parser": "7.21.8",
- "@babel/plugin-proposal-class-properties": "7.18.6",
- "@babel/plugin-proposal-object-rest-spread": "7.20.7",
- "@babel/plugin-syntax-dynamic-import": "7.8.3",
- "@babel/preset-env": "7.21.5",
- "@babel/preset-react": "7.18.6",
- "@edx/eslint-config": "3.2.0",
- "@edx/new-relic-source-map-webpack-plugin": "2.1.0",
- "@fullhuman/postcss-purgecss": "^5.0.0",
- "@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
- "@svgr/webpack": "8.0.1",
- "autoprefixer": "10.4.14",
- "babel-jest": "26.6.3",
- "babel-loader": "9.1.2",
- "babel-plugin-react-intl": "7.9.4",
- "babel-plugin-transform-imports": "2.0.0",
- "babel-polyfill": "6.26.0",
- "clean-webpack-plugin": "4.0.0",
- "css-loader": "5.2.7",
- "cssnano": "6.0.1",
- "dotenv": "8.6.0",
- "dotenv-webpack": "8.0.1",
- "eslint": "8.41.0",
- "eslint-config-airbnb": "19.0.4",
- "eslint-plugin-import": "2.27.5",
- "eslint-plugin-jsx-a11y": "6.7.1",
- "eslint-plugin-react": "7.32.2",
- "eslint-plugin-react-hooks": "4.6.0",
- "file-loader": "6.2.0",
- "html-webpack-plugin": "5.5.1",
- "identity-obj-proxy": "3.0.0",
- "image-minimizer-webpack-plugin": "3.8.2",
- "jest": "26.6.3",
- "mini-css-extract-plugin": "1.6.2",
- "postcss": "8.4.24",
- "postcss-custom-media": "^9.1.2",
- "postcss-loader": "7.3.2",
- "postcss-rtlcss": "4.0.6",
- "react-dev-utils": "12.0.1",
- "react-refresh": "0.14.0",
- "resolve-url-loader": "5.0.0",
- "sass": "1.62.1",
- "sass-loader": "13.3.1",
- "sharp": "^0.32.0",
- "source-map-loader": "^4.0.1",
- "style-loader": "3.3.3",
- "url-loader": "4.1.1",
- "webpack": "5.84.1",
- "webpack-bundle-analyzer": "4.8.0",
- "webpack-cli": "5.1.3",
- "webpack-dev-server": "4.15.0",
- "webpack-merge": "5.9.0"
- },
- "bin": {
- "fedx-scripts": "bin/fedx-scripts.js"
- },
- "peerDependencies": {
- "react": "^16.9.0 || ^17.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@edx/frontend-platform": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.5.1.tgz",
- "integrity": "sha512-4nZGk+Wl+z4D5gE01xzLwl+vkxPzupCnEeZJ4uwoU6A7P8oID8ZdoR5RNtKBuhsYnouEWcgzHtFDtWK9Py9NuA==",
+ "packages/frontend-platform-shim/node_modules/@edx/frontend-platform": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.5.1.tgz",
+ "integrity": "sha512-4nZGk+Wl+z4D5gE01xzLwl+vkxPzupCnEeZJ4uwoU6A7P8oID8ZdoR5RNtKBuhsYnouEWcgzHtFDtWK9Py9NuA==",
"dependencies": {
"@cospired/i18n-iso-languages": "4.1.0",
"@formatjs/intl-pluralrules": "4.3.3",
@@ -24301,21565 +23895,6 @@
"react-router-dom": "^5.0.1",
"redux": "^4.0.4"
}
- },
- "packages/frontend-platform-shim/node_modules/@edx/new-relic-source-map-webpack-plugin": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-2.1.0.tgz",
- "integrity": "sha512-OrlvtdsPcWuOm6NBWfUxFE06qdPiu2bf9nU4I9t8Lu7WW6NsosAB5hxm5U+MBMZP2AuVl3FAt0k0lZsu3+ri8Q==",
- "peer": true,
- "dependencies": {
- "@newrelic/publish-sourcemap": "^5.0.1"
- }
- },
- "packages/frontend-platform-shim/node_modules/@eslint/js": {
- "version": "8.41.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
- "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
- "peer": true,
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-plugin-add-jsx-attribute": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
- "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
- "peer": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
- "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
- "peer": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-plugin-svg-dynamic-title": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
- "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
- "peer": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-plugin-svg-em-dimensions": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
- "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
- "peer": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-plugin-transform-react-native-svg": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz",
- "integrity": "sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw==",
- "peer": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-plugin-transform-svg-component": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
- "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
- "peer": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/babel-preset": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.0.0.tgz",
- "integrity": "sha512-KLcjiZychInVrhs86OvcYPLTFu9L5XV2vj0XAaE1HwE3J3jLmIzRY8ttdeAg/iFyp8nhavJpafpDZTt+1LIpkQ==",
- "peer": true,
- "dependencies": {
- "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
- "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
- "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
- "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
- "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
- "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
- "@svgr/babel-plugin-transform-react-native-svg": "8.0.0",
- "@svgr/babel-plugin-transform-svg-component": "8.0.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/core": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.0.0.tgz",
- "integrity": "sha512-aJKtc+Pie/rFYsVH/unSkDaZGvEeylNv/s2cP+ta9/rYWxRVvoV/S4Qw65Kmrtah4CBK5PM6ISH9qUH7IJQCng==",
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.21.3",
- "@svgr/babel-preset": "8.0.0",
- "camelcase": "^6.2.0",
- "cosmiconfig": "^8.1.3",
- "snake-case": "^3.0.4"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/hast-util-to-babel-ast": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
- "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
- "peer": true,
- "dependencies": {
- "@babel/types": "^7.21.3",
- "entities": "^4.4.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/plugin-jsx": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.0.1.tgz",
- "integrity": "sha512-bfCFb+4ZsM3UuKP2t7KmDwn6YV8qVn9HIQJmau6xeQb/iV65Rpi7NBNBWA2hcCd4GKoCqG8hpaaDk5FDR0eH+g==",
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.21.3",
- "@svgr/babel-preset": "8.0.0",
- "@svgr/hast-util-to-babel-ast": "8.0.0",
- "svg-parser": "^2.0.4"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@svgr/core": "*"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/plugin-svgo": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.0.1.tgz",
- "integrity": "sha512-29OJ1QmJgnohQHDAgAuY2h21xWD6TZiXji+hnx+W635RiXTAlHTbjrZDktfqzkN0bOeQEtNe+xgq73/XeWFfSg==",
- "peer": true,
- "dependencies": {
- "cosmiconfig": "^8.1.3",
- "deepmerge": "^4.3.1",
- "svgo": "^3.0.2"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- },
- "peerDependencies": {
- "@svgr/core": "*"
- }
- },
- "packages/frontend-platform-shim/node_modules/@svgr/webpack": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.0.1.tgz",
- "integrity": "sha512-zSoeKcbCmfMXjA11uDuCJb+1LWNb3vy6Qw/VHj0Nfcl3UuqwuoZWknHsBIhCWvi4wU9vPui3aq054qjVyZqY4A==",
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.21.3",
- "@babel/plugin-transform-react-constant-elements": "^7.21.3",
- "@babel/preset-env": "^7.20.2",
- "@babel/preset-react": "^7.18.6",
- "@babel/preset-typescript": "^7.21.0",
- "@svgr/core": "8.0.0",
- "@svgr/plugin-jsx": "8.0.1",
- "@svgr/plugin-svgo": "8.0.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/gregberge"
- }
- },
- "packages/frontend-platform-shim/node_modules/@types/html-minifier-terser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
- "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
- "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/helper-api-error": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
- "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/helper-buffer": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
- "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/helper-numbers": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
- "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/floating-point-hex-parser": "1.11.6",
- "@webassemblyjs/helper-api-error": "1.11.6",
- "@xtuc/long": "4.2.2"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
- "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/helper-wasm-section": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
- "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/ieee754": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
- "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
- "peer": true,
- "dependencies": {
- "@xtuc/ieee754": "^1.2.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/leb128": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
- "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
- "peer": true,
- "dependencies": {
- "@xtuc/long": "4.2.2"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/utf8": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
- "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/wasm-edit": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
- "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/helper-wasm-section": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6",
- "@webassemblyjs/wasm-opt": "1.11.6",
- "@webassemblyjs/wasm-parser": "1.11.6",
- "@webassemblyjs/wast-printer": "1.11.6"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/wasm-gen": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
- "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/ieee754": "1.11.6",
- "@webassemblyjs/leb128": "1.11.6",
- "@webassemblyjs/utf8": "1.11.6"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/wasm-opt": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
- "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6",
- "@webassemblyjs/wasm-parser": "1.11.6"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/wasm-parser": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
- "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-api-error": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/ieee754": "1.11.6",
- "@webassemblyjs/leb128": "1.11.6",
- "@webassemblyjs/utf8": "1.11.6"
- }
- },
- "packages/frontend-platform-shim/node_modules/@webassemblyjs/wast-printer": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
- "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
- "peer": true,
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@xtuc/long": "4.2.2"
- }
- },
- "packages/frontend-platform-shim/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "peer": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "packages/frontend-platform-shim/node_modules/axios": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
- "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
- "dependencies": {
- "follow-redirects": "^1.14.9",
- "form-data": "^4.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "peer": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/clean-css": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz",
- "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==",
- "peer": true,
- "dependencies": {
- "source-map": "~0.6.0"
- },
- "engines": {
- "node": ">= 10.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/clean-webpack-plugin": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz",
- "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==",
- "peer": true,
- "dependencies": {
- "del": "^4.1.1"
- },
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "webpack": ">=4.0.0 <6.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "peer": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/colorette": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
- "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/cosmiconfig": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
- "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==",
- "peer": true,
- "dependencies": {
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/d-fischer"
- }
- },
- "packages/frontend-platform-shim/node_modules/css-loader": {
- "version": "5.2.7",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
- "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
- "peer": true,
- "dependencies": {
- "icss-utils": "^5.1.0",
- "loader-utils": "^2.0.0",
- "postcss": "^8.2.15",
- "postcss-modules-extract-imports": "^3.0.0",
- "postcss-modules-local-by-default": "^4.0.0",
- "postcss-modules-scope": "^3.0.0",
- "postcss-modules-values": "^4.0.0",
- "postcss-value-parser": "^4.1.0",
- "schema-utils": "^3.0.0",
- "semver": "^7.3.5"
- },
- "engines": {
- "node": ">= 10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "webpack": "^4.27.0 || ^5.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/css-loader/node_modules/semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "peer": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "packages/frontend-platform-shim/node_modules/css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "peer": true,
- "dependencies": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/cssnano": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.1.tgz",
- "integrity": "sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==",
- "peer": true,
- "dependencies": {
- "cssnano-preset-default": "^6.0.1",
- "lilconfig": "^2.1.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/cssnano"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/cssnano-preset-default": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.1.tgz",
- "integrity": "sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==",
- "peer": true,
- "dependencies": {
- "css-declaration-sorter": "^6.3.1",
- "cssnano-utils": "^4.0.0",
- "postcss-calc": "^9.0.0",
- "postcss-colormin": "^6.0.0",
- "postcss-convert-values": "^6.0.0",
- "postcss-discard-comments": "^6.0.0",
- "postcss-discard-duplicates": "^6.0.0",
- "postcss-discard-empty": "^6.0.0",
- "postcss-discard-overridden": "^6.0.0",
- "postcss-merge-longhand": "^6.0.0",
- "postcss-merge-rules": "^6.0.1",
- "postcss-minify-font-values": "^6.0.0",
- "postcss-minify-gradients": "^6.0.0",
- "postcss-minify-params": "^6.0.0",
- "postcss-minify-selectors": "^6.0.0",
- "postcss-normalize-charset": "^6.0.0",
- "postcss-normalize-display-values": "^6.0.0",
- "postcss-normalize-positions": "^6.0.0",
- "postcss-normalize-repeat-style": "^6.0.0",
- "postcss-normalize-string": "^6.0.0",
- "postcss-normalize-timing-functions": "^6.0.0",
- "postcss-normalize-unicode": "^6.0.0",
- "postcss-normalize-url": "^6.0.0",
- "postcss-normalize-whitespace": "^6.0.0",
- "postcss-ordered-values": "^6.0.0",
- "postcss-reduce-initial": "^6.0.0",
- "postcss-reduce-transforms": "^6.0.0",
- "postcss-svgo": "^6.0.0",
- "postcss-unique-selectors": "^6.0.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/cssnano-utils": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz",
- "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==",
- "peer": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/csso": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
- "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
- "peer": true,
- "dependencies": {
- "css-tree": "~2.2.0"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
- "npm": ">=7.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/csso/node_modules/css-tree": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
- "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
- "peer": true,
- "dependencies": {
- "mdn-data": "2.0.28",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
- "npm": ">=7.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/csso/node_modules/mdn-data": {
- "version": "2.0.28",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
- "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "peer": true,
- "dependencies": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/dom-serializer/node_modules/entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "peer": true,
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "peer": true,
- "dependencies": {
- "domelementtype": "^2.2.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "peer": true,
- "dependencies": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/dotenv": {
- "version": "8.6.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
- "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
- "peer": true,
- "engines": {
- "node": ">=10"
- }
- },
- "packages/frontend-platform-shim/node_modules/dotenv-webpack": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz",
- "integrity": "sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==",
- "peer": true,
- "dependencies": {
- "dotenv-defaults": "^2.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "webpack": "^4 || ^5"
- }
- },
- "packages/frontend-platform-shim/node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "packages/frontend-platform-shim/node_modules/eslint": {
- "version": "8.41.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
- "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
- "peer": true,
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.4.0",
- "@eslint/eslintrc": "^2.0.3",
- "@eslint/js": "8.41.0",
- "@humanwhocodes/config-array": "^0.11.8",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@nodelib/fs.walk": "^1.2.8",
- "ajv": "^6.10.0",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
- "debug": "^4.3.2",
- "doctrine": "^3.0.0",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.0",
- "eslint-visitor-keys": "^3.4.1",
- "espree": "^9.5.2",
- "esquery": "^1.4.2",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.0.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
- "strip-ansi": "^6.0.1",
- "strip-json-comments": "^3.1.0",
- "text-table": "^0.2.0"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "packages/frontend-platform-shim/node_modules/eslint/node_modules/eslint-scope": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
- "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
- "peer": true,
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "packages/frontend-platform-shim/node_modules/filesize": {
- "version": "8.0.7",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
- "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==",
- "peer": true,
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/fork-ts-checker-webpack-plugin": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz",
- "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==",
- "peer": true,
- "dependencies": {
- "@babel/code-frame": "^7.8.3",
- "@types/json-schema": "^7.0.5",
- "chalk": "^4.1.0",
- "chokidar": "^3.4.2",
- "cosmiconfig": "^6.0.0",
- "deepmerge": "^4.2.2",
- "fs-extra": "^9.0.0",
- "glob": "^7.1.6",
- "memfs": "^3.1.2",
- "minimatch": "^3.0.4",
- "schema-utils": "2.7.0",
- "semver": "^7.3.2",
- "tapable": "^1.0.0"
- },
- "engines": {
- "node": ">=10",
- "yarn": ">=1.0.0"
- },
- "peerDependencies": {
- "eslint": ">= 6",
- "typescript": ">= 2.7",
- "vue-template-compiler": "*",
- "webpack": ">= 4"
- },
- "peerDependenciesMeta": {
- "eslint": {
- "optional": true
- },
- "vue-template-compiler": {
- "optional": true
- }
- }
- },
- "packages/frontend-platform-shim/node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "peer": true,
- "dependencies": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.7.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "packages/frontend-platform-shim/node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
- "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
- "peer": true,
- "dependencies": {
- "@types/json-schema": "^7.0.4",
- "ajv": "^6.12.2",
- "ajv-keywords": "^3.4.1"
- },
- "engines": {
- "node": ">= 8.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
- "packages/frontend-platform-shim/node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "peer": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "packages/frontend-platform-shim/node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
- "packages/frontend-platform-shim/node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "packages/frontend-platform-shim/node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "peer": true,
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/globals": {
- "version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
- "peer": true,
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "packages/frontend-platform-shim/node_modules/globby": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "peer": true,
- "dependencies": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.2.9",
- "ignore": "^5.2.0",
- "merge2": "^1.4.1",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "packages/frontend-platform-shim/node_modules/globby/node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "packages/frontend-platform-shim/node_modules/gzip-size": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
- "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
- "peer": true,
- "dependencies": {
- "duplexer": "^0.1.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "packages/frontend-platform-shim/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "packages/frontend-platform-shim/node_modules/html-minifier-terser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
- "peer": true,
- "dependencies": {
- "camel-case": "^4.1.2",
- "clean-css": "^5.2.2",
- "commander": "^8.3.0",
- "he": "^1.2.0",
- "param-case": "^3.0.4",
- "relateurl": "^0.2.7",
- "terser": "^5.10.0"
- },
- "bin": {
- "html-minifier-terser": "cli.js"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "packages/frontend-platform-shim/node_modules/html-minifier-terser/node_modules/commander": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
- "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
- "peer": true,
- "engines": {
- "node": ">= 12"
- }
- },
- "packages/frontend-platform-shim/node_modules/html-webpack-plugin": {
- "version": "5.5.1",
- "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz",
- "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==",
- "peer": true,
- "dependencies": {
- "@types/html-minifier-terser": "^6.0.0",
- "html-minifier-terser": "^6.0.2",
- "lodash": "^4.17.21",
- "pretty-error": "^4.0.0",
- "tapable": "^2.0.0"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/html-webpack-plugin"
- },
- "peerDependencies": {
- "webpack": "^5.20.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/htmlparser2": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
- "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
- "funding": [
- "https://github.com/fb55/htmlparser2?sponsor=1",
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "peer": true,
- "dependencies": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.0.0",
- "domutils": "^2.5.2",
- "entities": "^2.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/htmlparser2/node_modules/entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "peer": true,
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "packages/frontend-platform-shim/node_modules/immer": {
- "version": "9.0.21",
- "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
- "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/immer"
- }
- },
- "packages/frontend-platform-shim/node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "peer": true,
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "packages/frontend-platform-shim/node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/mini-css-extract-plugin": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz",
- "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==",
- "peer": true,
- "dependencies": {
- "loader-utils": "^2.0.0",
- "schema-utils": "^3.0.0",
- "webpack-sources": "^1.1.0"
- },
- "engines": {
- "node": ">= 10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "webpack": "^4.4.0 || ^5.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/open": {
- "version": "8.4.2",
- "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
- "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
- "peer": true,
- "dependencies": {
- "define-lazy-prop": "^2.0.0",
- "is-docker": "^2.1.1",
- "is-wsl": "^2.2.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss": {
- "version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "peer": true,
- "dependencies": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-calc": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
- "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
- "peer": true,
- "dependencies": {
- "postcss-selector-parser": "^6.0.11",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.2"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-colormin": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz",
- "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0",
- "colord": "^2.9.1",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-convert-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz",
- "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-custom-media": {
- "version": "9.1.4",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.4.tgz",
- "integrity": "sha512-4A7WEG3iIyKwfpxL5bkuSlHoHHGRTHl0212Z3uvpwJPyVfZJlkZAQNNgVC+oogrJgksDnfKyuuMbG6HafZPW8Q==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
- "peer": true,
- "dependencies": {
- "@csstools/cascade-layer-name-parser": "^1.0.2",
- "@csstools/css-parser-algorithms": "^2.1.1",
- "@csstools/css-tokenizer": "^2.1.1",
- "@csstools/media-query-list-parser": "^2.1.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18"
- },
- "peerDependencies": {
- "postcss": "^8.4"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-discard-comments": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz",
- "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==",
- "peer": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-discard-duplicates": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz",
- "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==",
- "peer": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-discard-empty": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz",
- "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==",
- "peer": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-discard-overridden": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz",
- "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==",
- "peer": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-loader": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.2.tgz",
- "integrity": "sha512-c7qDlXErX6n0VT+LUsW+nwefVtTu3ORtVvK8EXuUIDcxo+b/euYqpuHlJAvePb0Af5e8uMjR/13e0lTuYifaig==",
- "peer": true,
- "dependencies": {
- "cosmiconfig": "^8.1.3",
- "jiti": "^1.18.2",
- "klona": "^2.0.6",
- "semver": "^7.3.8"
- },
- "engines": {
- "node": ">= 14.15.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "postcss": "^7.0.0 || ^8.0.1",
- "webpack": "^5.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-loader/node_modules/semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "peer": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-merge-longhand": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz",
- "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0",
- "stylehacks": "^6.0.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-merge-rules": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.1.tgz",
- "integrity": "sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0",
- "cssnano-utils": "^4.0.0",
- "postcss-selector-parser": "^6.0.5"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-minify-font-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz",
- "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-minify-gradients": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz",
- "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==",
- "peer": true,
- "dependencies": {
- "colord": "^2.9.1",
- "cssnano-utils": "^4.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-minify-params": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz",
- "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "cssnano-utils": "^4.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-minify-selectors": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz",
- "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==",
- "peer": true,
- "dependencies": {
- "postcss-selector-parser": "^6.0.5"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-charset": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz",
- "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==",
- "peer": true,
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-display-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz",
- "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-positions": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz",
- "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-repeat-style": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz",
- "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-string": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz",
- "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-timing-functions": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz",
- "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-unicode": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz",
- "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-url": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz",
- "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-normalize-whitespace": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz",
- "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-ordered-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz",
- "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==",
- "peer": true,
- "dependencies": {
- "cssnano-utils": "^4.0.0",
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-reduce-initial": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz",
- "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-reduce-transforms": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz",
- "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-rtlcss": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-4.0.6.tgz",
- "integrity": "sha512-YNm6g2Y7Gngqtrpq3GC7cUkzH5Gq7aB+Lw9MSgF9s2ro1BDY7W4zqnd15g2ueatUUpSTg2/F5KDjQoTdjhbAKg==",
- "peer": true,
- "dependencies": {
- "rtlcss": "4.1.0"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.21"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-svgo": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz",
- "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==",
- "peer": true,
- "dependencies": {
- "postcss-value-parser": "^4.2.0",
- "svgo": "^3.0.2"
- },
- "engines": {
- "node": "^14 || ^16 || >= 18"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/postcss-unique-selectors": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz",
- "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==",
- "peer": true,
- "dependencies": {
- "postcss-selector-parser": "^6.0.5"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/pretty-error": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
- "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
- "peer": true,
- "dependencies": {
- "lodash": "^4.17.20",
- "renderkid": "^3.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/react-dev-utils": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
- "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==",
- "peer": true,
- "dependencies": {
- "@babel/code-frame": "^7.16.0",
- "address": "^1.1.2",
- "browserslist": "^4.18.1",
- "chalk": "^4.1.2",
- "cross-spawn": "^7.0.3",
- "detect-port-alt": "^1.1.6",
- "escape-string-regexp": "^4.0.0",
- "filesize": "^8.0.6",
- "find-up": "^5.0.0",
- "fork-ts-checker-webpack-plugin": "^6.5.0",
- "global-modules": "^2.0.0",
- "globby": "^11.0.4",
- "gzip-size": "^6.0.0",
- "immer": "^9.0.7",
- "is-root": "^2.1.0",
- "loader-utils": "^3.2.0",
- "open": "^8.4.0",
- "pkg-up": "^3.1.0",
- "prompts": "^2.4.2",
- "react-error-overlay": "^6.0.11",
- "recursive-readdir": "^2.2.2",
- "shell-quote": "^1.7.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "packages/frontend-platform-shim/node_modules/react-dev-utils/node_modules/loader-utils": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
- "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
- "peer": true,
- "engines": {
- "node": ">= 12.13.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/renderkid": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
- "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
- "peer": true,
- "dependencies": {
- "css-select": "^4.1.3",
- "dom-converter": "^0.2.0",
- "htmlparser2": "^6.1.0",
- "lodash": "^4.17.21",
- "strip-ansi": "^6.0.1"
- }
- },
- "packages/frontend-platform-shim/node_modules/renderkid/node_modules/css-select": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "peer": true,
- "dependencies": {
- "boolbase": "^1.0.0",
- "css-what": "^6.0.1",
- "domhandler": "^4.3.1",
- "domutils": "^2.8.0",
- "nth-check": "^2.0.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "packages/frontend-platform-shim/node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "peer": true,
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "packages/frontend-platform-shim/node_modules/rtlcss": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.0.tgz",
- "integrity": "sha512-W+N4hh0nVqVrrn3mRkHakxpB+c9cQ4CRT67O39kgA+1DjyhrdsqyCqIuHXyvWaXn4/835n+oX3fYJCi4+G/06A==",
- "peer": true,
- "dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0",
- "postcss": "^8.4.21",
- "strip-json-comments": "^3.1.1"
- },
- "bin": {
- "rtlcss": "bin/rtlcss.js"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/sass": {
- "version": "1.62.1",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz",
- "integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==",
- "peer": true,
- "dependencies": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
- "source-map-js": ">=0.6.2 <2.0.0"
- },
- "bin": {
- "sass": "sass.js"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/sass-loader": {
- "version": "13.3.1",
- "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.1.tgz",
- "integrity": "sha512-cBTxmgyVA1nXPvIK4brjJMXOMJ2v2YrQEuHqLw3LylGb3gsR6jAvdjHMcy/+JGTmmIF9SauTrLLR7bsWDMWqgg==",
- "peer": true,
- "dependencies": {
- "klona": "^2.0.6",
- "neo-async": "^2.6.2"
- },
- "engines": {
- "node": ">= 14.15.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "fibers": ">= 3.1.0",
- "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
- "sass": "^1.3.0",
- "sass-embedded": "*",
- "webpack": "^5.0.0"
- },
- "peerDependenciesMeta": {
- "fibers": {
- "optional": true
- },
- "node-sass": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- }
- }
- },
- "packages/frontend-platform-shim/node_modules/schema-utils": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz",
- "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==",
- "peer": true,
- "dependencies": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- },
- "engines": {
- "node": ">= 10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
- "packages/frontend-platform-shim/node_modules/shell-quote": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
- "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
- "peer": true,
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "packages/frontend-platform-shim/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/style-loader": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz",
- "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==",
- "peer": true,
- "engines": {
- "node": ">= 12.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "webpack": "^5.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/stylehacks": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz",
- "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==",
- "peer": true,
- "dependencies": {
- "browserslist": "^4.21.4",
- "postcss-selector-parser": "^6.0.4"
- },
- "engines": {
- "node": "^14 || ^16 || >=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.15"
- }
- },
- "packages/frontend-platform-shim/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "peer": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "packages/frontend-platform-shim/node_modules/svgo": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
- "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
- "peer": true,
- "dependencies": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^5.1.0",
- "css-tree": "^2.2.1",
- "csso": "^5.0.5",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "svgo": "bin/svgo"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/svgo"
- }
- },
- "packages/frontend-platform-shim/node_modules/svgo/node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "packages/frontend-platform-shim/node_modules/tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
- "packages/frontend-platform-shim/node_modules/terser": {
- "version": "5.17.7",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz",
- "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==",
- "peer": true,
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "packages/frontend-platform-shim/node_modules/terser/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "peer": true
- },
- "packages/frontend-platform-shim/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack": {
- "version": "5.84.1",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.1.tgz",
- "integrity": "sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg==",
- "peer": true,
- "dependencies": {
- "@types/eslint-scope": "^3.7.3",
- "@types/estree": "^1.0.0",
- "@webassemblyjs/ast": "^1.11.5",
- "@webassemblyjs/wasm-edit": "^1.11.5",
- "@webassemblyjs/wasm-parser": "^1.11.5",
- "acorn": "^8.7.1",
- "acorn-import-assertions": "^1.9.0",
- "browserslist": "^4.14.5",
- "chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.14.1",
- "es-module-lexer": "^1.2.1",
- "eslint-scope": "5.1.1",
- "events": "^3.2.0",
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.2.9",
- "json-parse-even-better-errors": "^2.3.1",
- "loader-runner": "^4.2.0",
- "mime-types": "^2.1.27",
- "neo-async": "^2.6.2",
- "schema-utils": "^3.1.2",
- "tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.7",
- "watchpack": "^2.4.0",
- "webpack-sources": "^3.2.3"
- },
- "bin": {
- "webpack": "bin/webpack.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependenciesMeta": {
- "webpack-cli": {
- "optional": true
- }
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-cli": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.3.tgz",
- "integrity": "sha512-MTuk7NUMvEHQUSXCpvUrF1q2p0FJS40dPFfqQvG3jTWcgv/8plBNz2Kv2HXZiLGPnfmSAA5uCtCILO1JBmmkfw==",
- "peer": true,
- "dependencies": {
- "@discoveryjs/json-ext": "^0.5.0",
- "@webpack-cli/configtest": "^2.1.1",
- "@webpack-cli/info": "^2.0.2",
- "@webpack-cli/serve": "^2.0.5",
- "colorette": "^2.0.14",
- "commander": "^10.0.1",
- "cross-spawn": "^7.0.3",
- "envinfo": "^7.7.3",
- "fastest-levenshtein": "^1.0.12",
- "import-local": "^3.0.2",
- "interpret": "^3.1.1",
- "rechoir": "^0.8.0",
- "webpack-merge": "^5.7.3"
- },
- "bin": {
- "webpack-cli": "bin/cli.js"
- },
- "engines": {
- "node": ">=14.15.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "webpack": "5.x.x"
- },
- "peerDependenciesMeta": {
- "@webpack-cli/generators": {
- "optional": true
- },
- "webpack-bundle-analyzer": {
- "optional": true
- },
- "webpack-dev-server": {
- "optional": true
- }
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-cli/node_modules/commander": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
- "peer": true,
- "engines": {
- "node": ">=14"
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-dev-server": {
- "version": "4.15.0",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz",
- "integrity": "sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ==",
- "peer": true,
- "dependencies": {
- "@types/bonjour": "^3.5.9",
- "@types/connect-history-api-fallback": "^1.3.5",
- "@types/express": "^4.17.13",
- "@types/serve-index": "^1.9.1",
- "@types/serve-static": "^1.13.10",
- "@types/sockjs": "^0.3.33",
- "@types/ws": "^8.5.1",
- "ansi-html-community": "^0.0.8",
- "bonjour-service": "^1.0.11",
- "chokidar": "^3.5.3",
- "colorette": "^2.0.10",
- "compression": "^1.7.4",
- "connect-history-api-fallback": "^2.0.0",
- "default-gateway": "^6.0.3",
- "express": "^4.17.3",
- "graceful-fs": "^4.2.6",
- "html-entities": "^2.3.2",
- "http-proxy-middleware": "^2.0.3",
- "ipaddr.js": "^2.0.1",
- "launch-editor": "^2.6.0",
- "open": "^8.0.9",
- "p-retry": "^4.5.0",
- "rimraf": "^3.0.2",
- "schema-utils": "^4.0.0",
- "selfsigned": "^2.1.1",
- "serve-index": "^1.9.1",
- "sockjs": "^0.3.24",
- "spdy": "^4.0.2",
- "webpack-dev-middleware": "^5.3.1",
- "ws": "^8.13.0"
- },
- "bin": {
- "webpack-dev-server": "bin/webpack-dev-server.js"
- },
- "engines": {
- "node": ">= 12.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- },
- "peerDependencies": {
- "webpack": "^4.37.0 || ^5.0.0"
- },
- "peerDependenciesMeta": {
- "webpack": {
- "optional": true
- },
- "webpack-cli": {
- "optional": true
- }
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-dev-server/node_modules/ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "peer": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-dev-server/node_modules/ajv-keywords": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "peer": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.3"
- },
- "peerDependencies": {
- "ajv": "^8.8.2"
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-dev-server/node_modules/schema-utils": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
- "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
- "peer": true,
- "dependencies": {
- "@types/json-schema": "^7.0.9",
- "ajv": "^8.9.0",
- "ajv-formats": "^2.1.1",
- "ajv-keywords": "^5.1.0"
- },
- "engines": {
- "node": ">= 12.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack-merge": {
- "version": "5.9.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
- "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==",
- "peer": true,
- "dependencies": {
- "clone-deep": "^4.0.1",
- "wildcard": "^2.0.0"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/webpack/node_modules/webpack-sources": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
- "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
- "peer": true,
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "packages/frontend-platform-shim/node_modules/ws": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
- "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
- "peer": true,
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- }
- },
- "dependencies": {
- "@algolia/cache-browser-local-storage": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.8.3.tgz",
- "integrity": "sha512-Cwc03hikHSUI+xvgUdN+H+f6jFyoDsC9fegzXzJ2nPn1YSN9EXzDMBnbrgl0sbl9iLGXe0EIGMYqR2giCv1wMQ==",
- "requires": {
- "@algolia/cache-common": "4.8.3"
- }
- },
- "@algolia/cache-common": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.8.3.tgz",
- "integrity": "sha512-Cf7zZ2i6H+tLSBTkFePHhYvlgc9fnMPKsF9qTmiU38kFIGORy/TN2Fx5n1GBuRLIzaSXvcf+oHv1HvU0u1gE1g=="
- },
- "@algolia/cache-in-memory": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.8.3.tgz",
- "integrity": "sha512-+N7tkvmijXiDy2E7u1mM73AGEgGPWFmEmPeJS96oT46I98KXAwVPNYbcAqBE79YlixdXpkYJk41cFcORzNh+Iw==",
- "requires": {
- "@algolia/cache-common": "4.8.3"
- }
- },
- "@algolia/client-account": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.8.3.tgz",
- "integrity": "sha512-Uku8LqnXBwfDCtsTCDYTUOz2/2oqcAQCKgaO0uGdIR8DTQENBXFQvzziambHdn9KuFuY+6Et9k1+cjpTPBDTBg==",
- "requires": {
- "@algolia/client-common": "4.8.3",
- "@algolia/client-search": "4.8.3",
- "@algolia/transporter": "4.8.3"
- }
- },
- "@algolia/client-analytics": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.8.3.tgz",
- "integrity": "sha512-9ensIWmjYJprZ+YjAVSZdWUG05xEnbytENXp508X59tf34IMIX8BR2xl0RjAQODtxBdAteGxuKt5THX6U9tQLA==",
- "requires": {
- "@algolia/client-common": "4.8.3",
- "@algolia/client-search": "4.8.3",
- "@algolia/requester-common": "4.8.3",
- "@algolia/transporter": "4.8.3"
- }
- },
- "@algolia/client-common": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.8.3.tgz",
- "integrity": "sha512-TU3623AEFAWUQlDTznkgAMSYo8lfS9pNs5QYDQzkvzWdqK0GBDWthwdRfo9iIsfxiR9qdCMHqwEu+AlZMVhNSA==",
- "requires": {
- "@algolia/requester-common": "4.8.3",
- "@algolia/transporter": "4.8.3"
- }
- },
- "@algolia/client-recommendation": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.8.3.tgz",
- "integrity": "sha512-qysGbmkcc6Agt29E38KWJq9JuxjGsyEYoKuX9K+P5HyQh08yR/BlRYrA8mB7vT/OIUHRGFToGO6Vq/rcg0NIOQ==",
- "requires": {
- "@algolia/client-common": "4.8.3",
- "@algolia/requester-common": "4.8.3",
- "@algolia/transporter": "4.8.3"
- }
- },
- "@algolia/client-search": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.8.3.tgz",
- "integrity": "sha512-rAnvoy3GAhbzOQVniFcKVn1eM2NX77LearzYNCbtFrFYavG+hJI187bNVmajToiuGZ10FfJvK99X2OB1AzzezQ==",
- "requires": {
- "@algolia/client-common": "4.8.3",
- "@algolia/requester-common": "4.8.3",
- "@algolia/transporter": "4.8.3"
- }
- },
- "@algolia/events": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz",
- "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ=="
- },
- "@algolia/logger-common": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.8.3.tgz",
- "integrity": "sha512-03wksHRbhl2DouEKnqWuUb64s1lV6kDAAabMCQ2Du1fb8X/WhDmxHC4UXMzypeOGlH5BZBsgVwSB7vsZLP3MZg=="
- },
- "@algolia/logger-console": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.8.3.tgz",
- "integrity": "sha512-Npt+hI4UF8t3TLMluL5utr9Gc11BjL5kDnGZOhDOAz5jYiSO2nrHMFmnpLT4Cy/u7a5t7EB5dlypuC4/AGStkA==",
- "requires": {
- "@algolia/logger-common": "4.8.3"
- }
- },
- "@algolia/requester-browser-xhr": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.8.3.tgz",
- "integrity": "sha512-/LTTIpgEmEwkyhn8yXxDdBWqXqzlgw5w2PtTpIwkSlP2/jDwdR/9w1TkFzhNbJ81ki6LAEQM5mSwoTTnbIIecg==",
- "requires": {
- "@algolia/requester-common": "4.8.3"
- }
- },
- "@algolia/requester-common": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.8.3.tgz",
- "integrity": "sha512-+Yo9vBkofoKR1SCqqtMnmnfq9yt/BiaDewY/6bYSMNxSYCnu2Fw1JKSIaf/4zos09PMSsxGpLohZwGas3+0GDQ=="
- },
- "@algolia/requester-node-http": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.8.3.tgz",
- "integrity": "sha512-k2fiKIeMIFqgC01FnzII6kqC2GQBAfbNaUX4k7QCPa6P8t4sp2xE6fImOUiztLnnL3C9X9ZX6Fw3L+cudi7jvQ==",
- "requires": {
- "@algolia/requester-common": "4.8.3"
- }
- },
- "@algolia/transporter": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.8.3.tgz",
- "integrity": "sha512-nU7fy2iU8snxATlsks0MjMyv97QJWQmOVwTjDc+KZ4+nue8CLcgm4LA4dsTBqvxeCQIoEtt3n72GwXcaqiJSjQ==",
- "requires": {
- "@algolia/cache-common": "4.8.3",
- "@algolia/logger-common": "4.8.3",
- "@algolia/requester-common": "4.8.3"
- }
- },
- "@ampproject/remapping": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
- "requires": {
- "@jridgewell/gen-mapping": "^0.1.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- },
- "@babel/cli": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.21.0.tgz",
- "integrity": "sha512-xi7CxyS8XjSyiwUGCfwf+brtJxjW1/ZTcBUkP10xawIEXLX5HzLn+3aXkgxozcP2UhRhtKTmQurw9Uaes7jZrA==",
- "dev": true,
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.17",
- "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
- "chokidar": "^3.4.0",
- "commander": "^4.0.1",
- "convert-source-map": "^1.1.0",
- "fs-readdir-recursive": "^1.1.0",
- "glob": "^7.2.0",
- "make-dir": "^2.1.0",
- "slash": "^2.0.0"
- }
- },
- "@babel/code-frame": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
- "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
- "requires": {
- "@babel/highlight": "^7.18.6"
- }
- },
- "@babel/compat-data": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz",
- "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ=="
- },
- "@babel/core": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
- "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
- "requires": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.21.4",
- "@babel/generator": "^7.21.4",
- "@babel/helper-compilation-targets": "^7.21.4",
- "@babel/helper-module-transforms": "^7.21.2",
- "@babel/helpers": "^7.21.0",
- "@babel/parser": "^7.21.4",
- "@babel/template": "^7.20.7",
- "@babel/traverse": "^7.21.4",
- "@babel/types": "^7.21.4",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.2",
- "semver": "^6.3.0"
- }
- },
- "@babel/eslint-parser": {
- "version": "7.21.8",
- "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.8.tgz",
- "integrity": "sha512-HLhI+2q+BP3sf78mFUZNCGc10KEmoUqtUT1OCdMZsN+qr4qFeLUod62/zAnF3jNQstwyasDkZnVXwfK2Bml7MQ==",
- "peer": true,
- "requires": {
- "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
- "eslint-visitor-keys": "^2.1.0",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "eslint-visitor-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
- "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
- "peer": true
- }
- }
- },
- "@babel/generator": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz",
- "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==",
- "requires": {
- "@babel/types": "^7.22.3",
- "@jridgewell/gen-mapping": "^0.3.2",
- "@jridgewell/trace-mapping": "^0.3.17",
- "jsesc": "^2.5.1"
- },
- "dependencies": {
- "@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "requires": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- }
- }
- },
- "@babel/helper-annotate-as-pure": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
- "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-builder-binary-assignment-operator-visitor": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz",
- "integrity": "sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg==",
- "requires": {
- "@babel/types": "^7.22.3"
- }
- },
- "@babel/helper-compilation-targets": {
- "version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz",
- "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==",
- "requires": {
- "@babel/compat-data": "^7.22.0",
- "@babel/helper-validator-option": "^7.21.0",
- "browserslist": "^4.21.3",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "requires": {
- "yallist": "^3.0.2"
- }
- },
- "yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
- }
- }
- },
- "@babel/helper-create-class-features-plugin": {
- "version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz",
- "integrity": "sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-environment-visitor": "^7.22.1",
- "@babel/helper-function-name": "^7.21.0",
- "@babel/helper-member-expression-to-functions": "^7.22.0",
- "@babel/helper-optimise-call-expression": "^7.18.6",
- "@babel/helper-replace-supers": "^7.22.1",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "semver": "^6.3.0"
- }
- },
- "@babel/helper-create-regexp-features-plugin": {
- "version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz",
- "integrity": "sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "regexpu-core": "^5.3.1",
- "semver": "^6.3.0"
- }
- },
- "@babel/helper-define-polyfill-provider": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
- "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
- "requires": {
- "@babel/helper-compilation-targets": "^7.17.7",
- "@babel/helper-plugin-utils": "^7.16.7",
- "debug": "^4.1.1",
- "lodash.debounce": "^4.0.8",
- "resolve": "^1.14.2",
- "semver": "^6.1.2"
- }
- },
- "@babel/helper-environment-visitor": {
- "version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz",
- "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA=="
- },
- "@babel/helper-function-name": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
- "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
- "requires": {
- "@babel/template": "^7.20.7",
- "@babel/types": "^7.21.0"
- }
- },
- "@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-member-expression-to-functions": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz",
- "integrity": "sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==",
- "requires": {
- "@babel/types": "^7.22.3"
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
- "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
- "requires": {
- "@babel/types": "^7.21.4"
- }
- },
- "@babel/helper-module-transforms": {
- "version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz",
- "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==",
- "requires": {
- "@babel/helper-environment-visitor": "^7.22.1",
- "@babel/helper-module-imports": "^7.21.4",
- "@babel/helper-simple-access": "^7.21.5",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/helper-validator-identifier": "^7.19.1",
- "@babel/template": "^7.21.9",
- "@babel/traverse": "^7.22.1",
- "@babel/types": "^7.22.0"
- }
- },
- "@babel/helper-optimise-call-expression": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
- "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz",
- "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg=="
- },
- "@babel/helper-remap-async-to-generator": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
- "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-wrap-function": "^7.18.9",
- "@babel/types": "^7.18.9"
- }
- },
- "@babel/helper-replace-supers": {
- "version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz",
- "integrity": "sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ==",
- "requires": {
- "@babel/helper-environment-visitor": "^7.22.1",
- "@babel/helper-member-expression-to-functions": "^7.22.0",
- "@babel/helper-optimise-call-expression": "^7.18.6",
- "@babel/template": "^7.21.9",
- "@babel/traverse": "^7.22.1",
- "@babel/types": "^7.22.0"
- }
- },
- "@babel/helper-simple-access": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz",
- "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==",
- "requires": {
- "@babel/types": "^7.21.5"
- }
- },
- "@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
- "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
- "requires": {
- "@babel/types": "^7.20.0"
- }
- },
- "@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-string-parser": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
- "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w=="
- },
- "@babel/helper-validator-identifier": {
- "version": "7.19.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
- "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
- },
- "@babel/helper-validator-option": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
- "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ=="
- },
- "@babel/helper-wrap-function": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz",
- "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==",
- "requires": {
- "@babel/helper-function-name": "^7.19.0",
- "@babel/template": "^7.18.10",
- "@babel/traverse": "^7.20.5",
- "@babel/types": "^7.20.5"
- }
- },
- "@babel/helpers": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz",
- "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==",
- "requires": {
- "@babel/template": "^7.21.9",
- "@babel/traverse": "^7.22.1",
- "@babel/types": "^7.22.3"
- }
- },
- "@babel/highlight": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
- "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
- "requires": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "chalk": "^2.0.0",
- "js-tokens": "^4.0.0"
- }
- },
- "@babel/parser": {
- "version": "7.22.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz",
- "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA=="
- },
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
- "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz",
- "integrity": "sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
- "@babel/plugin-transform-optional-chaining": "^7.22.3"
- }
- },
- "@babel/plugin-proposal-async-generator-functions": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz",
- "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==",
- "requires": {
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/helper-remap-async-to-generator": "^7.18.9",
- "@babel/plugin-syntax-async-generators": "^7.8.4"
- }
- },
- "@babel/plugin-proposal-class-properties": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
- "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
- "requires": {
- "@babel/helper-create-class-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-proposal-class-static-block": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz",
- "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==",
- "requires": {
- "@babel/helper-create-class-features-plugin": "^7.21.0",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/plugin-syntax-class-static-block": "^7.14.5"
- }
- },
- "@babel/plugin-proposal-dynamic-import": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
- "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3"
- }
- },
- "@babel/plugin-proposal-export-namespace-from": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
- "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.9",
- "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
- }
- },
- "@babel/plugin-proposal-json-strings": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
- "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/plugin-syntax-json-strings": "^7.8.3"
- }
- },
- "@babel/plugin-proposal-logical-assignment-operators": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz",
- "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
- }
- },
- "@babel/plugin-proposal-nullish-coalescing-operator": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
- "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
- }
- },
- "@babel/plugin-proposal-numeric-separator": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
- "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/plugin-syntax-numeric-separator": "^7.10.4"
- }
- },
- "@babel/plugin-proposal-object-rest-spread": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
- "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
- "requires": {
- "@babel/compat-data": "^7.20.5",
- "@babel/helper-compilation-targets": "^7.20.7",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-transform-parameters": "^7.20.7"
- }
- },
- "@babel/plugin-proposal-optional-catch-binding": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
- "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
- }
- },
- "@babel/plugin-proposal-optional-chaining": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
- "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3"
- }
- },
- "@babel/plugin-proposal-private-methods": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
- "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
- "requires": {
- "@babel/helper-create-class-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
- "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-create-class-features-plugin": "^7.21.0",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
- }
- },
- "@babel/plugin-proposal-unicode-property-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
- "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-syntax-async-generators": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-bigint": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
- "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-class-properties": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
- "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.12.13"
- }
- },
- "@babel/plugin-syntax-class-static-block": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
- "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.14.5"
- }
- },
- "@babel/plugin-syntax-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-export-namespace-from": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
- "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.3"
- }
- },
- "@babel/plugin-syntax-import-assertions": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz",
- "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.19.0"
- }
- },
- "@babel/plugin-syntax-import-meta": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
- "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-jsx": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz",
- "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2"
- }
- },
- "@babel/plugin-syntax-logical-assignment-operators": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
- "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-numeric-separator": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
- "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-private-property-in-object": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
- "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.14.5"
- }
- },
- "@babel/plugin-syntax-top-level-await": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.14.5"
- }
- },
- "@babel/plugin-syntax-typescript": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz",
- "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2"
- }
- },
- "@babel/plugin-transform-arrow-functions": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz",
- "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-async-to-generator": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz",
- "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==",
- "requires": {
- "@babel/helper-module-imports": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/helper-remap-async-to-generator": "^7.18.9"
- }
- },
- "@babel/plugin-transform-block-scoped-functions": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
- "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-block-scoping": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz",
- "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2"
- }
- },
- "@babel/plugin-transform-classes": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz",
- "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-compilation-targets": "^7.20.7",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.21.0",
- "@babel/helper-optimise-call-expression": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/helper-replace-supers": "^7.20.7",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "globals": "^11.1.0"
- }
- },
- "@babel/plugin-transform-computed-properties": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz",
- "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/template": "^7.20.7"
- }
- },
- "@babel/plugin-transform-destructuring": {
- "version": "7.21.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz",
- "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2"
- }
- },
- "@babel/plugin-transform-dotall-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
- "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-duplicate-keys": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz",
- "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.9"
- }
- },
- "@babel/plugin-transform-exponentiation-operator": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
- "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
- "requires": {
- "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-for-of": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz",
- "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-function-name": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz",
- "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==",
- "requires": {
- "@babel/helper-compilation-targets": "^7.18.9",
- "@babel/helper-function-name": "^7.18.9",
- "@babel/helper-plugin-utils": "^7.18.9"
- }
- },
- "@babel/plugin-transform-literals": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz",
- "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.9"
- }
- },
- "@babel/plugin-transform-member-expression-literals": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
- "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-modules-amd": {
- "version": "7.20.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz",
- "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==",
- "requires": {
- "@babel/helper-module-transforms": "^7.20.11",
- "@babel/helper-plugin-utils": "^7.20.2"
- }
- },
- "@babel/plugin-transform-modules-commonjs": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz",
- "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==",
- "requires": {
- "@babel/helper-module-transforms": "^7.21.5",
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-simple-access": "^7.21.5"
- }
- },
- "@babel/plugin-transform-modules-systemjs": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz",
- "integrity": "sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw==",
- "requires": {
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-module-transforms": "^7.22.1",
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-validator-identifier": "^7.19.1"
- }
- },
- "@babel/plugin-transform-modules-umd": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
- "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
- "requires": {
- "@babel/helper-module-transforms": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz",
- "integrity": "sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q==",
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.1",
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-new-target": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz",
- "integrity": "sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-object-super": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
- "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/helper-replace-supers": "^7.18.6"
- }
- },
- "@babel/plugin-transform-optional-chaining": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz",
- "integrity": "sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3"
- }
- },
- "@babel/plugin-transform-parameters": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz",
- "integrity": "sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-property-literals": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
- "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-react-constant-elements": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.3.tgz",
- "integrity": "sha512-b5J6muxQYp4H7loAQv/c7GO5cPuRA6H5hx4gO+/Hn+Cu9MRQU0PNiUoWq1L//8sq6kFSNxGXFb2XTaUfa9y+Pg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-react-display-name": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz",
- "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-react-jsx": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz",
- "integrity": "sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-module-imports": "^7.21.4",
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/plugin-syntax-jsx": "^7.21.4",
- "@babel/types": "^7.22.3"
- }
- },
- "@babel/plugin-transform-react-jsx-development": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz",
- "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==",
- "requires": {
- "@babel/plugin-transform-react-jsx": "^7.18.6"
- }
- },
- "@babel/plugin-transform-react-pure-annotations": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz",
- "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-regenerator": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz",
- "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5",
- "regenerator-transform": "^0.15.1"
- }
- },
- "@babel/plugin-transform-reserved-words": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
- "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-runtime": {
- "version": "7.12.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz",
- "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==",
- "requires": {
- "@babel/helper-module-imports": "^7.12.1",
- "@babel/helper-plugin-utils": "^7.10.4",
- "resolve": "^1.8.1",
- "semver": "^5.5.1"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- }
- }
- },
- "@babel/plugin-transform-shorthand-properties": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
- "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-spread": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz",
- "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0"
- }
- },
- "@babel/plugin-transform-sticky-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
- "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/plugin-transform-template-literals": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz",
- "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.9"
- }
- },
- "@babel/plugin-transform-typeof-symbol": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz",
- "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.9"
- }
- },
- "@babel/plugin-transform-typescript": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.3.tgz",
- "integrity": "sha512-pyjnCIniO5PNaEuGxT28h0HbMru3qCVrMqVgVOz/krComdIrY9W6FCLBq9NWHY8HDGaUlan+UhmZElDENIfCcw==",
- "requires": {
- "@babel/helper-annotate-as-pure": "^7.18.6",
- "@babel/helper-create-class-features-plugin": "^7.22.1",
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/plugin-syntax-typescript": "^7.21.4"
- }
- },
- "@babel/plugin-transform-unicode-escapes": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz",
- "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5"
- }
- },
- "@babel/plugin-transform-unicode-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
- "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
- "requires": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/preset-env": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz",
- "integrity": "sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.21.4",
- "@babel/helper-compilation-targets": "^7.21.4",
- "@babel/helper-plugin-utils": "^7.20.2",
- "@babel/helper-validator-option": "^7.21.0",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7",
- "@babel/plugin-proposal-async-generator-functions": "^7.20.7",
- "@babel/plugin-proposal-class-properties": "^7.18.6",
- "@babel/plugin-proposal-class-static-block": "^7.21.0",
- "@babel/plugin-proposal-dynamic-import": "^7.18.6",
- "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
- "@babel/plugin-proposal-json-strings": "^7.18.6",
- "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
- "@babel/plugin-proposal-numeric-separator": "^7.18.6",
- "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
- "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
- "@babel/plugin-proposal-optional-chaining": "^7.21.0",
- "@babel/plugin-proposal-private-methods": "^7.18.6",
- "@babel/plugin-proposal-private-property-in-object": "^7.21.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-class-properties": "^7.12.13",
- "@babel/plugin-syntax-class-static-block": "^7.14.5",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
- "@babel/plugin-syntax-import-assertions": "^7.20.0",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.10.4",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
- "@babel/plugin-syntax-top-level-await": "^7.14.5",
- "@babel/plugin-transform-arrow-functions": "^7.20.7",
- "@babel/plugin-transform-async-to-generator": "^7.20.7",
- "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
- "@babel/plugin-transform-block-scoping": "^7.21.0",
- "@babel/plugin-transform-classes": "^7.21.0",
- "@babel/plugin-transform-computed-properties": "^7.20.7",
- "@babel/plugin-transform-destructuring": "^7.21.3",
- "@babel/plugin-transform-dotall-regex": "^7.18.6",
- "@babel/plugin-transform-duplicate-keys": "^7.18.9",
- "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
- "@babel/plugin-transform-for-of": "^7.21.0",
- "@babel/plugin-transform-function-name": "^7.18.9",
- "@babel/plugin-transform-literals": "^7.18.9",
- "@babel/plugin-transform-member-expression-literals": "^7.18.6",
- "@babel/plugin-transform-modules-amd": "^7.20.11",
- "@babel/plugin-transform-modules-commonjs": "^7.21.2",
- "@babel/plugin-transform-modules-systemjs": "^7.20.11",
- "@babel/plugin-transform-modules-umd": "^7.18.6",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5",
- "@babel/plugin-transform-new-target": "^7.18.6",
- "@babel/plugin-transform-object-super": "^7.18.6",
- "@babel/plugin-transform-parameters": "^7.21.3",
- "@babel/plugin-transform-property-literals": "^7.18.6",
- "@babel/plugin-transform-regenerator": "^7.20.5",
- "@babel/plugin-transform-reserved-words": "^7.18.6",
- "@babel/plugin-transform-shorthand-properties": "^7.18.6",
- "@babel/plugin-transform-spread": "^7.20.7",
- "@babel/plugin-transform-sticky-regex": "^7.18.6",
- "@babel/plugin-transform-template-literals": "^7.18.9",
- "@babel/plugin-transform-typeof-symbol": "^7.18.9",
- "@babel/plugin-transform-unicode-escapes": "^7.18.10",
- "@babel/plugin-transform-unicode-regex": "^7.18.6",
- "@babel/preset-modules": "^0.1.5",
- "@babel/types": "^7.21.4",
- "babel-plugin-polyfill-corejs2": "^0.3.3",
- "babel-plugin-polyfill-corejs3": "^0.6.0",
- "babel-plugin-polyfill-regenerator": "^0.4.1",
- "core-js-compat": "^3.25.1",
- "semver": "^6.3.0"
- }
- },
- "@babel/preset-modules": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
- "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
- "@babel/plugin-transform-dotall-regex": "^7.4.4",
- "@babel/types": "^7.4.4",
- "esutils": "^2.0.2"
- }
- },
- "@babel/preset-react": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz",
- "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6",
- "@babel/helper-validator-option": "^7.18.6",
- "@babel/plugin-transform-react-display-name": "^7.18.6",
- "@babel/plugin-transform-react-jsx": "^7.18.6",
- "@babel/plugin-transform-react-jsx-development": "^7.18.6",
- "@babel/plugin-transform-react-pure-annotations": "^7.18.6"
- }
- },
- "@babel/preset-typescript": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz",
- "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-validator-option": "^7.21.0",
- "@babel/plugin-syntax-jsx": "^7.21.4",
- "@babel/plugin-transform-modules-commonjs": "^7.21.5",
- "@babel/plugin-transform-typescript": "^7.21.3"
- }
- },
- "@babel/regjsgen": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
- "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
- },
- "@babel/runtime": {
- "version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz",
- "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==",
- "requires": {
- "regenerator-runtime": "^0.13.11"
- },
- "dependencies": {
- "regenerator-runtime": {
- "version": "0.13.11",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
- "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
- }
- }
- },
- "@babel/runtime-corejs3": {
- "version": "7.20.1",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz",
- "integrity": "sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg==",
- "requires": {
- "core-js-pure": "^3.25.1",
- "regenerator-runtime": "^0.13.10"
- },
- "dependencies": {
- "regenerator-runtime": {
- "version": "0.13.11",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
- "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
- }
- }
- },
- "@babel/template": {
- "version": "7.21.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz",
- "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==",
- "requires": {
- "@babel/code-frame": "^7.21.4",
- "@babel/parser": "^7.21.9",
- "@babel/types": "^7.21.5"
- }
- },
- "@babel/traverse": {
- "version": "7.22.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz",
- "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==",
- "requires": {
- "@babel/code-frame": "^7.21.4",
- "@babel/generator": "^7.22.3",
- "@babel/helper-environment-visitor": "^7.22.1",
- "@babel/helper-function-name": "^7.21.0",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.22.4",
- "@babel/types": "^7.22.4",
- "debug": "^4.1.0",
- "globals": "^11.1.0"
- }
- },
- "@babel/types": {
- "version": "7.22.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz",
- "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==",
- "requires": {
- "@babel/helper-string-parser": "^7.21.5",
- "@babel/helper-validator-identifier": "^7.19.1",
- "to-fast-properties": "^2.0.0"
- }
- },
- "@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
- },
- "@cnakazawa/watch": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
- "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==",
- "requires": {
- "exec-sh": "^0.3.2",
- "minimist": "^1.2.0"
- }
- },
- "@cospired/i18n-iso-languages": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@cospired/i18n-iso-languages/-/i18n-iso-languages-2.2.0.tgz",
- "integrity": "sha512-hywY9u9apWGeLxQuRcXw7IW0XkMdXum/hr3TpmHY2fAbXMTFlhhkPCdsQeHzjxMQwTnMgXaZ4j4WOCwKtlDRCQ=="
- },
- "@csstools/cascade-layer-name-parser": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.2.tgz",
- "integrity": "sha512-xm7Mgwej/wBfLoK0K5LfntmPJzoULayl1XZY9JYgQgT29JiqNw++sLnx95u5y9zCihblzkyaRYJrsRMhIBzRdg==",
- "requires": {}
- },
- "@csstools/css-parser-algorithms": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz",
- "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==",
- "requires": {}
- },
- "@csstools/css-tokenizer": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz",
- "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA=="
- },
- "@csstools/media-query-list-parser": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.0.tgz",
- "integrity": "sha512-MXkR+TeaS2q9IkpyO6jVCdtA/bfpABJxIrfkLswThFN8EZZgI2RfAHhm6sDNDuYV25d5+b8Lj1fpTccIcSLPsQ==",
- "requires": {}
- },
- "@discoveryjs/json-ext": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
- "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
- },
- "@edx/brand": {
- "version": "npm:@edx/brand-openedx@1.2.0",
- "resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.2.0.tgz",
- "integrity": "sha512-r4PDN3rCgDsLovW44ayxoNNHgG5I4Rvss6MG5CrQEX4oW8YhQVEod+jJtwR5vi0mFLN2GIaMlDpd7iIy03VqXg=="
- },
- "@edx/browserslist-config": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.0.0.tgz",
- "integrity": "sha512-gLAlpz9Y5VruxqiUBTROG7PvouIxoMc6dvhvNpXUDHRN0KEke+zBj+zJ4frL9kGbkeex273nzSazbG42hNDLrg==",
- "dev": true
- },
- "@edx/eslint-config": {
- "version": "4.0.0-alpha.1",
- "resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.0.0-alpha.1.tgz",
- "integrity": "sha512-+Hx8r6z+DdwryqluA0MF7S/RCurakzQs4AfNufWu2BLcIaf/Jot19NfcxS4Dzo8WUWiBL+lo4/42fclHHrvk9Q==",
- "dev": true,
- "requires": {}
- },
- "@edx/frontend-build": {
- "version": "12.9.0-alpha.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.9.0-alpha.1.tgz",
- "integrity": "sha512-boHfp7yzUn5JptWGi2jK/EMaIlJ4nzjZBp7PW19vkNokbuQ3ansJsRvMscFg83tw0lXRTKCPN1y0uKg92AMizg==",
- "dev": true,
- "requires": {
- "@babel/cli": "7.21.0",
- "@babel/core": "7.21.4",
- "@babel/plugin-proposal-class-properties": "7.18.6",
- "@babel/plugin-proposal-object-rest-spread": "7.20.7",
- "@babel/plugin-syntax-dynamic-import": "7.8.3",
- "@babel/preset-env": "7.21.4",
- "@babel/preset-react": "7.18.6",
- "@edx/eslint-config": "4.0.0-alpha.1",
- "@edx/new-relic-source-map-webpack-plugin": "1.0.2",
- "@edx/typescript-config": "^1.0.0",
- "@fullhuman/postcss-purgecss": "^5.0.0",
- "@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
- "@svgr/webpack": "6.5.1",
- "@types/jest": "^26.0.0",
- "@typescript-eslint/eslint-plugin": "^5.58.0",
- "@typescript-eslint/parser": "^5.58.0",
- "autoprefixer": "10.4.14",
- "babel-jest": "26.6.3",
- "babel-loader": "9.1.2",
- "babel-plugin-react-intl": "7.9.4",
- "babel-plugin-transform-imports": "2.0.0",
- "babel-polyfill": "6.26.0",
- "clean-webpack-plugin": "3.0.0",
- "css-loader": "5.2.7",
- "cssnano": "5.1.15",
- "dotenv": "8.6.0",
- "dotenv-webpack": "7.1.1",
- "eslint": "8.38.0",
- "eslint-config-airbnb": "19.0.4",
- "eslint-config-airbnb-typescript": "^17.0.0",
- "eslint-plugin-import": "2.27.5",
- "eslint-plugin-jsx-a11y": "6.7.1",
- "eslint-plugin-react": "7.32.2",
- "eslint-plugin-react-hooks": "4.6.0",
- "file-loader": "6.2.0",
- "html-webpack-plugin": "5.5.0",
- "identity-obj-proxy": "3.0.0",
- "image-minimizer-webpack-plugin": "3.8.2",
- "jest": "26.6.3",
- "mini-css-extract-plugin": "1.6.2",
- "postcss": "8.4.21",
- "postcss-custom-media": "^9.1.2",
- "postcss-loader": "6.2.1",
- "postcss-rtlcss": "3.7.2",
- "react-dev-utils": "12.0.1",
- "react-refresh": "0.14.0",
- "resolve-url-loader": "5.0.0",
- "sass": "1.62.0",
- "sass-loader": "12.6.0",
- "sharp": "^0.32.0",
- "source-map-loader": "^4.0.1",
- "style-loader": "3.3.2",
- "ts-jest": "^26.5.0",
- "typescript": "^4.9.4",
- "url-loader": "4.1.1",
- "webpack": "5.79.0",
- "webpack-bundle-analyzer": "4.8.0",
- "webpack-cli": "5.0.1",
- "webpack-dev-server": "4.13.2",
- "webpack-merge": "5.8.0"
- },
- "dependencies": {
- "@types/html-minifier-terser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
- "dev": true
- },
- "@types/jest": {
- "version": "26.0.24",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz",
- "integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==",
- "dev": true,
- "requires": {
- "jest-diff": "^26.0.0",
- "pretty-format": "^26.0.0"
- }
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "clean-css": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
- "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==",
- "dev": true,
- "requires": {
- "source-map": "~0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "commander": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
- "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
- "dev": true
- },
- "cosmiconfig": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "dev": true,
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.7.2"
- }
- },
- "css-loader": {
- "version": "5.2.7",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
- "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
- "dev": true,
- "requires": {
- "icss-utils": "^5.1.0",
- "loader-utils": "^2.0.0",
- "postcss": "^8.2.15",
- "postcss-modules-extract-imports": "^3.0.0",
- "postcss-modules-local-by-default": "^4.0.0",
- "postcss-modules-scope": "^3.0.0",
- "postcss-modules-values": "^4.0.0",
- "postcss-value-parser": "^4.1.0",
- "schema-utils": "^3.0.0",
- "semver": "^7.3.5"
- },
- "dependencies": {
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "css-select": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "^6.0.1",
- "domhandler": "^4.3.1",
- "domutils": "^2.8.0",
- "nth-check": "^2.0.1"
- }
- },
- "cssnano": {
- "version": "5.1.15",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz",
- "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
- "dev": true,
- "requires": {
- "cssnano-preset-default": "^5.2.14",
- "lilconfig": "^2.0.3",
- "yaml": "^1.10.2"
- }
- },
- "cssnano-preset-default": {
- "version": "5.2.14",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
- "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
- "dev": true,
- "requires": {
- "css-declaration-sorter": "^6.3.1",
- "cssnano-utils": "^3.1.0",
- "postcss-calc": "^8.2.3",
- "postcss-colormin": "^5.3.1",
- "postcss-convert-values": "^5.1.3",
- "postcss-discard-comments": "^5.1.2",
- "postcss-discard-duplicates": "^5.1.0",
- "postcss-discard-empty": "^5.1.1",
- "postcss-discard-overridden": "^5.1.0",
- "postcss-merge-longhand": "^5.1.7",
- "postcss-merge-rules": "^5.1.4",
- "postcss-minify-font-values": "^5.1.0",
- "postcss-minify-gradients": "^5.1.1",
- "postcss-minify-params": "^5.1.4",
- "postcss-minify-selectors": "^5.2.1",
- "postcss-normalize-charset": "^5.1.0",
- "postcss-normalize-display-values": "^5.1.0",
- "postcss-normalize-positions": "^5.1.1",
- "postcss-normalize-repeat-style": "^5.1.1",
- "postcss-normalize-string": "^5.1.0",
- "postcss-normalize-timing-functions": "^5.1.0",
- "postcss-normalize-unicode": "^5.1.1",
- "postcss-normalize-url": "^5.1.0",
- "postcss-normalize-whitespace": "^5.1.1",
- "postcss-ordered-values": "^5.1.3",
- "postcss-reduce-initial": "^5.1.2",
- "postcss-reduce-transforms": "^5.1.0",
- "postcss-svgo": "^5.1.0",
- "postcss-unique-selectors": "^5.1.1"
- }
- },
- "cssnano-utils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
- "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
- "dev": true,
- "requires": {}
- },
- "diff-sequences": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
- "dev": true
- },
- "dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- }
- },
- "domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.2.0"
- }
- },
- "domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "dev": true,
- "requires": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- }
- },
- "dotenv": {
- "version": "8.6.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
- "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
- "dev": true
- },
- "dotenv-webpack": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-7.1.1.tgz",
- "integrity": "sha512-xw/19VqHDkXALtBOJAnnrSU/AZDIQRXczAmJyp0lZv6SH2aBLzUTl96W1MVryJZ7okZ+djZS4Gj4KlZ0xP7deA==",
- "dev": true,
- "requires": {
- "dotenv-defaults": "^2.0.2"
- }
- },
- "entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true
- },
- "filesize": {
- "version": "8.0.7",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
- "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==",
- "dev": true
- },
- "globby": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "dev": true,
- "requires": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.2.9",
- "ignore": "^5.2.0",
- "merge2": "^1.4.1",
- "slash": "^3.0.0"
- }
- },
- "gzip-size": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
- "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
- "dev": true,
- "requires": {
- "duplexer": "^0.1.2"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "html-minifier-terser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
- "dev": true,
- "requires": {
- "camel-case": "^4.1.2",
- "clean-css": "^5.2.2",
- "commander": "^8.3.0",
- "he": "^1.2.0",
- "param-case": "^3.0.4",
- "relateurl": "^0.2.7",
- "terser": "^5.10.0"
- }
- },
- "html-webpack-plugin": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
- "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
- "dev": true,
- "requires": {
- "@types/html-minifier-terser": "^6.0.0",
- "html-minifier-terser": "^6.0.2",
- "lodash": "^4.17.21",
- "pretty-error": "^4.0.0",
- "tapable": "^2.0.0"
- }
- },
- "htmlparser2": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
- "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.0.0",
- "domutils": "^2.5.2",
- "entities": "^2.0.0"
- }
- },
- "immer": {
- "version": "9.0.19",
- "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
- "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==",
- "dev": true
- },
- "jest-diff": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
- "dev": true
- },
- "mini-css-extract-plugin": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz",
- "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==",
- "dev": true,
- "requires": {
- "loader-utils": "^2.0.0",
- "schema-utils": "^3.0.0",
- "webpack-sources": "^1.1.0"
- }
- },
- "normalize-url": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
- "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
- "dev": true
- },
- "open": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
- "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
- "dev": true,
- "requires": {
- "define-lazy-prop": "^2.0.0",
- "is-docker": "^2.1.1",
- "is-wsl": "^2.2.0"
- }
- },
- "postcss": {
- "version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
- "dev": true,
- "requires": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- },
- "postcss-calc": {
- "version": "8.2.4",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
- "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
- "dev": true,
- "requires": {
- "postcss-selector-parser": "^6.0.9",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-colormin": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
- "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0",
- "colord": "^2.9.1",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-convert-values": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
- "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-custom-media": {
- "version": "9.1.4",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.4.tgz",
- "integrity": "sha512-4A7WEG3iIyKwfpxL5bkuSlHoHHGRTHl0212Z3uvpwJPyVfZJlkZAQNNgVC+oogrJgksDnfKyuuMbG6HafZPW8Q==",
- "dev": true,
- "requires": {
- "@csstools/cascade-layer-name-parser": "^1.0.2",
- "@csstools/css-parser-algorithms": "^2.1.1",
- "@csstools/css-tokenizer": "^2.1.1",
- "@csstools/media-query-list-parser": "^2.1.0"
- }
- },
- "postcss-discard-comments": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
- "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
- "dev": true,
- "requires": {}
- },
- "postcss-discard-duplicates": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
- "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
- "dev": true,
- "requires": {}
- },
- "postcss-discard-empty": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
- "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
- "dev": true,
- "requires": {}
- },
- "postcss-discard-overridden": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
- "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
- "dev": true,
- "requires": {}
- },
- "postcss-merge-longhand": {
- "version": "5.1.7",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
- "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0",
- "stylehacks": "^5.1.1"
- }
- },
- "postcss-merge-rules": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
- "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0",
- "cssnano-utils": "^3.1.0",
- "postcss-selector-parser": "^6.0.5"
- }
- },
- "postcss-minify-font-values": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
- "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-gradients": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
- "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
- "dev": true,
- "requires": {
- "colord": "^2.9.1",
- "cssnano-utils": "^3.1.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-params": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
- "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "cssnano-utils": "^3.1.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-selectors": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
- "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
- "dev": true,
- "requires": {
- "postcss-selector-parser": "^6.0.5"
- }
- },
- "postcss-normalize-charset": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
- "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
- "dev": true,
- "requires": {}
- },
- "postcss-normalize-display-values": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
- "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-positions": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
- "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-repeat-style": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
- "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-string": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
- "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-timing-functions": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
- "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-unicode": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
- "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-url": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
- "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
- "dev": true,
- "requires": {
- "normalize-url": "^6.0.1",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-whitespace": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
- "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-ordered-values": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
- "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
- "dev": true,
- "requires": {
- "cssnano-utils": "^3.1.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-reduce-initial": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
- "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0"
- }
- },
- "postcss-reduce-transforms": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
- "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-svgo": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
- "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
- "dev": true,
- "requires": {
- "postcss-value-parser": "^4.2.0",
- "svgo": "^2.7.0"
- }
- },
- "postcss-unique-selectors": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
- "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
- "dev": true,
- "requires": {
- "postcss-selector-parser": "^6.0.5"
- }
- },
- "pretty-error": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
- "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
- "dev": true,
- "requires": {
- "lodash": "^4.17.20",
- "renderkid": "^3.0.0"
- }
- },
- "react-dev-utils": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
- "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.16.0",
- "address": "^1.1.2",
- "browserslist": "^4.18.1",
- "chalk": "^4.1.2",
- "cross-spawn": "^7.0.3",
- "detect-port-alt": "^1.1.6",
- "escape-string-regexp": "^4.0.0",
- "filesize": "^8.0.6",
- "find-up": "^5.0.0",
- "fork-ts-checker-webpack-plugin": "^6.5.0",
- "global-modules": "^2.0.0",
- "globby": "^11.0.4",
- "gzip-size": "^6.0.0",
- "immer": "^9.0.7",
- "is-root": "^2.1.0",
- "loader-utils": "^3.2.0",
- "open": "^8.4.0",
- "pkg-up": "^3.1.0",
- "prompts": "^2.4.2",
- "react-error-overlay": "^6.0.11",
- "recursive-readdir": "^2.2.2",
- "shell-quote": "^1.7.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
- },
- "dependencies": {
- "fork-ts-checker-webpack-plugin": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz",
- "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@types/json-schema": "^7.0.5",
- "chalk": "^4.1.0",
- "chokidar": "^3.4.2",
- "cosmiconfig": "^6.0.0",
- "deepmerge": "^4.2.2",
- "fs-extra": "^9.0.0",
- "glob": "^7.1.6",
- "memfs": "^3.1.2",
- "minimatch": "^3.0.4",
- "schema-utils": "2.7.0",
- "semver": "^7.3.2",
- "tapable": "^1.0.0"
- }
- },
- "loader-utils": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
- "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
- "dev": true
- },
- "schema-utils": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
- "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
- "dev": true,
- "requires": {
- "@types/json-schema": "^7.0.4",
- "ajv": "^6.12.2",
- "ajv-keywords": "^3.4.1"
- }
- },
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
- "tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
- "dev": true
- }
- }
- },
- "renderkid": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
- "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
- "dev": true,
- "requires": {
- "css-select": "^4.1.3",
- "dom-converter": "^0.2.0",
- "htmlparser2": "^6.1.0",
- "lodash": "^4.17.21",
- "strip-ansi": "^6.0.1"
- }
- },
- "shell-quote": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz",
- "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==",
- "dev": true
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- },
- "stylehacks": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
- "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
- "dev": true,
- "requires": {
- "browserslist": "^4.21.4",
- "postcss-selector-parser": "^6.0.4"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
- },
- "terser": {
- "version": "5.15.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
- "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
- "dev": true,
- "requires": {
- "@jridgewell/source-map": "^0.3.2",
- "acorn": "^8.5.0",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- }
- }
- },
- "webpack-merge": {
- "version": "5.8.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
- "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
- "dev": true,
- "requires": {
- "clone-deep": "^4.0.1",
- "wildcard": "^2.0.0"
- }
- }
- }
- },
- "@edx/frontend-enterprise-catalog-search": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-catalog-search/-/frontend-enterprise-catalog-search-3.1.5.tgz",
- "integrity": "sha512-CDthbmT5PGbY7NfQ7+VZifr3NJEvFjARfpZP3p1Yxthxjy3dYxQCozC3BaZJBlozeO3xYf6g3a8BfZ7HepHMYA==",
- "requires": {
- "@edx/frontend-enterprise-utils": "^2.2.0",
- "classnames": "2.2.5",
- "lodash.debounce": "4.0.8",
- "prop-types": "15.7.2"
- },
- "dependencies": {
- "@edx/frontend-enterprise-utils": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-2.2.0.tgz",
- "integrity": "sha512-5cAFO0vk4RIbprdIqi6Fq1nJcZHC+1wH3APSj8R+yIrJW1oWY4z6lnqHjnbsmyFQdECs3WCMsA43bRTLPgdnOw==",
- "requires": {
- "@testing-library/react": "11.2.6",
- "history": "4.10.1"
- }
- },
- "@testing-library/react": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz",
- "integrity": "sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==",
- "requires": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^7.28.1"
- }
- },
- "classnames": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
- "integrity": "sha512-DTt3GhOUDKhh4ONwIJW4lmhyotQmV2LjNlGK/J2hkwUcqcbKkCLAdJPtxQnxnlc7SR3f1CEXCyMmc7WLUsWbNA=="
- }
- }
- },
- "@edx/frontend-enterprise-hotjar": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-hotjar/-/frontend-enterprise-hotjar-1.2.0.tgz",
- "integrity": "sha512-VkoRaUcCePKIRn0j4ODR7aTu5iyKtNjssAfr7m7NHrr/d0YjpFPUtNMqxxjsOZ5rySOgpdb6ibQ9TpbyNJ5qlQ==",
- "requires": {}
- },
- "@edx/frontend-enterprise-logistration": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-logistration/-/frontend-enterprise-logistration-2.1.0.tgz",
- "integrity": "sha512-AP6aUYnLsy+lH3Hz0OGpJtMOTx5+BAeVMdIFtXXo17HlcqR+TFNrmdNN6ZIIomgsSAFmBNknVyrrcLhD/wJktg==",
- "requires": {
- "@edx/frontend-enterprise-utils": "^2.1.0",
- "prop-types": "15.7.2"
- }
- },
- "@edx/frontend-enterprise-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-2.1.0.tgz",
- "integrity": "sha512-lfGwpfOnNO7oe3p8YUXhjzA2kDYz0eYZk4eeY+SYib6oRXDE+nbZXd+IU9jhd/YyZnPAAAFRY3JYwNQ26Go+0Q==",
- "requires": {
- "@testing-library/react": "11.2.6",
- "history": "4.10.1"
- },
- "dependencies": {
- "@testing-library/react": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz",
- "integrity": "sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==",
- "requires": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^7.28.1"
- }
- }
- }
- },
- "@edx/frontend-platform": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.4.0.tgz",
- "integrity": "sha512-i5z6cg+A+OLHgpvqPzU2jd7VM2soHsOAb8Jk6t4ddAtN1gLDad/pFhty3+aBEPIsnTuGDmrtUl5Yl/hP8Muc+w==",
- "requires": {
- "@cospired/i18n-iso-languages": "2.2.0",
- "@formatjs/intl-pluralrules": "^4.3.3",
- "@formatjs/intl-relativetimeformat": "^10.0.1",
- "axios": "0.26.1",
- "axios-cache-adapter": "2.7.3",
- "form-urlencoded": "4.1.4",
- "glob": "7.2.0",
- "history": "4.10.1",
- "i18n-iso-countries": "4.3.1",
- "jwt-decode": "3.1.2",
- "localforage": "1.10.0",
- "localforage-memoryStorageDriver": "0.9.2",
- "lodash.camelcase": "4.3.0",
- "lodash.memoize": "4.1.2",
- "lodash.merge": "4.6.2",
- "lodash.snakecase": "4.1.1",
- "pubsub-js": "1.9.4",
- "react-intl": "^5.25.0",
- "universal-cookie": "4.0.4"
- },
- "dependencies": {
- "axios": {
- "version": "0.26.1",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
- "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
- "requires": {
- "follow-redirects": "^1.14.8"
- }
- },
- "glob": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
- }
- },
- "@edx/new-relic-source-map-webpack-plugin": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.0.2.tgz",
- "integrity": "sha512-jwu9WjRtEbv0rvPHGCnhbQbbv6+DTADShj43NQVsuAyD823znjutgoHnlc+9HIOiYiIxjz/wIMcGVwjrTnMceQ==",
- "dev": true,
- "requires": {
- "@newrelic/publish-sourcemap": "^5.0.1"
- }
- },
- "@edx/paragon": {
- "version": "20.39.2",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.39.2.tgz",
- "integrity": "sha512-SvJskMG+hjRAoteR+dhjXIFAAgMk4IgnDA4gvBomxOk/D+zV+E3mebEoslX2Qx+krRLSwmfQLWhWYn4qlps/5w==",
- "requires": {
- "@fortawesome/fontawesome-svg-core": "^6.1.1",
- "@fortawesome/react-fontawesome": "^0.1.18",
- "@popperjs/core": "^2.11.4",
- "bootstrap": "^4.6.2",
- "classnames": "^2.3.1",
- "email-prop-type": "^3.0.0",
- "file-selector": "^0.6.0",
- "font-awesome": "^4.7.0",
- "glob": "^8.0.3",
- "lodash.uniqby": "^4.7.0",
- "mailto-link": "^2.0.0",
- "prop-types": "^15.8.1",
- "react-bootstrap": "^1.6.5",
- "react-colorful": "^5.6.1",
- "react-dropzone": "^14.2.1",
- "react-focus-on": "^3.5.4",
- "react-loading-skeleton": "^3.1.0",
- "react-popper": "^2.2.5",
- "react-proptype-conditional-require": "^1.0.4",
- "react-responsive": "^8.2.0",
- "react-table": "^7.7.0",
- "react-transition-group": "^4.4.2",
- "tabbable": "^5.3.3",
- "uncontrollable": "^7.2.1",
- "uuid": "^9.0.0"
- },
- "dependencies": {
- "@fortawesome/fontawesome-common-types": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz",
- "integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ=="
- },
- "@fortawesome/fontawesome-svg-core": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz",
- "integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==",
- "requires": {
- "@fortawesome/fontawesome-common-types": "6.2.1"
- }
- },
- "@fortawesome/react-fontawesome": {
- "version": "0.1.19",
- "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz",
- "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==",
- "requires": {
- "prop-types": "^15.8.1"
- }
- },
- "brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "requires": {
- "balanced-match": "^1.0.0"
- }
- },
- "classnames": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
- "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
- },
- "glob": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
- "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- }
- },
- "minimatch": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
- "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
- "requires": {
- "brace-expansion": "^2.0.1"
- }
- },
- "prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- }
- }
- },
- "@edx/typescript-config": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@edx/typescript-config/-/typescript-config-1.0.1.tgz",
- "integrity": "sha512-w0g3nIX9oEch8Rip8q8sb/nrurGEHA1BEjK/I1LAQwA44K4FPMWvyvabmZErrdTJ9sXcZL10aWD3bat1obV8Bg==",
- "dev": true,
- "requires": {}
- },
- "@eslint-community/eslint-utils": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
- "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
- "requires": {
- "eslint-visitor-keys": "^3.3.0"
- }
- },
- "@eslint-community/regexpp": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz",
- "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ=="
- },
- "@eslint/eslintrc": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz",
- "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==",
- "requires": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^9.5.2",
- "globals": "^13.19.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "dependencies": {
- "argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
- },
- "globals": {
- "version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
- "requires": {
- "type-fest": "^0.20.2"
- }
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
- }
- }
- },
- "@eslint/js": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
- "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g=="
- },
- "@faker-js/faker": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz",
- "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==",
- "dev": true
- },
- "@formatjs/ecma402-abstract": {
- "version": "1.11.4",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
- "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
- "requires": {
- "@formatjs/intl-localematcher": "0.2.25",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/fast-memoize": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
- "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
- "requires": {
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/icu-messageformat-parser": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz",
- "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/icu-skeleton-parser": "1.3.6",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/icu-skeleton-parser": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz",
- "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/intl": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz",
- "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/fast-memoize": "1.2.1",
- "@formatjs/icu-messageformat-parser": "2.1.0",
- "@formatjs/intl-displaynames": "5.4.3",
- "@formatjs/intl-listformat": "6.5.3",
- "intl-messageformat": "9.13.0",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/intl-displaynames": {
- "version": "5.4.3",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz",
- "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/intl-localematcher": "0.2.25",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/intl-listformat": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz",
- "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/intl-localematcher": "0.2.25",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/intl-localematcher": {
- "version": "0.2.25",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
- "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
- "requires": {
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/intl-numberformat": {
- "version": "5.7.6",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz",
- "integrity": "sha512-ZlZfYtvbVHYZY5OG3RXizoCwxKxEKOrzEe2YOw9wbzoxF3PmFn0SAgojCFGLyNXkkR6xVxlylhbuOPf1dkIVNg==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.4.0",
- "tslib": "^2.0.1"
- },
- "dependencies": {
- "@formatjs/ecma402-abstract": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz",
- "integrity": "sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==",
- "requires": {
- "tslib": "^2.0.1"
- }
- }
- }
- },
- "@formatjs/intl-pluralrules": {
- "version": "4.3.3",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz",
- "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/intl-localematcher": "0.2.25",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/intl-relativetimeformat": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz",
- "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/intl-localematcher": "0.2.25",
- "tslib": "^2.1.0"
- }
- },
- "@formatjs/ts-transformer": {
- "version": "2.13.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz",
- "integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==",
- "requires": {
- "intl-messageformat-parser": "6.1.2",
- "tslib": "^2.0.1",
- "typescript": "^4.0"
- },
- "dependencies": {
- "@formatjs/ecma402-abstract": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz",
- "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==",
- "requires": {
- "tslib": "^2.0.1"
- }
- },
- "intl-messageformat-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz",
- "integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.5.0",
- "tslib": "^2.0.1"
- }
- }
- }
- },
- "@fortawesome/fontawesome-common-types": {
- "version": "0.2.36",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
- "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg=="
- },
- "@fortawesome/fontawesome-svg-core": {
- "version": "1.2.35",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz",
- "integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==",
- "requires": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- }
- },
- "@fortawesome/free-brands-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.3.tgz",
- "integrity": "sha512-1hirPcbjj72ZJtFvdnXGPbAbpn3Ox6mH3g5STbANFp3vGSiE5u5ingAKV06mK6ZVqNYxUPlh4DlTnaIvLtF2kw==",
- "requires": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- }
- },
- "@fortawesome/free-regular-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz",
- "integrity": "sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ==",
- "requires": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- }
- },
- "@fortawesome/free-solid-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz",
- "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==",
- "requires": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- }
- },
- "@fortawesome/react-fontawesome": {
- "version": "0.1.14",
- "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
- "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
- "requires": {
- "prop-types": "^15.7.2"
- }
- },
- "@fullhuman/postcss-purgecss": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz",
- "integrity": "sha512-onDS/b/2pMRzqSoj4qOs2tYFmOpaspjTAgvACIHMPiicu1ptajiBruTrjBzTKdxWdX0ldaBb7wj8nEaTLyFkJw==",
- "requires": {
- "purgecss": "^5.0.0"
- }
- },
- "@humanwhocodes/config-array": {
- "version": "0.11.8",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
- "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
- "requires": {
- "@humanwhocodes/object-schema": "^1.2.1",
- "debug": "^4.1.1",
- "minimatch": "^3.0.5"
- }
- },
- "@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
- },
- "@humanwhocodes/object-schema": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
- "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
- },
- "@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
- "requires": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "dependencies": {
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "requires": {
- "p-limit": "^2.2.0"
- }
- }
- }
- },
- "@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="
- },
- "@jest/console": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz",
- "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==",
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "jest-message-util": "^26.6.2",
- "jest-util": "^26.6.2",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jest/core": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz",
- "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==",
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/reporters": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.4",
- "jest-changed-files": "^26.6.2",
- "jest-config": "^26.6.3",
- "jest-haste-map": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-resolve": "^26.6.2",
- "jest-resolve-dependencies": "^26.6.3",
- "jest-runner": "^26.6.3",
- "jest-runtime": "^26.6.3",
- "jest-snapshot": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "jest-watcher": "^26.6.2",
- "micromatch": "^4.0.2",
- "p-each-series": "^2.1.0",
- "rimraf": "^3.0.0",
- "slash": "^3.0.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jest/environment": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz",
- "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==",
- "requires": {
- "@jest/fake-timers": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "jest-mock": "^26.6.2"
- }
- },
- "@jest/expect-utils": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz",
- "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==",
- "dev": true,
- "requires": {
- "jest-get-type": "^29.2.0"
- }
- },
- "@jest/fake-timers": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz",
- "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==",
- "requires": {
- "@jest/types": "^26.6.2",
- "@sinonjs/fake-timers": "^6.0.1",
- "@types/node": "*",
- "jest-message-util": "^26.6.2",
- "jest-mock": "^26.6.2",
- "jest-util": "^26.6.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jest/globals": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz",
- "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==",
- "requires": {
- "@jest/environment": "^26.6.2",
- "@jest/types": "^26.6.2",
- "expect": "^26.6.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "diff-sequences": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q=="
- },
- "expect": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-styles": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-regex-util": "^26.0.0"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-diff": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- },
- "jest-matcher-utils": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jest/reporters": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz",
- "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==",
- "requires": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.2",
- "graceful-fs": "^4.2.4",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^4.0.3",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.0.2",
- "jest-haste-map": "^26.6.2",
- "jest-resolve": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-worker": "^26.6.2",
- "node-notifier": "^8.0.0",
- "slash": "^3.0.0",
- "source-map": "^0.6.0",
- "string-length": "^4.0.1",
- "terminal-link": "^2.0.0",
- "v8-to-istanbul": "^7.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "istanbul-lib-instrument": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
- "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
- "requires": {
- "@babel/core": "^7.7.5",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.0.0",
- "semver": "^6.3.0"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jest/schemas": {
- "version": "29.0.0",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz",
- "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==",
- "dev": true,
- "requires": {
- "@sinclair/typebox": "^0.24.1"
- }
- },
- "@jest/source-map": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz",
- "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==",
- "requires": {
- "callsites": "^3.0.0",
- "graceful-fs": "^4.2.4",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "@jest/test-result": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz",
- "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==",
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "collect-v8-coverage": "^1.0.0"
- }
- },
- "@jest/test-sequencer": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz",
- "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==",
- "requires": {
- "@jest/test-result": "^26.6.2",
- "graceful-fs": "^4.2.4",
- "jest-haste-map": "^26.6.2",
- "jest-runner": "^26.6.3",
- "jest-runtime": "^26.6.3"
- }
- },
- "@jest/transform": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz",
- "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==",
- "requires": {
- "@babel/core": "^7.1.0",
- "@jest/types": "^26.6.2",
- "babel-plugin-istanbul": "^6.0.0",
- "chalk": "^4.0.0",
- "convert-source-map": "^1.4.0",
- "fast-json-stable-stringify": "^2.0.0",
- "graceful-fs": "^4.2.4",
- "jest-haste-map": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-util": "^26.6.2",
- "micromatch": "^4.0.2",
- "pirates": "^4.0.1",
- "slash": "^3.0.0",
- "source-map": "^0.6.1",
- "write-file-atomic": "^3.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jest/types": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@jridgewell/gen-mapping": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
- "requires": {
- "@jridgewell/set-array": "^1.0.0",
- "@jridgewell/sourcemap-codec": "^1.4.10"
- }
- },
- "@jridgewell/resolve-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
- },
- "@jridgewell/set-array": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
- "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
- },
- "@jridgewell/source-map": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
- "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
- "requires": {
- "@jridgewell/gen-mapping": "^0.3.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- },
- "dependencies": {
- "@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "requires": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- }
- }
- },
- "@jridgewell/sourcemap-codec": {
- "version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
- },
- "@jridgewell/trace-mapping": {
- "version": "0.3.17",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
- "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
- "requires": {
- "@jridgewell/resolve-uri": "3.1.0",
- "@jridgewell/sourcemap-codec": "1.4.14"
- }
- },
- "@leichtgewicht/ip-codec": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
- "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
- },
- "@newrelic/publish-sourcemap": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@newrelic/publish-sourcemap/-/publish-sourcemap-5.1.0.tgz",
- "integrity": "sha512-pOpW0InKZp/DXUmD3h6vaCGdtMDY5LyzzKvq3S3MBwTKm5Qc5ka3yZC73sLAMOXnjKZmdyG3d8A5LC+LawOEpA==",
- "requires": {
- "superagent": "^3.4.1",
- "yargs": "^16.0.3"
- }
- },
- "@nicolo-ribaudo/chokidar-2": {
- "version": "2.1.8-no-fsevents.3",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
- "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
- "optional": true
- },
- "@nicolo-ribaudo/eslint-scope-5-internals": {
- "version": "5.1.1-v1",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
- "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
- "peer": true,
- "requires": {
- "eslint-scope": "5.1.1"
- }
- },
- "@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "requires": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- }
- },
- "@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
- },
- "@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "requires": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- }
- },
- "@pmmmwh/react-refresh-webpack-plugin": {
- "version": "0.5.10",
- "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz",
- "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==",
- "requires": {
- "ansi-html-community": "^0.0.8",
- "common-path-prefix": "^3.0.0",
- "core-js-pure": "^3.23.3",
- "error-stack-parser": "^2.0.6",
- "find-up": "^5.0.0",
- "html-entities": "^2.1.0",
- "loader-utils": "^2.0.4",
- "schema-utils": "^3.0.0",
- "source-map": "^0.7.3"
- },
- "dependencies": {
- "source-map": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
- }
- }
- },
- "@polka/url": {
- "version": "1.0.0-next.21",
- "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz",
- "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g=="
- },
- "@popperjs/core": {
- "version": "2.11.6",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
- "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
- },
- "@restart/context": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
- "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==",
- "requires": {}
- },
- "@restart/hooks": {
- "version": "0.4.7",
- "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz",
- "integrity": "sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==",
- "requires": {
- "dequal": "^2.0.2"
- }
- },
- "@sinclair/typebox": {
- "version": "0.24.51",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
- "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==",
- "dev": true
- },
- "@sinonjs/commons": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz",
- "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==",
- "requires": {
- "type-detect": "4.0.8"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
- "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@svgr/babel-plugin-add-jsx-attribute": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz",
- "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==",
- "dev": true,
- "requires": {}
- },
- "@svgr/babel-plugin-remove-jsx-attribute": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
- "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
- "requires": {}
- },
- "@svgr/babel-plugin-remove-jsx-empty-expression": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
- "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
- "requires": {}
- },
- "@svgr/babel-plugin-replace-jsx-attribute-value": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz",
- "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==",
- "dev": true,
- "requires": {}
- },
- "@svgr/babel-plugin-svg-dynamic-title": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz",
- "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==",
- "dev": true,
- "requires": {}
- },
- "@svgr/babel-plugin-svg-em-dimensions": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz",
- "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==",
- "dev": true,
- "requires": {}
- },
- "@svgr/babel-plugin-transform-react-native-svg": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz",
- "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==",
- "dev": true,
- "requires": {}
- },
- "@svgr/babel-plugin-transform-svg-component": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz",
- "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==",
- "dev": true,
- "requires": {}
- },
- "@svgr/babel-preset": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz",
- "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==",
- "dev": true,
- "requires": {
- "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1",
- "@svgr/babel-plugin-remove-jsx-attribute": "*",
- "@svgr/babel-plugin-remove-jsx-empty-expression": "*",
- "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1",
- "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1",
- "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1",
- "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1",
- "@svgr/babel-plugin-transform-svg-component": "^6.5.1"
- }
- },
- "@svgr/core": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz",
- "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.19.6",
- "@svgr/babel-preset": "^6.5.1",
- "@svgr/plugin-jsx": "^6.5.1",
- "camelcase": "^6.2.0",
- "cosmiconfig": "^7.0.1"
- }
- },
- "@svgr/hast-util-to-babel-ast": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz",
- "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.20.0",
- "entities": "^4.4.0"
- }
- },
- "@svgr/plugin-jsx": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz",
- "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.19.6",
- "@svgr/babel-preset": "^6.5.1",
- "@svgr/hast-util-to-babel-ast": "^6.5.1",
- "svg-parser": "^2.0.4"
- }
- },
- "@svgr/plugin-svgo": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz",
- "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==",
- "dev": true,
- "requires": {
- "cosmiconfig": "^7.0.1",
- "deepmerge": "^4.2.2",
- "svgo": "^2.8.0"
- }
- },
- "@svgr/webpack": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz",
- "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.19.6",
- "@babel/plugin-transform-react-constant-elements": "^7.18.12",
- "@babel/preset-env": "^7.19.4",
- "@babel/preset-react": "^7.18.6",
- "@babel/preset-typescript": "^7.18.6",
- "@svgr/core": "^6.5.1",
- "@svgr/plugin-jsx": "^6.5.1",
- "@svgr/plugin-svgo": "^6.5.1"
- }
- },
- "@testing-library/dom": {
- "version": "7.31.2",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
- "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
- "requires": {
- "@babel/code-frame": "^7.10.4",
- "@babel/runtime": "^7.12.5",
- "@types/aria-query": "^4.2.0",
- "aria-query": "^4.2.2",
- "chalk": "^4.1.0",
- "dom-accessibility-api": "^0.5.6",
- "lz-string": "^1.4.4",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@testing-library/jest-dom": {
- "version": "5.11.9",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz",
- "integrity": "sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.9.2",
- "@types/testing-library__jest-dom": "^5.9.1",
- "aria-query": "^4.2.2",
- "chalk": "^3.0.0",
- "css": "^3.0.0",
- "css.escape": "^1.5.1",
- "lodash": "^4.17.15",
- "redent": "^3.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "@testing-library/react": {
- "version": "11.2.7",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.7.tgz",
- "integrity": "sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^7.28.1"
- }
- },
- "@testing-library/react-hooks": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-5.0.3.tgz",
- "integrity": "sha512-UrnnRc5II7LMH14xsYNm/WRch/67cBafmrSQcyFh0v+UUmSf1uzfB7zn5jQXSettGwOSxJwdQUN7PgkT0w22Lg==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.12.5",
- "@types/react": ">=16.9.0",
- "@types/react-dom": ">=16.9.0",
- "@types/react-test-renderer": ">=16.9.0",
- "filter-console": "^0.1.1",
- "react-error-boundary": "^3.1.0"
- }
- },
- "@testing-library/user-event": {
- "version": "12.8.3",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz",
- "integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.12.5"
- }
- },
- "@tootallnate/once": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
- "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
- },
- "@trysound/sax": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="
- },
- "@types/aria-query": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="
- },
- "@types/babel__core": {
- "version": "7.1.20",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz",
- "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==",
- "requires": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
- },
- "@types/babel__generator": {
- "version": "7.6.4",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
- "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
- "requires": {
- "@babel/types": "^7.0.0"
- }
- },
- "@types/babel__template": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
- "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
- "requires": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "@types/babel__traverse": {
- "version": "7.18.2",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz",
- "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==",
- "requires": {
- "@babel/types": "^7.3.0"
- }
- },
- "@types/body-parser": {
- "version": "1.19.2",
- "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
- "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
- "requires": {
- "@types/connect": "*",
- "@types/node": "*"
- }
- },
- "@types/bonjour": {
- "version": "3.5.10",
- "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz",
- "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/connect": {
- "version": "3.4.35",
- "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
- "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/connect-history-api-fallback": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz",
- "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==",
- "requires": {
- "@types/express-serve-static-core": "*",
- "@types/node": "*"
- }
- },
- "@types/cookie": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
- "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
- },
- "@types/eslint": {
- "version": "8.4.10",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
- "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
- "requires": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
- "@types/eslint-scope": {
- "version": "3.7.4",
- "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
- "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
- "requires": {
- "@types/eslint": "*",
- "@types/estree": "*"
- }
- },
- "@types/estree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
- "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
- },
- "@types/express": {
- "version": "4.17.16",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.16.tgz",
- "integrity": "sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==",
- "requires": {
- "@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.31",
- "@types/qs": "*",
- "@types/serve-static": "*"
- }
- },
- "@types/express-serve-static-core": {
- "version": "4.17.33",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
- "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
- "requires": {
- "@types/node": "*",
- "@types/qs": "*",
- "@types/range-parser": "*"
- }
- },
- "@types/fs-extra": {
- "version": "9.0.13",
- "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
- "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/glob": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
- "requires": {
- "@types/minimatch": "*",
- "@types/node": "*"
- }
- },
- "@types/graceful-fs": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
- "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/hast": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
- "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
- "requires": {
- "@types/unist": "*"
- }
- },
- "@types/hoist-non-react-statics": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
- "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
- "requires": {
- "@types/react": "*",
- "hoist-non-react-statics": "^3.3.0"
- }
- },
- "@types/html-minifier-terser": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz",
- "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==",
- "peer": true
- },
- "@types/http-proxy": {
- "version": "1.17.9",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
- "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/invariant": {
- "version": "2.2.35",
- "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
- "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
- },
- "@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g=="
- },
- "@types/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
- "requires": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "@types/istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "@types/jest": {
- "version": "29.2.3",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.3.tgz",
- "integrity": "sha512-6XwoEbmatfyoCjWRX7z0fKMmgYKe9+/HrviJ5k0X/tjJWHGAezZOfYaxqQKuzG/TvQyr+ktjm4jgbk0s4/oF2w==",
- "dev": true,
- "requires": {
- "expect": "^29.0.0",
- "pretty-format": "^29.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- },
- "pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.0.0",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- }
- },
- "react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- }
- }
- },
- "@types/json-schema": {
- "version": "7.0.11",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
- "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
- },
- "@types/json5": {
- "version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
- "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
- },
- "@types/mdast": {
- "version": "3.0.10",
- "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
- "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==",
- "requires": {
- "@types/unist": "*"
- }
- },
- "@types/mime": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
- "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
- },
- "@types/minimatch": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
- "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA=="
- },
- "@types/node": {
- "version": "18.11.9",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
- "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg=="
- },
- "@types/normalize-package-data": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
- "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
- },
- "@types/parse-json": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
- },
- "@types/prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow=="
- },
- "@types/prop-types": {
- "version": "15.7.5",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
- "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
- },
- "@types/qs": {
- "version": "6.9.7",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
- "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
- },
- "@types/range-parser": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
- "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
- },
- "@types/react": {
- "version": "18.0.25",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz",
- "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==",
- "requires": {
- "@types/prop-types": "*",
- "@types/scheduler": "*",
- "csstype": "^3.0.2"
- }
- },
- "@types/react-dom": {
- "version": "18.0.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz",
- "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==",
- "dev": true,
- "requires": {
- "@types/react": "*"
- }
- },
- "@types/react-redux": {
- "version": "7.1.24",
- "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
- "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==",
- "requires": {
- "@types/hoist-non-react-statics": "^3.3.0",
- "@types/react": "*",
- "hoist-non-react-statics": "^3.3.0",
- "redux": "^4.0.0"
- }
- },
- "@types/react-test-renderer": {
- "version": "18.0.0",
- "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz",
- "integrity": "sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ==",
- "dev": true,
- "requires": {
- "@types/react": "*"
- }
- },
- "@types/react-transition-group": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
- "requires": {
- "@types/react": "*"
- }
- },
- "@types/retry": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
- "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
- },
- "@types/scheduler": {
- "version": "0.16.2",
- "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
- "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
- },
- "@types/schema-utils": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@types/schema-utils/-/schema-utils-2.4.0.tgz",
- "integrity": "sha512-454hrj5gz/FXcUE20ygfEiN4DxZ1sprUo0V1gqIqkNZ/CzoEzAZEll2uxMsuyz6BYjiQan4Aa65xbTemfzW9hQ==",
- "requires": {
- "schema-utils": "*"
- }
- },
- "@types/semver": {
- "version": "7.5.0",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
- "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
- "dev": true
- },
- "@types/serve-index": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==",
- "requires": {
- "@types/express": "*"
- }
- },
- "@types/serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
- "requires": {
- "@types/mime": "*",
- "@types/node": "*"
- }
- },
- "@types/sockjs": {
- "version": "0.3.33",
- "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
- "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/source-list-map": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
- "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA=="
- },
- "@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
- },
- "@types/tapable": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
- "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ=="
- },
- "@types/testing-library__jest-dom": {
- "version": "5.14.5",
- "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz",
- "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==",
- "dev": true,
- "requires": {
- "@types/jest": "*"
- }
- },
- "@types/uglify-js": {
- "version": "3.17.1",
- "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz",
- "integrity": "sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==",
- "requires": {
- "source-map": "^0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "@types/unist": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
- "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
- },
- "@types/warning": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
- "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA=="
- },
- "@types/webpack": {
- "version": "4.41.33",
- "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz",
- "integrity": "sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g==",
- "requires": {
- "@types/node": "*",
- "@types/tapable": "^1",
- "@types/uglify-js": "*",
- "@types/webpack-sources": "*",
- "anymatch": "^3.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "@types/webpack-sources": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz",
- "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==",
- "requires": {
- "@types/node": "*",
- "@types/source-list-map": "*",
- "source-map": "^0.7.3"
- },
- "dependencies": {
- "source-map": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
- }
- }
- },
- "@types/ws": {
- "version": "8.5.4",
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
- "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/yargs": {
- "version": "15.0.14",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz",
- "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==",
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "@types/yargs-parser": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA=="
- },
- "@typescript-eslint/eslint-plugin": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz",
- "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==",
- "dev": true,
- "requires": {
- "@eslint-community/regexpp": "^4.4.0",
- "@typescript-eslint/scope-manager": "5.59.9",
- "@typescript-eslint/type-utils": "5.59.9",
- "@typescript-eslint/utils": "5.59.9",
- "debug": "^4.3.4",
- "grapheme-splitter": "^1.0.4",
- "ignore": "^5.2.0",
- "natural-compare-lite": "^1.4.0",
- "semver": "^7.3.7",
- "tsutils": "^3.21.0"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "@typescript-eslint/parser": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz",
- "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==",
- "dev": true,
- "requires": {
- "@typescript-eslint/scope-manager": "5.59.9",
- "@typescript-eslint/types": "5.59.9",
- "@typescript-eslint/typescript-estree": "5.59.9",
- "debug": "^4.3.4"
- }
- },
- "@typescript-eslint/scope-manager": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz",
- "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==",
- "dev": true,
- "requires": {
- "@typescript-eslint/types": "5.59.9",
- "@typescript-eslint/visitor-keys": "5.59.9"
- }
- },
- "@typescript-eslint/type-utils": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz",
- "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==",
- "dev": true,
- "requires": {
- "@typescript-eslint/typescript-estree": "5.59.9",
- "@typescript-eslint/utils": "5.59.9",
- "debug": "^4.3.4",
- "tsutils": "^3.21.0"
- }
- },
- "@typescript-eslint/types": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz",
- "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==",
- "dev": true
- },
- "@typescript-eslint/typescript-estree": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz",
- "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==",
- "dev": true,
- "requires": {
- "@typescript-eslint/types": "5.59.9",
- "@typescript-eslint/visitor-keys": "5.59.9",
- "debug": "^4.3.4",
- "globby": "^11.1.0",
- "is-glob": "^4.0.3",
- "semver": "^7.3.7",
- "tsutils": "^3.21.0"
- },
- "dependencies": {
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true
- },
- "globby": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "dev": true,
- "requires": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.2.9",
- "ignore": "^5.2.0",
- "merge2": "^1.4.1",
- "slash": "^3.0.0"
- }
- },
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- }
- }
- },
- "@typescript-eslint/utils": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz",
- "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==",
- "dev": true,
- "requires": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@types/json-schema": "^7.0.9",
- "@types/semver": "^7.3.12",
- "@typescript-eslint/scope-manager": "5.59.9",
- "@typescript-eslint/types": "5.59.9",
- "@typescript-eslint/typescript-estree": "5.59.9",
- "eslint-scope": "^5.1.1",
- "semver": "^7.3.7"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "@typescript-eslint/visitor-keys": {
- "version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz",
- "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==",
- "dev": true,
- "requires": {
- "@typescript-eslint/types": "5.59.9",
- "eslint-visitor-keys": "^3.3.0"
- }
- },
- "@webassemblyjs/ast": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
- "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
- "requires": {
- "@webassemblyjs/helper-numbers": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
- }
- },
- "@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
- "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
- },
- "@webassemblyjs/helper-api-error": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
- "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
- },
- "@webassemblyjs/helper-buffer": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
- "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
- },
- "@webassemblyjs/helper-numbers": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
- "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
- "requires": {
- "@webassemblyjs/floating-point-hex-parser": "1.11.1",
- "@webassemblyjs/helper-api-error": "1.11.1",
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
- "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
- },
- "@webassemblyjs/helper-wasm-section": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
- "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-buffer": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/wasm-gen": "1.11.1"
- }
- },
- "@webassemblyjs/ieee754": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
- "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
- "requires": {
- "@xtuc/ieee754": "^1.2.0"
- }
- },
- "@webassemblyjs/leb128": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
- "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
- "requires": {
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/utf8": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
- "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
- },
- "@webassemblyjs/wasm-edit": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
- "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-buffer": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/helper-wasm-section": "1.11.1",
- "@webassemblyjs/wasm-gen": "1.11.1",
- "@webassemblyjs/wasm-opt": "1.11.1",
- "@webassemblyjs/wasm-parser": "1.11.1",
- "@webassemblyjs/wast-printer": "1.11.1"
- }
- },
- "@webassemblyjs/wasm-gen": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
- "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/ieee754": "1.11.1",
- "@webassemblyjs/leb128": "1.11.1",
- "@webassemblyjs/utf8": "1.11.1"
- }
- },
- "@webassemblyjs/wasm-opt": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
- "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-buffer": "1.11.1",
- "@webassemblyjs/wasm-gen": "1.11.1",
- "@webassemblyjs/wasm-parser": "1.11.1"
- }
- },
- "@webassemblyjs/wasm-parser": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
- "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-api-error": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/ieee754": "1.11.1",
- "@webassemblyjs/leb128": "1.11.1",
- "@webassemblyjs/utf8": "1.11.1"
- }
- },
- "@webassemblyjs/wast-printer": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
- "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@xtuc/long": "4.2.2"
- }
- },
- "@webpack-cli/configtest": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
- "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
- "requires": {}
- },
- "@webpack-cli/info": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
- "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
- "requires": {}
- },
- "@webpack-cli/serve": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
- "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
- "requires": {}
- },
- "@xtuc/ieee754": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
- "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
- },
- "@xtuc/long": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
- "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
- },
- "abab": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
- "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
- },
- "accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "requires": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- }
- },
- "acorn": {
- "version": "8.8.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
- "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw=="
- },
- "acorn-globals": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
- "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
- "requires": {
- "acorn": "^7.1.1",
- "acorn-walk": "^7.1.1"
- },
- "dependencies": {
- "acorn": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
- "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
- }
- }
- },
- "acorn-import-assertions": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
- "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
- "requires": {}
- },
- "acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "requires": {}
- },
- "acorn-walk": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
- "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
- },
- "address": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz",
- "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA=="
- },
- "adjust-sourcemap-loader": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
- "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
- "requires": {
- "loader-utils": "^2.0.0",
- "regex-parser": "^2.2.11"
- }
- },
- "agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "requires": {
- "debug": "4"
- }
- },
- "airbnb-prop-types": {
- "version": "2.16.0",
- "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz",
- "integrity": "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==",
- "dev": true,
- "requires": {
- "array.prototype.find": "^2.1.1",
- "function.prototype.name": "^1.1.2",
- "is-regex": "^1.1.0",
- "object-is": "^1.1.2",
- "object.assign": "^4.1.0",
- "object.entries": "^1.1.2",
- "prop-types": "^15.7.2",
- "prop-types-exact": "^1.2.0",
- "react-is": "^16.13.1"
- }
- },
- "ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- }
- },
- "ajv-errors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
- "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
- "peer": true,
- "requires": {}
- },
- "ajv-formats": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
- "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "requires": {
- "ajv": "^8.0.0"
- },
- "dependencies": {
- "ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- }
- },
- "json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
- }
- }
- },
- "ajv-keywords": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
- "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "requires": {}
- },
- "algoliasearch": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.8.3.tgz",
- "integrity": "sha512-pljX9jEE2TQ3i1JayhG8afNdE8UuJg3O9c7unW6QO67yRWCKr6b0t5aKC3hSVtjt7pA2TQXLKoAISb4SHx9ozQ==",
- "requires": {
- "@algolia/cache-browser-local-storage": "4.8.3",
- "@algolia/cache-common": "4.8.3",
- "@algolia/cache-in-memory": "4.8.3",
- "@algolia/client-account": "4.8.3",
- "@algolia/client-analytics": "4.8.3",
- "@algolia/client-common": "4.8.3",
- "@algolia/client-recommendation": "4.8.3",
- "@algolia/client-search": "4.8.3",
- "@algolia/logger-common": "4.8.3",
- "@algolia/logger-console": "4.8.3",
- "@algolia/requester-browser-xhr": "4.8.3",
- "@algolia/requester-common": "4.8.3",
- "@algolia/requester-node-http": "4.8.3",
- "@algolia/transporter": "4.8.3"
- }
- },
- "algoliasearch-helper": {
- "version": "3.11.1",
- "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.1.tgz",
- "integrity": "sha512-mvsPN3eK4E0bZG0/WlWJjeqe/bUD2KOEVOl0GyL/TGXn6wcpZU8NOuztGHCUKXkyg5gq6YzUakVTmnmSSO5Yiw==",
- "requires": {
- "@algolia/events": "^4.0.1"
- }
- },
- "ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "requires": {
- "type-fest": "^0.21.3"
- }
- },
- "ansi-html-community": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
- "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw=="
- },
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "requires": {
- "sprintf-js": "~1.0.2"
- }
- },
- "aria-hidden": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.2.tgz",
- "integrity": "sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==",
- "requires": {
- "tslib": "^2.0.0"
- }
- },
- "aria-query": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
- "requires": {
- "@babel/runtime": "^7.10.2",
- "@babel/runtime-corejs3": "^7.10.2"
- }
- },
- "arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA=="
- },
- "arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
- },
- "arr-union": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q=="
- },
- "array-buffer-byte-length": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
- "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
- "requires": {
- "call-bind": "^1.0.2",
- "is-array-buffer": "^3.0.1"
- }
- },
- "array-flatten": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
- "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ=="
- },
- "array-includes": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
- "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "get-intrinsic": "^1.1.3",
- "is-string": "^1.0.7"
- }
- },
- "array-union": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
- "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
- "requires": {
- "array-uniq": "^1.0.1"
- }
- },
- "array-uniq": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
- "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q=="
- },
- "array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ=="
- },
- "array.prototype.filter": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.2.tgz",
- "integrity": "sha512-us+UrmGOilqttSOgoWZTpOvHu68vZT2YCjc/H4vhu56vzZpaDFBhB+Se2UwqWzMKbDv7Myq5M5pcZLAtUvTQdQ==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-array-method-boxes-properly": "^1.0.0",
- "is-string": "^1.0.7"
- }
- },
- "array.prototype.find": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.1.tgz",
- "integrity": "sha512-I2ri5Z9uMpMvnsNrHre9l3PaX+z9D0/z6F7Yt2u15q7wt0I62g5kX6xUKR1SJiefgG+u2/gJUmM8B47XRvQR6w==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-shim-unscopables": "^1.0.0"
- }
- },
- "array.prototype.flat": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
- "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-shim-unscopables": "^1.0.0"
- }
- },
- "array.prototype.flatmap": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz",
- "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-shim-unscopables": "^1.0.0"
- }
- },
- "array.prototype.reduce": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz",
- "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==",
- "peer": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-array-method-boxes-properly": "^1.0.0",
- "is-string": "^1.0.7"
- }
- },
- "array.prototype.tosorted": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz",
- "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-shim-unscopables": "^1.0.0",
- "get-intrinsic": "^1.1.3"
- }
- },
- "assert-ok": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-ok/-/assert-ok-1.0.0.tgz",
- "integrity": "sha512-lCvYmCpMl8c1tp9ynExhoDEk0gGW43SVVC3RE1VYrrVKhNMy8GHfdiwZdoIM6a605s56bUAbENQxtOC0uZp3wg=="
- },
- "assign-symbols": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="
- },
- "ast-types-flow": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
- "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
- },
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "at-least-node": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
- "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="
- },
- "atob": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
- },
- "attr-accept": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
- "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
- },
- "autoprefixer": {
- "version": "10.4.14",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
- "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
- "requires": {
- "browserslist": "^4.21.5",
- "caniuse-lite": "^1.0.30001464",
- "fraction.js": "^4.2.0",
- "normalize-range": "^0.1.2",
- "picocolors": "^1.0.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "available-typed-arrays": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
- "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
- },
- "axe-core": {
- "version": "4.7.2",
- "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz",
- "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g=="
- },
- "axios": {
- "version": "0.21.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
- "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
- "peer": true,
- "requires": {
- "follow-redirects": "^1.14.0"
- }
- },
- "axios-cache-adapter": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/axios-cache-adapter/-/axios-cache-adapter-2.7.3.tgz",
- "integrity": "sha512-A+ZKJ9lhpjthOEp4Z3QR/a9xC4du1ALaAsejgRGrH9ef6kSDxdFrhRpulqsh9khsEnwXxGfgpUuDp1YXMNMEiQ==",
- "requires": {
- "cache-control-esm": "1.0.0",
- "md5": "^2.2.1"
- }
- },
- "axios-cache-interceptor": {
- "version": "0.10.7",
- "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-0.10.7.tgz",
- "integrity": "sha512-UjpxChG5DpF6Kf1IPGMLOzRDNL8ZNS6TOn1jTaVvCE7cWFU904jJwi0T1s+IbijpnLEjK2iq5uLIuR8Sj+RsFQ==",
- "requires": {
- "cache-parser": "^1.2.4",
- "fast-defer": "^1.1.7",
- "object-code": "^1.2.4"
- }
- },
- "axios-mock-adapter": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz",
- "integrity": "sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg==",
- "requires": {
- "fast-deep-equal": "^3.1.3",
- "is-buffer": "^2.0.3"
- }
- },
- "axobject-query": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
- "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
- "requires": {
- "deep-equal": "^2.0.5"
- }
- },
- "babel-jest": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
- "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==",
- "requires": {
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/babel__core": "^7.1.7",
- "babel-plugin-istanbul": "^6.0.0",
- "babel-preset-jest": "^26.6.2",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "babel-loader": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz",
- "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==",
- "requires": {
- "find-cache-dir": "^3.3.2",
- "schema-utils": "^4.0.0"
- },
- "dependencies": {
- "ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- }
- },
- "ajv-keywords": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "requires": {
- "fast-deep-equal": "^3.1.3"
- }
- },
- "json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
- },
- "schema-utils": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
- "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
- "requires": {
- "@types/json-schema": "^7.0.9",
- "ajv": "^8.9.0",
- "ajv-formats": "^2.1.1",
- "ajv-keywords": "^5.1.0"
- }
- }
- }
- },
- "babel-plugin-istanbul": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
- "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-instrument": "^5.0.4",
- "test-exclude": "^6.0.0"
- }
- },
- "babel-plugin-jest-hoist": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz",
- "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==",
- "requires": {
- "@babel/template": "^7.3.3",
- "@babel/types": "^7.3.3",
- "@types/babel__core": "^7.0.0",
- "@types/babel__traverse": "^7.0.6"
- }
- },
- "babel-plugin-polyfill-corejs2": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
- "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==",
- "requires": {
- "@babel/compat-data": "^7.17.7",
- "@babel/helper-define-polyfill-provider": "^0.3.3",
- "semver": "^6.1.1"
- }
- },
- "babel-plugin-polyfill-corejs3": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz",
- "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==",
- "requires": {
- "@babel/helper-define-polyfill-provider": "^0.3.3",
- "core-js-compat": "^3.25.1"
- }
- },
- "babel-plugin-polyfill-regenerator": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz",
- "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==",
- "requires": {
- "@babel/helper-define-polyfill-provider": "^0.3.3"
- }
- },
- "babel-plugin-react-intl": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz",
- "integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==",
- "requires": {
- "@babel/core": "^7.9.0",
- "@babel/helper-plugin-utils": "^7.8.3",
- "@babel/types": "^7.9.5",
- "@formatjs/ts-transformer": "^2.6.0",
- "@types/babel__core": "^7.1.7",
- "@types/fs-extra": "^9.0.1",
- "@types/schema-utils": "^2.4.0",
- "fs-extra": "^9.0.0",
- "intl-messageformat-parser": "^5.3.7",
- "schema-utils": "^2.6.6"
- },
- "dependencies": {
- "schema-utils": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
- "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
- "requires": {
- "@types/json-schema": "^7.0.5",
- "ajv": "^6.12.4",
- "ajv-keywords": "^3.5.2"
- }
- }
- }
- },
- "babel-plugin-transform-imports": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-imports/-/babel-plugin-transform-imports-2.0.0.tgz",
- "integrity": "sha512-65ewumYJ85QiXdcB/jmiU0y0jg6eL6CdnDqQAqQ8JMOKh1E52VPG3NJzbVKWcgovUR5GBH8IWpCXQ7I8Q3wjgw==",
- "requires": {
- "@babel/types": "^7.4",
- "is-valid-path": "^0.1.1"
- }
- },
- "babel-polyfill": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
- "integrity": "sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==",
- "requires": {
- "babel-runtime": "^6.26.0",
- "core-js": "^2.5.0",
- "regenerator-runtime": "^0.10.5"
- },
- "dependencies": {
- "core-js": {
- "version": "2.6.12",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
- "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
- },
- "regenerator-runtime": {
- "version": "0.10.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
- "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w=="
- }
- }
- },
- "babel-preset-current-node-syntax": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
- "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
- "requires": {
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-bigint": "^7.8.3",
- "@babel/plugin-syntax-class-properties": "^7.8.3",
- "@babel/plugin-syntax-import-meta": "^7.8.3",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-top-level-await": "^7.8.3"
- }
- },
- "babel-preset-jest": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz",
- "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==",
- "requires": {
- "babel-plugin-jest-hoist": "^26.6.2",
- "babel-preset-current-node-syntax": "^1.0.0"
- }
- },
- "babel-runtime": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
- "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
- "requires": {
- "core-js": "^2.4.0",
- "regenerator-runtime": "^0.11.0"
- },
- "dependencies": {
- "core-js": {
- "version": "2.6.12",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
- "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
- },
- "regenerator-runtime": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
- "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
- }
- }
- },
- "bail": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
- "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ=="
- },
- "balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "requires": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
- },
- "batch": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
- "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw=="
- },
- "big.js": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
- "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
- },
- "binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
- },
- "bl": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
- "requires": {
- "buffer": "^5.5.0",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
- },
- "dependencies": {
- "readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
- }
- },
- "body-parser": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
- "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
- "requires": {
- "bytes": "3.1.2",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.11.0",
- "raw-body": "2.5.1",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
- },
- "dependencies": {
- "bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- }
- }
- },
- "bonjour-service": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz",
- "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==",
- "requires": {
- "array-flatten": "^2.1.2",
- "dns-equal": "^1.0.0",
- "fast-deep-equal": "^3.1.3",
- "multicast-dns": "^7.2.5"
- }
- },
- "boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
- },
- "bootstrap": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz",
- "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==",
- "requires": {}
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "requires": {
- "fill-range": "^7.0.1"
- }
- },
- "browser-process-hrtime": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
- "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
- },
- "browserslist": {
- "version": "4.21.7",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz",
- "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==",
- "requires": {
- "caniuse-lite": "^1.0.30001489",
- "electron-to-chromium": "^1.4.411",
- "node-releases": "^2.0.12",
- "update-browserslist-db": "^1.0.11"
- }
- },
- "bs-logger": {
- "version": "0.2.6",
- "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
- "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
- "devOptional": true,
- "requires": {
- "fast-json-stable-stringify": "2.x"
- }
- },
- "bser": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
- "requires": {
- "node-int64": "^0.4.0"
- }
- },
- "buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "requires": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
- "buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
- },
- "bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw=="
- },
- "cache-base": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
- "requires": {
- "collection-visit": "^1.0.0",
- "component-emitter": "^1.2.1",
- "get-value": "^2.0.6",
- "has-value": "^1.0.0",
- "isobject": "^3.0.1",
- "set-value": "^2.0.0",
- "to-object-path": "^0.3.0",
- "union-value": "^1.0.0",
- "unset-value": "^1.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "cache-control-esm": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/cache-control-esm/-/cache-control-esm-1.0.0.tgz",
- "integrity": "sha512-Fa3UV4+eIk4EOih8FTV6EEsVKO0W5XWtNs6FC3InTfVz+EjurjPfDXY5wZDo/lxjDxg5RjNcurLyxEJBcEUx9g=="
- },
- "cache-parser": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.4.tgz",
- "integrity": "sha512-O0KwuHuJnbHUrghHi2kGp0SxnWSIBXTYt7M8WVhW0kbPRUNUKoE/Of6e1rRD6AAxmfxFunKnt90yEK09D+sc5g=="
- },
- "call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
- "requires": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
- }
- },
- "callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
- },
- "camel-case": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
- "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
- "requires": {
- "pascal-case": "^3.1.2",
- "tslib": "^2.0.3"
- }
- },
- "camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
- },
- "caniuse-api": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
- "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
- "requires": {
- "browserslist": "^4.0.0",
- "caniuse-lite": "^1.0.0",
- "lodash.memoize": "^4.1.2",
- "lodash.uniq": "^4.5.0"
- }
- },
- "caniuse-lite": {
- "version": "1.0.30001495",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz",
- "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg=="
- },
- "capture-exit": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
- "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==",
- "requires": {
- "rsvp": "^4.8.4"
- }
- },
- "cast-array": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cast-array/-/cast-array-1.0.1.tgz",
- "integrity": "sha512-EiqtV+M9L42wd0IRgYjgVGDq7vdNBUUrdecd03QReJp8pIr59o2A1b0XfP+aCUlzLKx2E7zVetaogeJCtiHa+w==",
- "requires": {
- "isarray": "0.0.1"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "char-regex": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="
- },
- "character-entities": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
- "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="
- },
- "character-entities-legacy": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
- "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="
- },
- "character-reference-invalid": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
- "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="
- },
- "charenc": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
- "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
- },
- "cheerio": {
- "version": "1.0.0-rc.12",
- "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
- "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
- "dev": true,
- "requires": {
- "cheerio-select": "^2.1.0",
- "dom-serializer": "^2.0.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.0.1",
- "htmlparser2": "^8.0.1",
- "parse5": "^7.0.0",
- "parse5-htmlparser2-tree-adapter": "^7.0.0"
- }
- },
- "cheerio-select": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
- "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-select": "^5.1.0",
- "css-what": "^6.1.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.0.1"
- }
- },
- "chokidar": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
- "requires": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "fsevents": "~2.3.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- }
- },
- "chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
- },
- "chrome-trace-event": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
- "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
- },
- "ci-info": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
- "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
- "dev": true
- },
- "cjs-module-lexer": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz",
- "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw=="
- },
- "class-utils": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
- "requires": {
- "arr-union": "^3.1.0",
- "define-property": "^0.2.5",
- "isobject": "^3.0.0",
- "static-extend": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
- }
- }
- },
- "classnames": {
- "version": "2.2.6",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
- "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
- },
- "clean-css": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
- "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
- "peer": true,
- "requires": {
- "source-map": "~0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "peer": true
- }
- }
- },
- "clean-webpack-plugin": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz",
- "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==",
- "requires": {
- "@types/webpack": "^4.4.31",
- "del": "^4.1.1"
- }
- },
- "cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "clone-deep": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
- "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
- "requires": {
- "is-plain-object": "^2.0.4",
- "kind-of": "^6.0.2",
- "shallow-clone": "^3.0.0"
- }
- },
- "co": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="
- },
- "collect-v8-coverage": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
- "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg=="
- },
- "collection-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
- "requires": {
- "map-visit": "^1.0.0",
- "object-visit": "^1.0.0"
- }
- },
- "color": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
- "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
- "requires": {
- "color-convert": "^1.9.1",
- "color-string": "^1.5.4"
- }
- },
- "color-contrast-checker": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/color-contrast-checker/-/color-contrast-checker-2.1.0.tgz",
- "integrity": "sha512-6Y0aIEej3pwZTVlicIqVzhO6T4izDWouaIXnYoDdTuFFAMQ9nnN0dgHNP9J94jRnH6asjPq1/wzUKxwoNbWtRQ=="
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
- },
- "color-string": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
- "requires": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
- },
- "colord": {
- "version": "2.9.3",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
- "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
- },
- "colorette": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
- "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="
- },
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "requires": {
- "delayed-stream": "~1.0.0"
- }
- },
- "comma-separated-tokens": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
- "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw=="
- },
- "commander": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
- },
- "common-path-prefix": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
- "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
- },
- "commondir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
- "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
- },
- "component-emitter": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
- "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
- },
- "compressible": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
- "requires": {
- "mime-db": ">= 1.43.0 < 2"
- }
- },
- "compression": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
- "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
- "requires": {
- "accepts": "~1.3.5",
- "bytes": "3.0.0",
- "compressible": "~2.0.16",
- "debug": "2.6.9",
- "on-headers": "~1.0.2",
- "safe-buffer": "5.1.2",
- "vary": "~1.1.2"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- }
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
- },
- "confusing-browser-globals": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
- "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA=="
- },
- "connect-history-api-fallback": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
- "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA=="
- },
- "content-disposition": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
- "requires": {
- "safe-buffer": "5.2.1"
- }
- },
- "content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
- },
- "convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
- },
- "cookie": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
- "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
- },
- "cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
- },
- "cookiejar": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
- "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="
- },
- "copy-descriptor": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="
- },
- "core-js": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz",
- "integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA=="
- },
- "core-js-compat": {
- "version": "3.30.2",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz",
- "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==",
- "requires": {
- "browserslist": "^4.21.5"
- }
- },
- "core-js-pure": {
- "version": "3.26.1",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz",
- "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ=="
- },
- "core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
- },
- "cosmiconfig": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
- "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "dev": true,
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.2.1",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.10.0"
- }
- },
- "cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "requires": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- }
- },
- "crypt": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
- "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
- },
- "css": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
- "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
- "dev": true,
- "requires": {
- "inherits": "^2.0.4",
- "source-map": "^0.6.1",
- "source-map-resolve": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "css-declaration-sorter": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
- "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==",
- "requires": {}
- },
- "css-loader": {
- "version": "5.2.6",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.6.tgz",
- "integrity": "sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w==",
- "dev": true,
- "requires": {
- "icss-utils": "^5.1.0",
- "loader-utils": "^2.0.0",
- "postcss": "^8.2.15",
- "postcss-modules-extract-imports": "^3.0.0",
- "postcss-modules-local-by-default": "^4.0.0",
- "postcss-modules-scope": "^3.0.0",
- "postcss-modules-values": "^4.0.0",
- "postcss-value-parser": "^4.1.0",
- "schema-utils": "^3.0.0",
- "semver": "^7.3.5"
- },
- "dependencies": {
- "postcss": {
- "version": "8.4.19",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
- "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
- "dev": true,
- "requires": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- },
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "css-mediaquery": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz",
- "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q=="
- },
- "css-select": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
- "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "^6.1.0",
- "domhandler": "^5.0.2",
- "domutils": "^3.0.1",
- "nth-check": "^2.0.1"
- }
- },
- "css-tree": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
- "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
- "dev": true,
- "requires": {
- "mdn-data": "2.0.14",
- "source-map": "^0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
- },
- "css-what": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
- },
- "css.escape": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
- "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
- "dev": true
- },
- "cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
- },
- "cssfontparser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz",
- "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==",
- "dev": true
- },
- "csso": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
- "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
- "dev": true,
- "requires": {
- "css-tree": "^1.1.2"
- }
- },
- "cssom": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
- "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw=="
- },
- "cssstyle": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
- "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
- "requires": {
- "cssom": "~0.3.6"
- },
- "dependencies": {
- "cssom": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
- "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
- }
- }
- },
- "csstype": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
- "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
- },
- "damerau-levenshtein": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
- "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
- },
- "dash-embedded-component": {
- "version": "file:packages/dash-embedded-component-2.0.2.tgz",
- "integrity": "sha512-fqey60NHFet3SuUSYAJbV0LWkwdUFGqFUNFdYorasL1DMAJCSlCp4n7806b7GgEOteXdLuznie2mrWASZFSt+g==",
- "requires": {
- "@babel/runtime": "^7.10.0",
- "check-prop-types": "^1.1.2",
- "cookie": "^0.4.1",
- "dependency-graph": "^0.9.0",
- "prop-types": "^15.7.2",
- "ramda": "^0.26.1",
- "react": "^15.6.0 || ^16.0.0 || ^17.0.0",
- "react-dom": "^15.6.0 || ^16.0.0 || ^17.0.0",
- "react-redux": "^7.2.0",
- "redux": "^4.0.5",
- "redux-actions": "^2.6.5",
- "uuid": "^8.3.2",
- "viz.js": "2.1.2"
- },
- "dependencies": {
- "check-prop-types": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/check-prop-types/-/check-prop-types-1.1.2.tgz",
- "integrity": "sha512-hGDrZ1yhRgKuP1yzZ5sUX/PPmlKBLOF1GyF0Z008Sienko3BFZmlCXnmq+npRTIL/WlFCUzThyd+F5PQnnT1ug==",
- "requires": {}
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
- },
- "react-redux": {
- "version": "7.2.9",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
- "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
- "requires": {
- "@babel/runtime": "^7.15.4",
- "@types/react-redux": "^7.1.20",
- "hoist-non-react-statics": "^3.3.2",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.7.2",
- "react-is": "^17.0.2"
- }
- },
- "redux": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
- "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
- "requires": {
- "@babel/runtime": "^7.9.2"
- }
- },
- "uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
- }
- }
- },
- "data-urls": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
- "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
- "requires": {
- "abab": "^2.0.3",
- "whatwg-mimetype": "^2.3.0",
- "whatwg-url": "^8.0.0"
- }
- },
- "debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "requires": {
- "ms": "2.1.2"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
- },
- "decimal.js": {
- "version": "10.4.2",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz",
- "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA=="
- },
- "decode-uri-component": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
- "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
- },
- "decompress-response": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
- "requires": {
- "mimic-response": "^3.1.0"
- }
- },
- "deep-diff": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
- "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug=="
- },
- "deep-equal": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz",
- "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==",
- "requires": {
- "array-buffer-byte-length": "^1.0.0",
- "call-bind": "^1.0.2",
- "es-get-iterator": "^1.1.3",
- "get-intrinsic": "^1.2.0",
- "is-arguments": "^1.1.1",
- "is-array-buffer": "^3.0.2",
- "is-date-object": "^1.0.5",
- "is-regex": "^1.1.4",
- "is-shared-array-buffer": "^1.0.2",
- "isarray": "^2.0.5",
- "object-is": "^1.1.5",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.4",
- "regexp.prototype.flags": "^1.5.0",
- "side-channel": "^1.0.4",
- "which-boxed-primitive": "^1.0.2",
- "which-collection": "^1.0.1",
- "which-typed-array": "^1.1.9"
- },
- "dependencies": {
- "isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
- }
- }
- },
- "deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
- },
- "deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
- },
- "deepmerge": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
- },
- "default-gateway": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
- "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
- "requires": {
- "execa": "^5.0.0"
- },
- "dependencies": {
- "execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "requires": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- }
- },
- "get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
- },
- "is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
- },
- "npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "requires": {
- "path-key": "^3.0.0"
- }
- }
- }
- },
- "define-lazy-prop": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
- "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
- },
- "define-properties": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
- "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
- "requires": {
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- }
- },
- "define-property": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
- "requires": {
- "is-descriptor": "^1.0.2",
- "isobject": "^3.0.1"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "del": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
- "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
- "requires": {
- "@types/glob": "^7.1.1",
- "globby": "^6.1.0",
- "is-path-cwd": "^2.0.0",
- "is-path-in-cwd": "^2.0.0",
- "p-map": "^2.0.0",
- "pify": "^4.0.1",
- "rimraf": "^2.6.3"
- }
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
- },
- "depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
- },
- "dependency-graph": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.9.0.tgz",
- "integrity": "sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w=="
- },
- "dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
- },
- "destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
- },
- "detect-libc": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
- "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
- },
- "detect-newline": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
- "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="
- },
- "detect-node": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
- "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
- },
- "detect-node-es": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
- "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
- },
- "detect-port-alt": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz",
- "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==",
- "requires": {
- "address": "^1.0.1",
- "debug": "^2.6.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- }
- }
- },
- "diacritics": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
- "integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA=="
- },
- "diff-sequences": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz",
- "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==",
- "dev": true
- },
- "dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "requires": {
- "path-type": "^4.0.0"
- }
- },
- "discontinuous-range": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
- "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==",
- "dev": true
- },
- "dns-equal": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
- "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg=="
- },
- "dns-packet": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz",
- "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==",
- "requires": {
- "@leichtgewicht/ip-codec": "^2.0.1"
- }
- },
- "doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "dom-accessibility-api": {
- "version": "0.5.14",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz",
- "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg=="
- },
- "dom-converter": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
- "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
- "requires": {
- "utila": "~0.4"
- }
- },
- "dom-helpers": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
- "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
- "requires": {
- "@babel/runtime": "^7.8.7",
- "csstype": "^3.0.2"
- }
- },
- "dom-serializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "requires": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "entities": "^4.2.0"
- }
- },
- "domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
- },
- "domexception": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
- "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
- "requires": {
- "webidl-conversions": "^5.0.0"
- },
- "dependencies": {
- "webidl-conversions": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
- "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="
- }
- }
- },
- "domhandler": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "requires": {
- "domelementtype": "^2.3.0"
- }
- },
- "domutils": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
- "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
- "requires": {
- "dom-serializer": "^2.0.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.1"
- }
- },
- "dot-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
- "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
- "requires": {
- "no-case": "^3.0.4",
- "tslib": "^2.0.3"
- }
- },
- "dotenv": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
- "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
- },
- "dotenv-defaults": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
- "integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
- "requires": {
- "dotenv": "^8.2.0"
- }
- },
- "dotenv-webpack": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-3.0.0.tgz",
- "integrity": "sha512-HsBTgVbh9E71IdC6QcQTnAuVt11niA5QMKblcBqhVBxP975XPGMqxf21pqgVBPyRKn2GqPh5kSHMgjxeR/HEAA==",
- "peer": true,
- "requires": {
- "dotenv-defaults": "^2.0.1"
- }
- },
- "duplexer": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
- "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
- },
- "ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
- },
- "electron-to-chromium": {
- "version": "1.4.421",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.421.tgz",
- "integrity": "sha512-wZOyn3s/aQOtLGAwXMZfteQPN68kgls2wDAnYOA8kCjBvKVrW5RwmWVspxJYTqrcN7Y263XJVsC66VCIGzDO3g=="
- },
- "email-prop-type": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/email-prop-type/-/email-prop-type-3.0.1.tgz",
- "integrity": "sha512-tONZGMEOOkadp5OBftuVXU8DsceWmINxYK+pqPFB4LT5ODjrPX/esel3WGqbV7d6in5/MnZE4n4QcqOr4gh7dg==",
- "requires": {
- "email-validator": "^2.0.4"
- }
- },
- "email-validator": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz",
- "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ=="
- },
- "emittery": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz",
- "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ=="
- },
- "emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
- },
- "emojis-list": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
- "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
- },
- "end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "requires": {
- "once": "^1.4.0"
- }
- },
- "enhanced-resolve": {
- "version": "5.14.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz",
- "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==",
- "requires": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "dependencies": {
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
- }
- }
- },
- "entities": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
- "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
- },
- "envinfo": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
- "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
- },
- "enzyme": {
- "version": "3.11.0",
- "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz",
- "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==",
- "dev": true,
- "requires": {
- "array.prototype.flat": "^1.2.3",
- "cheerio": "^1.0.0-rc.3",
- "enzyme-shallow-equal": "^1.0.1",
- "function.prototype.name": "^1.1.2",
- "has": "^1.0.3",
- "html-element-map": "^1.2.0",
- "is-boolean-object": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-number-object": "^1.0.4",
- "is-regex": "^1.0.5",
- "is-string": "^1.0.5",
- "is-subset": "^0.1.1",
- "lodash.escape": "^4.0.1",
- "lodash.isequal": "^4.5.0",
- "object-inspect": "^1.7.0",
- "object-is": "^1.0.2",
- "object.assign": "^4.1.0",
- "object.entries": "^1.1.1",
- "object.values": "^1.1.1",
- "raf": "^3.4.1",
- "rst-selector-parser": "^2.2.3",
- "string.prototype.trim": "^1.2.1"
- }
- },
- "enzyme-adapter-react-16": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz",
- "integrity": "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==",
- "dev": true,
- "requires": {
- "enzyme-adapter-utils": "^1.14.0",
- "enzyme-shallow-equal": "^1.0.4",
- "has": "^1.0.3",
- "object.assign": "^4.1.2",
- "object.values": "^1.1.2",
- "prop-types": "^15.7.2",
- "react-is": "^16.13.1",
- "react-test-renderer": "^16.0.0-0",
- "semver": "^5.7.0"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
- },
- "enzyme-adapter-utils": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.1.tgz",
- "integrity": "sha512-JZgMPF1QOI7IzBj24EZoDpaeG/p8Os7WeBZWTJydpsH7JRStc7jYbHE4CmNQaLqazaGFyLM8ALWA3IIZvxW3PQ==",
- "dev": true,
- "requires": {
- "airbnb-prop-types": "^2.16.0",
- "function.prototype.name": "^1.1.5",
- "has": "^1.0.3",
- "object.assign": "^4.1.4",
- "object.fromentries": "^2.0.5",
- "prop-types": "^15.8.1",
- "semver": "^5.7.1"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
- },
- "enzyme-shallow-equal": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.5.tgz",
- "integrity": "sha512-i6cwm7hN630JXenxxJFBKzgLC3hMTafFQXflvzHgPmDhOBhxUWDe8AeRv1qp2/uWJ2Y8z5yLWMzmAfkTOiOCZg==",
- "dev": true,
- "requires": {
- "has": "^1.0.3",
- "object-is": "^1.1.5"
- }
- },
- "error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "requires": {
- "is-arrayish": "^0.2.1"
- }
- },
- "error-stack-parser": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
- "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
- "requires": {
- "stackframe": "^1.3.4"
- }
- },
- "es-abstract": {
- "version": "1.20.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
- "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
- "requires": {
- "call-bind": "^1.0.2",
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "function.prototype.name": "^1.1.5",
- "get-intrinsic": "^1.1.3",
- "get-symbol-description": "^1.0.0",
- "has": "^1.0.3",
- "has-property-descriptors": "^1.0.0",
- "has-symbols": "^1.0.3",
- "internal-slot": "^1.0.3",
- "is-callable": "^1.2.7",
- "is-negative-zero": "^2.0.2",
- "is-regex": "^1.1.4",
- "is-shared-array-buffer": "^1.0.2",
- "is-string": "^1.0.7",
- "is-weakref": "^1.0.2",
- "object-inspect": "^1.12.2",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.4",
- "regexp.prototype.flags": "^1.4.3",
- "safe-regex-test": "^1.0.0",
- "string.prototype.trimend": "^1.0.5",
- "string.prototype.trimstart": "^1.0.5",
- "unbox-primitive": "^1.0.2"
- }
- },
- "es-array-method-boxes-properly": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
- "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
- },
- "es-get-iterator": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
- "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
- "requires": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.3",
- "has-symbols": "^1.0.3",
- "is-arguments": "^1.1.1",
- "is-map": "^2.0.2",
- "is-set": "^2.0.2",
- "is-string": "^1.0.7",
- "isarray": "^2.0.5",
- "stop-iteration-iterator": "^1.0.0"
- },
- "dependencies": {
- "isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
- }
- }
- },
- "es-module-lexer": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz",
- "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg=="
- },
- "es-shim-unscopables": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
- "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
- "requires": {
- "has": "^1.0.3"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "es6-error": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="
- },
- "escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
- },
- "escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
- },
- "escodegen": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
- "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
- "requires": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2",
- "optionator": "^0.8.1",
- "source-map": "~0.6.1"
- },
- "dependencies": {
- "levn": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
- "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
- "requires": {
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2"
- }
- },
- "optionator": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
- "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
- "requires": {
- "deep-is": "~0.1.3",
- "fast-levenshtein": "~2.0.6",
- "levn": "~0.3.0",
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2",
- "word-wrap": "~1.2.3"
- }
- },
- "prelude-ls": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "optional": true
- },
- "type-check": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
- "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
- "requires": {
- "prelude-ls": "~1.1.2"
- }
- }
- }
- },
- "eslint": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
- "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
- "requires": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.4.0",
- "@eslint/eslintrc": "^2.0.2",
- "@eslint/js": "8.38.0",
- "@humanwhocodes/config-array": "^0.11.8",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@nodelib/fs.walk": "^1.2.8",
- "ajv": "^6.10.0",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
- "debug": "^4.3.2",
- "doctrine": "^3.0.0",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.1.1",
- "eslint-visitor-keys": "^3.4.0",
- "espree": "^9.5.1",
- "esquery": "^1.4.2",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "grapheme-splitter": "^1.0.4",
- "ignore": "^5.2.0",
- "import-fresh": "^3.0.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
- "js-sdsl": "^4.1.4",
- "js-yaml": "^4.1.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
- "strip-ansi": "^6.0.1",
- "strip-json-comments": "^3.1.0",
- "text-table": "^0.2.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
- },
- "eslint-scope": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
- "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
- "requires": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- }
- },
- "glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "requires": {
- "is-glob": "^4.0.3"
- }
- },
- "globals": {
- "version": "13.19.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
- "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
- "requires": {
- "type-fest": "^0.20.2"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
- }
- }
- },
- "eslint-config-airbnb": {
- "version": "19.0.4",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
- "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
- "requires": {
- "eslint-config-airbnb-base": "^15.0.0",
- "object.assign": "^4.1.2",
- "object.entries": "^1.1.5"
- }
- },
- "eslint-config-airbnb-base": {
- "version": "15.0.0",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
- "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
- "requires": {
- "confusing-browser-globals": "^1.0.10",
- "object.assign": "^4.1.2",
- "object.entries": "^1.1.5",
- "semver": "^6.3.0"
- }
- },
- "eslint-config-airbnb-typescript": {
- "version": "17.0.0",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz",
- "integrity": "sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==",
- "dev": true,
- "requires": {
- "eslint-config-airbnb-base": "^15.0.0"
- }
- },
- "eslint-import-resolver-node": {
- "version": "0.3.7",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
- "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==",
- "requires": {
- "debug": "^3.2.7",
- "is-core-module": "^2.11.0",
- "resolve": "^1.22.1"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "requires": {
- "ms": "^2.1.1"
- }
- }
- }
- },
- "eslint-module-utils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz",
- "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==",
- "requires": {
- "debug": "^3.2.7"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "requires": {
- "ms": "^2.1.1"
- }
- }
- }
- },
- "eslint-plugin-import": {
- "version": "2.27.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
- "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
- "requires": {
- "array-includes": "^3.1.6",
- "array.prototype.flat": "^1.3.1",
- "array.prototype.flatmap": "^1.3.1",
- "debug": "^3.2.7",
- "doctrine": "^2.1.0",
- "eslint-import-resolver-node": "^0.3.7",
- "eslint-module-utils": "^2.7.4",
- "has": "^1.0.3",
- "is-core-module": "^2.11.0",
- "is-glob": "^4.0.3",
- "minimatch": "^3.1.2",
- "object.values": "^1.1.6",
- "resolve": "^1.22.1",
- "semver": "^6.3.0",
- "tsconfig-paths": "^3.14.1"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "requires": {
- "esutils": "^2.0.2"
- }
- }
- }
- },
- "eslint-plugin-jsx-a11y": {
- "version": "6.7.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz",
- "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
- "requires": {
- "@babel/runtime": "^7.20.7",
- "aria-query": "^5.1.3",
- "array-includes": "^3.1.6",
- "array.prototype.flatmap": "^1.3.1",
- "ast-types-flow": "^0.0.7",
- "axe-core": "^4.6.2",
- "axobject-query": "^3.1.1",
- "damerau-levenshtein": "^1.0.8",
- "emoji-regex": "^9.2.2",
- "has": "^1.0.3",
- "jsx-ast-utils": "^3.3.3",
- "language-tags": "=1.0.5",
- "minimatch": "^3.1.2",
- "object.entries": "^1.1.6",
- "object.fromentries": "^2.0.6",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "aria-query": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
- "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
- "requires": {
- "deep-equal": "^2.0.5"
- }
- }
- }
- },
- "eslint-plugin-react": {
- "version": "7.32.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz",
- "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==",
- "requires": {
- "array-includes": "^3.1.6",
- "array.prototype.flatmap": "^1.3.1",
- "array.prototype.tosorted": "^1.1.1",
- "doctrine": "^2.1.0",
- "estraverse": "^5.3.0",
- "jsx-ast-utils": "^2.4.1 || ^3.0.0",
- "minimatch": "^3.1.2",
- "object.entries": "^1.1.6",
- "object.fromentries": "^2.0.6",
- "object.hasown": "^1.1.2",
- "object.values": "^1.1.6",
- "prop-types": "^15.8.1",
- "resolve": "^2.0.0-next.4",
- "semver": "^6.3.0",
- "string.prototype.matchall": "^4.0.8"
- },
- "dependencies": {
- "doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "resolve": {
- "version": "2.0.0-next.4",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
- "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
- "requires": {
- "is-core-module": "^2.9.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- }
- }
- }
- },
- "eslint-plugin-react-hooks": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
- "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
- "requires": {}
- },
- "eslint-scope": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
- "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "requires": {
- "esrecurse": "^4.3.0",
- "estraverse": "^4.1.1"
- },
- "dependencies": {
- "estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
- }
- }
- },
- "eslint-visitor-keys": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
- "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA=="
- },
- "espree": {
- "version": "9.5.2",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz",
- "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==",
- "requires": {
- "acorn": "^8.8.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
- }
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
- },
- "esquery": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
- "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
- "requires": {
- "estraverse": "^5.1.0"
- }
- },
- "esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "requires": {
- "estraverse": "^5.2.0"
- }
- },
- "estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
- },
- "esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
- },
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
- },
- "eventemitter3": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
- "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
- },
- "events": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
- },
- "exec-sh": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz",
- "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w=="
- },
- "execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "requires": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- },
- "shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
- "requires": {
- "shebang-regex": "^1.0.0"
- }
- },
- "shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "requires": {
- "isexe": "^2.0.0"
- }
- }
- }
- },
- "exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ=="
- },
- "expand-brackets": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
- "requires": {
- "debug": "^2.3.3",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "posix-character-classes": "^0.1.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- }
- }
- },
- "expand-template": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
- },
- "expect": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz",
- "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==",
- "dev": true,
- "requires": {
- "@jest/expect-utils": "^29.3.1",
- "jest-get-type": "^29.2.0",
- "jest-matcher-utils": "^29.3.1",
- "jest-message-util": "^29.3.1",
- "jest-util": "^29.3.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz",
- "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.0.0",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.13",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz",
- "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "ci-info": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.1.tgz",
- "integrity": "sha512-up5ggbaDqOqJ4UqLKZ2naVkyqSJQgJi5lwD6b6mM748ysrghDBX0bx/qJTUHzw7zu6Mq4gycviSF5hJnwceD8w==",
- "dev": true
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "jest-util": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz",
- "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^29.3.1",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "express": {
- "version": "4.18.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
- "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
- "requires": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
- "body-parser": "1.20.1",
- "content-disposition": "0.5.4",
- "content-type": "~1.0.4",
- "cookie": "0.5.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.2.0",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.7",
- "qs": "6.11.0",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
- "dependencies": {
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
- },
- "cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
- }
- }
- },
- "extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
- },
- "extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "requires": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- }
- },
- "extglob": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
- "requires": {
- "array-unique": "^0.3.2",
- "define-property": "^1.0.0",
- "expand-brackets": "^2.1.4",
- "extend-shallow": "^2.0.1",
- "fragment-cache": "^0.2.1",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
- }
- }
- },
- "fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
- },
- "fast-defer": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.7.tgz",
- "integrity": "sha512-tJ01ulDWT2WhqxMKS20nXX6wyX2iInBYpbN3GO7yjKwXMY4qvkdBRxak9IFwBLlFDESox+SwSvqMCZDfe1tqeg=="
- },
- "fast-glob": {
- "version": "3.2.12",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
- "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
- "requires": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.4"
- }
- },
- "fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
- },
- "fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
- },
- "fastest-levenshtein": {
- "version": "1.0.16",
- "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
- "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="
- },
- "fastq": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
- "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
- "requires": {
- "reusify": "^1.0.4"
- }
- },
- "faye-websocket": {
- "version": "0.11.4",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
- "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
- "requires": {
- "websocket-driver": ">=0.5.1"
- }
- },
- "fb-watchman": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
- "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
- "requires": {
- "bser": "2.1.1"
- }
- },
- "file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
- "requires": {
- "flat-cache": "^3.0.4"
- }
- },
- "file-loader": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
- "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
- "requires": {
- "loader-utils": "^2.0.0",
- "schema-utils": "^3.0.0"
- }
- },
- "file-saver": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
- "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
- },
- "file-selector": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
- "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
- "requires": {
- "tslib": "^2.4.0"
- }
- },
- "filesize": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
- "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==",
- "dev": true
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "filter-console": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/filter-console/-/filter-console-0.1.1.tgz",
- "integrity": "sha512-zrXoV1Uaz52DqPs+qEwNJWJFAWZpYJ47UNmpN9q4j+/EYsz85uV0DC9k8tRND5kYmoVzL0W+Y75q4Rg8sRJCdg==",
- "dev": true
- },
- "filter-obj": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
- "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
- },
- "finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- }
- }
- },
- "find-cache-dir": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
- "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
- "requires": {
- "commondir": "^1.0.1",
- "make-dir": "^3.0.2",
- "pkg-dir": "^4.1.0"
- },
- "dependencies": {
- "make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "requires": {
- "semver": "^6.0.0"
- }
- }
- }
- },
- "find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "requires": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "flat-cache": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
- "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
- "requires": {
- "flatted": "^3.1.0",
- "rimraf": "^3.0.2"
- },
- "dependencies": {
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "requires": {
- "glob": "^7.1.3"
- }
- }
- }
- },
- "flatted": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
- "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
- },
- "focus-lock": {
- "version": "0.11.3",
- "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.3.tgz",
- "integrity": "sha512-4n0pYcPTa/uI7Q66BZna61nRT7lDhnuJ9PJr6wiDjx4uStg491ks41y7uOG+s0umaaa+hulNKSldU9aTg9/yVg==",
- "requires": {
- "tslib": "^2.0.3"
- }
- },
- "follow-redirects": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
- },
- "font-awesome": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
- "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg=="
- },
- "for-each": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
- "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
- "requires": {
- "is-callable": "^1.1.3"
- }
- },
- "for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="
- },
- "fork-ts-checker-webpack-plugin": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz",
- "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.5.5",
- "chalk": "^2.4.1",
- "micromatch": "^3.1.10",
- "minimatch": "^3.0.4",
- "semver": "^5.6.0",
- "tapable": "^1.0.0",
- "worker-rpc": "^0.1.0"
- },
- "dependencies": {
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
- "dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "dev": true
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
- "dev": true
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
- "dev": true
- },
- "micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "dev": true,
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
- "dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- }
- }
- }
- },
- "form-data": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
- "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
- },
- "form-urlencoded": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-4.1.4.tgz",
- "integrity": "sha512-R7Vytos0gMYuPQTMwnNzvK9PBItNV+Qkm/pvghEZI3j2kMrzZmJlczAgHFmt12VV+IRYQXgTlSGP1PKAsMCIUA=="
- },
- "formidable": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
- "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ=="
- },
- "forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
- },
- "fraction.js": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
- "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA=="
- },
- "fragment-cache": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
- "requires": {
- "map-cache": "^0.2.2"
- }
- },
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
- },
- "frontend-platform-shim": {
- "version": "file:packages/frontend-platform-shim",
- "requires": {
- "@edx/frontend-platform": "4.5.1"
- },
- "dependencies": {
- "@babel/cli": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.21.5.tgz",
- "integrity": "sha512-TOKytQ9uQW9c4np8F+P7ZfPINy5Kv+pizDIUwSVH8X5zHgYHV4AA8HE5LA450xXeu4jEfmUckTYvv1I4S26M/g==",
- "peer": true,
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.17",
- "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
- "chokidar": "^3.4.0",
- "commander": "^4.0.1",
- "convert-source-map": "^1.1.0",
- "fs-readdir-recursive": "^1.1.0",
- "glob": "^7.2.0",
- "make-dir": "^2.1.0",
- "slash": "^2.0.0"
- }
- },
- "@babel/core": {
- "version": "7.21.8",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz",
- "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==",
- "peer": true,
- "requires": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.21.4",
- "@babel/generator": "^7.21.5",
- "@babel/helper-compilation-targets": "^7.21.5",
- "@babel/helper-module-transforms": "^7.21.5",
- "@babel/helpers": "^7.21.5",
- "@babel/parser": "^7.21.8",
- "@babel/template": "^7.20.7",
- "@babel/traverse": "^7.21.5",
- "@babel/types": "^7.21.5",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.2",
- "semver": "^6.3.0"
- }
- },
- "@babel/preset-env": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz",
- "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==",
- "peer": true,
- "requires": {
- "@babel/compat-data": "^7.21.5",
- "@babel/helper-compilation-targets": "^7.21.5",
- "@babel/helper-plugin-utils": "^7.21.5",
- "@babel/helper-validator-option": "^7.21.0",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7",
- "@babel/plugin-proposal-async-generator-functions": "^7.20.7",
- "@babel/plugin-proposal-class-properties": "^7.18.6",
- "@babel/plugin-proposal-class-static-block": "^7.21.0",
- "@babel/plugin-proposal-dynamic-import": "^7.18.6",
- "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
- "@babel/plugin-proposal-json-strings": "^7.18.6",
- "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
- "@babel/plugin-proposal-numeric-separator": "^7.18.6",
- "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
- "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
- "@babel/plugin-proposal-optional-chaining": "^7.21.0",
- "@babel/plugin-proposal-private-methods": "^7.18.6",
- "@babel/plugin-proposal-private-property-in-object": "^7.21.0",
- "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-class-properties": "^7.12.13",
- "@babel/plugin-syntax-class-static-block": "^7.14.5",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
- "@babel/plugin-syntax-import-assertions": "^7.20.0",
- "@babel/plugin-syntax-import-meta": "^7.10.4",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.10.4",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
- "@babel/plugin-syntax-top-level-await": "^7.14.5",
- "@babel/plugin-transform-arrow-functions": "^7.21.5",
- "@babel/plugin-transform-async-to-generator": "^7.20.7",
- "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
- "@babel/plugin-transform-block-scoping": "^7.21.0",
- "@babel/plugin-transform-classes": "^7.21.0",
- "@babel/plugin-transform-computed-properties": "^7.21.5",
- "@babel/plugin-transform-destructuring": "^7.21.3",
- "@babel/plugin-transform-dotall-regex": "^7.18.6",
- "@babel/plugin-transform-duplicate-keys": "^7.18.9",
- "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
- "@babel/plugin-transform-for-of": "^7.21.5",
- "@babel/plugin-transform-function-name": "^7.18.9",
- "@babel/plugin-transform-literals": "^7.18.9",
- "@babel/plugin-transform-member-expression-literals": "^7.18.6",
- "@babel/plugin-transform-modules-amd": "^7.20.11",
- "@babel/plugin-transform-modules-commonjs": "^7.21.5",
- "@babel/plugin-transform-modules-systemjs": "^7.20.11",
- "@babel/plugin-transform-modules-umd": "^7.18.6",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5",
- "@babel/plugin-transform-new-target": "^7.18.6",
- "@babel/plugin-transform-object-super": "^7.18.6",
- "@babel/plugin-transform-parameters": "^7.21.3",
- "@babel/plugin-transform-property-literals": "^7.18.6",
- "@babel/plugin-transform-regenerator": "^7.21.5",
- "@babel/plugin-transform-reserved-words": "^7.18.6",
- "@babel/plugin-transform-shorthand-properties": "^7.18.6",
- "@babel/plugin-transform-spread": "^7.20.7",
- "@babel/plugin-transform-sticky-regex": "^7.18.6",
- "@babel/plugin-transform-template-literals": "^7.18.9",
- "@babel/plugin-transform-typeof-symbol": "^7.18.9",
- "@babel/plugin-transform-unicode-escapes": "^7.21.5",
- "@babel/plugin-transform-unicode-regex": "^7.18.6",
- "@babel/preset-modules": "^0.1.5",
- "@babel/types": "^7.21.5",
- "babel-plugin-polyfill-corejs2": "^0.3.3",
- "babel-plugin-polyfill-corejs3": "^0.6.0",
- "babel-plugin-polyfill-regenerator": "^0.4.1",
- "core-js-compat": "^3.25.1",
- "semver": "^6.3.0"
- }
- },
- "@cospired/i18n-iso-languages": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@cospired/i18n-iso-languages/-/i18n-iso-languages-4.1.0.tgz",
- "integrity": "sha512-5+JK7YiO9r/FmwtlEPL1tQNt04/9AuN1t9GO/0C2yitqhKwFRa1r7VohNNUnFgB84MW5v4Lwq8ZAUZexuJh1nQ=="
- },
- "@edx/eslint-config": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-3.2.0.tgz",
- "integrity": "sha512-X2o34xr3KqmQSV/vJVv6k4FxUKYwbBATHTtTHLTYQvM9PVoM3WbKQP9tl6Z057pRErKzshJcks+4ENzDyhr11Q==",
- "peer": true,
- "requires": {}
- },
- "@edx/frontend-build": {
- "version": "12.8.40",
- "resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.8.40.tgz",
- "integrity": "sha512-11U9VCHLuozbot93VycbSVeCwvcXtCp235154wgExSR5tPoAhqh/OJ1MSlZXUIzVv5xWMxZI/DCPUdZ76+96Nw==",
- "peer": true,
- "requires": {
- "@babel/cli": "7.21.5",
- "@babel/core": "7.21.8",
- "@babel/eslint-parser": "7.21.8",
- "@babel/plugin-proposal-class-properties": "7.18.6",
- "@babel/plugin-proposal-object-rest-spread": "7.20.7",
- "@babel/plugin-syntax-dynamic-import": "7.8.3",
- "@babel/preset-env": "7.21.5",
- "@babel/preset-react": "7.18.6",
- "@edx/eslint-config": "3.2.0",
- "@edx/new-relic-source-map-webpack-plugin": "2.1.0",
- "@fullhuman/postcss-purgecss": "^5.0.0",
- "@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
- "@svgr/webpack": "8.0.1",
- "autoprefixer": "10.4.14",
- "babel-jest": "26.6.3",
- "babel-loader": "9.1.2",
- "babel-plugin-react-intl": "7.9.4",
- "babel-plugin-transform-imports": "2.0.0",
- "babel-polyfill": "6.26.0",
- "clean-webpack-plugin": "4.0.0",
- "css-loader": "5.2.7",
- "cssnano": "6.0.1",
- "dotenv": "8.6.0",
- "dotenv-webpack": "8.0.1",
- "eslint": "8.41.0",
- "eslint-config-airbnb": "19.0.4",
- "eslint-plugin-import": "2.27.5",
- "eslint-plugin-jsx-a11y": "6.7.1",
- "eslint-plugin-react": "7.32.2",
- "eslint-plugin-react-hooks": "4.6.0",
- "file-loader": "6.2.0",
- "html-webpack-plugin": "5.5.1",
- "identity-obj-proxy": "3.0.0",
- "image-minimizer-webpack-plugin": "3.8.2",
- "jest": "26.6.3",
- "mini-css-extract-plugin": "1.6.2",
- "postcss": "8.4.24",
- "postcss-custom-media": "^9.1.2",
- "postcss-loader": "7.3.2",
- "postcss-rtlcss": "4.0.6",
- "react-dev-utils": "12.0.1",
- "react-refresh": "0.14.0",
- "resolve-url-loader": "5.0.0",
- "sass": "1.62.1",
- "sass-loader": "13.3.1",
- "sharp": "^0.32.0",
- "source-map-loader": "^4.0.1",
- "style-loader": "3.3.3",
- "url-loader": "4.1.1",
- "webpack": "5.84.1",
- "webpack-bundle-analyzer": "4.8.0",
- "webpack-cli": "5.1.3",
- "webpack-dev-server": "4.15.0",
- "webpack-merge": "5.9.0"
- }
- },
- "@edx/frontend-platform": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.5.1.tgz",
- "integrity": "sha512-4nZGk+Wl+z4D5gE01xzLwl+vkxPzupCnEeZJ4uwoU6A7P8oID8ZdoR5RNtKBuhsYnouEWcgzHtFDtWK9Py9NuA==",
- "requires": {
- "@cospired/i18n-iso-languages": "4.1.0",
- "@formatjs/intl-pluralrules": "4.3.3",
- "@formatjs/intl-relativetimeformat": "10.0.1",
- "axios": "0.27.2",
- "axios-cache-interceptor": "0.10.7",
- "form-urlencoded": "4.1.4",
- "glob": "7.2.3",
- "history": "4.10.1",
- "i18n-iso-countries": "4.3.1",
- "jwt-decode": "3.1.2",
- "localforage": "1.10.0",
- "localforage-memoryStorageDriver": "0.9.2",
- "lodash.camelcase": "4.3.0",
- "lodash.memoize": "4.1.2",
- "lodash.merge": "4.6.2",
- "lodash.snakecase": "4.1.1",
- "pubsub-js": "1.9.4",
- "react-intl": "^5.25.0",
- "universal-cookie": "4.0.4"
- }
- },
- "@edx/new-relic-source-map-webpack-plugin": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-2.1.0.tgz",
- "integrity": "sha512-OrlvtdsPcWuOm6NBWfUxFE06qdPiu2bf9nU4I9t8Lu7WW6NsosAB5hxm5U+MBMZP2AuVl3FAt0k0lZsu3+ri8Q==",
- "peer": true,
- "requires": {
- "@newrelic/publish-sourcemap": "^5.0.1"
- }
- },
- "@eslint/js": {
- "version": "8.41.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
- "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
- "peer": true
- },
- "@svgr/babel-plugin-add-jsx-attribute": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
- "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
- "peer": true,
- "requires": {}
- },
- "@svgr/babel-plugin-replace-jsx-attribute-value": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
- "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
- "peer": true,
- "requires": {}
- },
- "@svgr/babel-plugin-svg-dynamic-title": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
- "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
- "peer": true,
- "requires": {}
- },
- "@svgr/babel-plugin-svg-em-dimensions": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
- "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
- "peer": true,
- "requires": {}
- },
- "@svgr/babel-plugin-transform-react-native-svg": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz",
- "integrity": "sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw==",
- "peer": true,
- "requires": {}
- },
- "@svgr/babel-plugin-transform-svg-component": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
- "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
- "peer": true,
- "requires": {}
- },
- "@svgr/babel-preset": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.0.0.tgz",
- "integrity": "sha512-KLcjiZychInVrhs86OvcYPLTFu9L5XV2vj0XAaE1HwE3J3jLmIzRY8ttdeAg/iFyp8nhavJpafpDZTt+1LIpkQ==",
- "peer": true,
- "requires": {
- "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
- "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
- "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
- "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
- "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
- "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
- "@svgr/babel-plugin-transform-react-native-svg": "8.0.0",
- "@svgr/babel-plugin-transform-svg-component": "8.0.0"
- }
- },
- "@svgr/core": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.0.0.tgz",
- "integrity": "sha512-aJKtc+Pie/rFYsVH/unSkDaZGvEeylNv/s2cP+ta9/rYWxRVvoV/S4Qw65Kmrtah4CBK5PM6ISH9qUH7IJQCng==",
- "peer": true,
- "requires": {
- "@babel/core": "^7.21.3",
- "@svgr/babel-preset": "8.0.0",
- "camelcase": "^6.2.0",
- "cosmiconfig": "^8.1.3",
- "snake-case": "^3.0.4"
- }
- },
- "@svgr/hast-util-to-babel-ast": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
- "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
- "peer": true,
- "requires": {
- "@babel/types": "^7.21.3",
- "entities": "^4.4.0"
- }
- },
- "@svgr/plugin-jsx": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.0.1.tgz",
- "integrity": "sha512-bfCFb+4ZsM3UuKP2t7KmDwn6YV8qVn9HIQJmau6xeQb/iV65Rpi7NBNBWA2hcCd4GKoCqG8hpaaDk5FDR0eH+g==",
- "peer": true,
- "requires": {
- "@babel/core": "^7.21.3",
- "@svgr/babel-preset": "8.0.0",
- "@svgr/hast-util-to-babel-ast": "8.0.0",
- "svg-parser": "^2.0.4"
- }
- },
- "@svgr/plugin-svgo": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.0.1.tgz",
- "integrity": "sha512-29OJ1QmJgnohQHDAgAuY2h21xWD6TZiXji+hnx+W635RiXTAlHTbjrZDktfqzkN0bOeQEtNe+xgq73/XeWFfSg==",
- "peer": true,
- "requires": {
- "cosmiconfig": "^8.1.3",
- "deepmerge": "^4.3.1",
- "svgo": "^3.0.2"
- }
- },
- "@svgr/webpack": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.0.1.tgz",
- "integrity": "sha512-zSoeKcbCmfMXjA11uDuCJb+1LWNb3vy6Qw/VHj0Nfcl3UuqwuoZWknHsBIhCWvi4wU9vPui3aq054qjVyZqY4A==",
- "peer": true,
- "requires": {
- "@babel/core": "^7.21.3",
- "@babel/plugin-transform-react-constant-elements": "^7.21.3",
- "@babel/preset-env": "^7.20.2",
- "@babel/preset-react": "^7.18.6",
- "@babel/preset-typescript": "^7.21.0",
- "@svgr/core": "8.0.0",
- "@svgr/plugin-jsx": "8.0.1",
- "@svgr/plugin-svgo": "8.0.1"
- }
- },
- "@types/html-minifier-terser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
- "peer": true
- },
- "@webassemblyjs/ast": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
- "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
- "peer": true,
- "requires": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
- "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
- "peer": true
- },
- "@webassemblyjs/helper-api-error": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
- "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
- "peer": true
- },
- "@webassemblyjs/helper-buffer": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
- "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
- "peer": true
- },
- "@webassemblyjs/helper-numbers": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
- "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
- "peer": true,
- "requires": {
- "@webassemblyjs/floating-point-hex-parser": "1.11.6",
- "@webassemblyjs/helper-api-error": "1.11.6",
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
- "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
- "peer": true
- },
- "@webassemblyjs/helper-wasm-section": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
- "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
- "peer": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6"
- }
- },
- "@webassemblyjs/ieee754": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
- "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
- "peer": true,
- "requires": {
- "@xtuc/ieee754": "^1.2.0"
- }
- },
- "@webassemblyjs/leb128": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
- "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
- "peer": true,
- "requires": {
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/utf8": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
- "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
- "peer": true
- },
- "@webassemblyjs/wasm-edit": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
- "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
- "peer": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/helper-wasm-section": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6",
- "@webassemblyjs/wasm-opt": "1.11.6",
- "@webassemblyjs/wasm-parser": "1.11.6",
- "@webassemblyjs/wast-printer": "1.11.6"
- }
- },
- "@webassemblyjs/wasm-gen": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
- "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
- "peer": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/ieee754": "1.11.6",
- "@webassemblyjs/leb128": "1.11.6",
- "@webassemblyjs/utf8": "1.11.6"
- }
- },
- "@webassemblyjs/wasm-opt": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
- "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
- "peer": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6",
- "@webassemblyjs/wasm-parser": "1.11.6"
- }
- },
- "@webassemblyjs/wasm-parser": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
- "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
- "peer": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-api-error": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/ieee754": "1.11.6",
- "@webassemblyjs/leb128": "1.11.6",
- "@webassemblyjs/utf8": "1.11.6"
- }
- },
- "@webassemblyjs/wast-printer": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
- "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
- "peer": true,
- "requires": {
- "@webassemblyjs/ast": "1.11.6",
- "@xtuc/long": "4.2.2"
- }
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "peer": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "peer": true
- },
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "peer": true
- },
- "axios": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
- "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
- "requires": {
- "follow-redirects": "^1.14.9",
- "form-data": "^4.0.0"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "peer": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "clean-css": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz",
- "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==",
- "peer": true,
- "requires": {
- "source-map": "~0.6.0"
- }
- },
- "clean-webpack-plugin": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz",
- "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==",
- "peer": true,
- "requires": {
- "del": "^4.1.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "peer": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "peer": true
- },
- "colorette": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
- "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
- "peer": true
- },
- "cosmiconfig": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
- "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==",
- "peer": true,
- "requires": {
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0"
- }
- },
- "css-loader": {
- "version": "5.2.7",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
- "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
- "peer": true,
- "requires": {
- "icss-utils": "^5.1.0",
- "loader-utils": "^2.0.0",
- "postcss": "^8.2.15",
- "postcss-modules-extract-imports": "^3.0.0",
- "postcss-modules-local-by-default": "^4.0.0",
- "postcss-modules-scope": "^3.0.0",
- "postcss-modules-values": "^4.0.0",
- "postcss-value-parser": "^4.1.0",
- "schema-utils": "^3.0.0",
- "semver": "^7.3.5"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "peer": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "peer": true,
- "requires": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- }
- },
- "cssnano": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.1.tgz",
- "integrity": "sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==",
- "peer": true,
- "requires": {
- "cssnano-preset-default": "^6.0.1",
- "lilconfig": "^2.1.0"
- }
- },
- "cssnano-preset-default": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.1.tgz",
- "integrity": "sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==",
- "peer": true,
- "requires": {
- "css-declaration-sorter": "^6.3.1",
- "cssnano-utils": "^4.0.0",
- "postcss-calc": "^9.0.0",
- "postcss-colormin": "^6.0.0",
- "postcss-convert-values": "^6.0.0",
- "postcss-discard-comments": "^6.0.0",
- "postcss-discard-duplicates": "^6.0.0",
- "postcss-discard-empty": "^6.0.0",
- "postcss-discard-overridden": "^6.0.0",
- "postcss-merge-longhand": "^6.0.0",
- "postcss-merge-rules": "^6.0.1",
- "postcss-minify-font-values": "^6.0.0",
- "postcss-minify-gradients": "^6.0.0",
- "postcss-minify-params": "^6.0.0",
- "postcss-minify-selectors": "^6.0.0",
- "postcss-normalize-charset": "^6.0.0",
- "postcss-normalize-display-values": "^6.0.0",
- "postcss-normalize-positions": "^6.0.0",
- "postcss-normalize-repeat-style": "^6.0.0",
- "postcss-normalize-string": "^6.0.0",
- "postcss-normalize-timing-functions": "^6.0.0",
- "postcss-normalize-unicode": "^6.0.0",
- "postcss-normalize-url": "^6.0.0",
- "postcss-normalize-whitespace": "^6.0.0",
- "postcss-ordered-values": "^6.0.0",
- "postcss-reduce-initial": "^6.0.0",
- "postcss-reduce-transforms": "^6.0.0",
- "postcss-svgo": "^6.0.0",
- "postcss-unique-selectors": "^6.0.0"
- }
- },
- "cssnano-utils": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.0.tgz",
- "integrity": "sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==",
- "peer": true,
- "requires": {}
- },
- "csso": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
- "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
- "peer": true,
- "requires": {
- "css-tree": "~2.2.0"
- },
- "dependencies": {
- "css-tree": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
- "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
- "peer": true,
- "requires": {
- "mdn-data": "2.0.28",
- "source-map-js": "^1.0.1"
- }
- },
- "mdn-data": {
- "version": "2.0.28",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
- "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
- "peer": true
- }
- }
- },
- "dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "peer": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- },
- "dependencies": {
- "entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "peer": true
- }
- }
- },
- "domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "peer": true,
- "requires": {
- "domelementtype": "^2.2.0"
- }
- },
- "domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "peer": true,
- "requires": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- }
- },
- "dotenv": {
- "version": "8.6.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
- "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
- "peer": true
- },
- "dotenv-webpack": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz",
- "integrity": "sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==",
- "peer": true,
- "requires": {
- "dotenv-defaults": "^2.0.2"
- }
- },
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "peer": true
- },
- "eslint": {
- "version": "8.41.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
- "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
- "peer": true,
- "requires": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.4.0",
- "@eslint/eslintrc": "^2.0.3",
- "@eslint/js": "8.41.0",
- "@humanwhocodes/config-array": "^0.11.8",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@nodelib/fs.walk": "^1.2.8",
- "ajv": "^6.10.0",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
- "debug": "^4.3.2",
- "doctrine": "^3.0.0",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.0",
- "eslint-visitor-keys": "^3.4.1",
- "espree": "^9.5.2",
- "esquery": "^1.4.2",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.0.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
- "strip-ansi": "^6.0.1",
- "strip-json-comments": "^3.1.0",
- "text-table": "^0.2.0"
- },
- "dependencies": {
- "eslint-scope": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
- "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
- "peer": true,
- "requires": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- }
- }
- }
- },
- "filesize": {
- "version": "8.0.7",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
- "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==",
- "peer": true
- },
- "fork-ts-checker-webpack-plugin": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz",
- "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==",
- "peer": true,
- "requires": {
- "@babel/code-frame": "^7.8.3",
- "@types/json-schema": "^7.0.5",
- "chalk": "^4.1.0",
- "chokidar": "^3.4.2",
- "cosmiconfig": "^6.0.0",
- "deepmerge": "^4.2.2",
- "fs-extra": "^9.0.0",
- "glob": "^7.1.6",
- "memfs": "^3.1.2",
- "minimatch": "^3.0.4",
- "schema-utils": "2.7.0",
- "semver": "^7.3.2",
- "tapable": "^1.0.0"
- },
- "dependencies": {
- "cosmiconfig": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
- "peer": true,
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.1.0",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.7.2"
- }
- },
- "schema-utils": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
- "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
- "peer": true,
- "requires": {
- "@types/json-schema": "^7.0.4",
- "ajv": "^6.12.2",
- "ajv-keywords": "^3.4.1"
- }
- },
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "peer": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
- "tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
- "peer": true
- }
- }
- },
- "form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
- },
- "glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "peer": true,
- "requires": {
- "is-glob": "^4.0.3"
- }
- },
- "globals": {
- "version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
- "peer": true,
- "requires": {
- "type-fest": "^0.20.2"
- }
- },
- "globby": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "peer": true,
- "requires": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.2.9",
- "ignore": "^5.2.0",
- "merge2": "^1.4.1",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "peer": true
- }
- }
- },
- "gzip-size": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
- "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
- "peer": true,
- "requires": {
- "duplexer": "^0.1.2"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "peer": true
- },
- "html-minifier-terser": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
- "peer": true,
- "requires": {
- "camel-case": "^4.1.2",
- "clean-css": "^5.2.2",
- "commander": "^8.3.0",
- "he": "^1.2.0",
- "param-case": "^3.0.4",
- "relateurl": "^0.2.7",
- "terser": "^5.10.0"
- },
- "dependencies": {
- "commander": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
- "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
- "peer": true
- }
- }
- },
- "html-webpack-plugin": {
- "version": "5.5.1",
- "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz",
- "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==",
- "peer": true,
- "requires": {
- "@types/html-minifier-terser": "^6.0.0",
- "html-minifier-terser": "^6.0.2",
- "lodash": "^4.17.21",
- "pretty-error": "^4.0.0",
- "tapable": "^2.0.0"
- }
- },
- "htmlparser2": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
- "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
- "peer": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.0.0",
- "domutils": "^2.5.2",
- "entities": "^2.0.0"
- },
- "dependencies": {
- "entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "peer": true
- }
- }
- },
- "immer": {
- "version": "9.0.21",
- "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
- "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
- "peer": true
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "peer": true,
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "peer": true
- },
- "mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "peer": true
- },
- "mini-css-extract-plugin": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz",
- "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==",
- "peer": true,
- "requires": {
- "loader-utils": "^2.0.0",
- "schema-utils": "^3.0.0",
- "webpack-sources": "^1.1.0"
- }
- },
- "open": {
- "version": "8.4.2",
- "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
- "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
- "peer": true,
- "requires": {
- "define-lazy-prop": "^2.0.0",
- "is-docker": "^2.1.1",
- "is-wsl": "^2.2.0"
- }
- },
- "postcss": {
- "version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
- "peer": true,
- "requires": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- },
- "postcss-calc": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
- "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
- "peer": true,
- "requires": {
- "postcss-selector-parser": "^6.0.11",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-colormin": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.0.tgz",
- "integrity": "sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0",
- "colord": "^2.9.1",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-convert-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz",
- "integrity": "sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-custom-media": {
- "version": "9.1.4",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.4.tgz",
- "integrity": "sha512-4A7WEG3iIyKwfpxL5bkuSlHoHHGRTHl0212Z3uvpwJPyVfZJlkZAQNNgVC+oogrJgksDnfKyuuMbG6HafZPW8Q==",
- "peer": true,
- "requires": {
- "@csstools/cascade-layer-name-parser": "^1.0.2",
- "@csstools/css-parser-algorithms": "^2.1.1",
- "@csstools/css-tokenizer": "^2.1.1",
- "@csstools/media-query-list-parser": "^2.1.0"
- }
- },
- "postcss-discard-comments": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz",
- "integrity": "sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==",
- "peer": true,
- "requires": {}
- },
- "postcss-discard-duplicates": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz",
- "integrity": "sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==",
- "peer": true,
- "requires": {}
- },
- "postcss-discard-empty": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz",
- "integrity": "sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==",
- "peer": true,
- "requires": {}
- },
- "postcss-discard-overridden": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz",
- "integrity": "sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==",
- "peer": true,
- "requires": {}
- },
- "postcss-loader": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.2.tgz",
- "integrity": "sha512-c7qDlXErX6n0VT+LUsW+nwefVtTu3ORtVvK8EXuUIDcxo+b/euYqpuHlJAvePb0Af5e8uMjR/13e0lTuYifaig==",
- "peer": true,
- "requires": {
- "cosmiconfig": "^8.1.3",
- "jiti": "^1.18.2",
- "klona": "^2.0.6",
- "semver": "^7.3.8"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "peer": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "postcss-merge-longhand": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz",
- "integrity": "sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0",
- "stylehacks": "^6.0.0"
- }
- },
- "postcss-merge-rules": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.1.tgz",
- "integrity": "sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0",
- "cssnano-utils": "^4.0.0",
- "postcss-selector-parser": "^6.0.5"
- }
- },
- "postcss-minify-font-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz",
- "integrity": "sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-gradients": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz",
- "integrity": "sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==",
- "peer": true,
- "requires": {
- "colord": "^2.9.1",
- "cssnano-utils": "^4.0.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-params": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz",
- "integrity": "sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "cssnano-utils": "^4.0.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-minify-selectors": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz",
- "integrity": "sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==",
- "peer": true,
- "requires": {
- "postcss-selector-parser": "^6.0.5"
- }
- },
- "postcss-normalize-charset": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz",
- "integrity": "sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==",
- "peer": true,
- "requires": {}
- },
- "postcss-normalize-display-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz",
- "integrity": "sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-positions": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz",
- "integrity": "sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-repeat-style": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz",
- "integrity": "sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-string": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz",
- "integrity": "sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-timing-functions": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz",
- "integrity": "sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-unicode": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz",
- "integrity": "sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-url": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz",
- "integrity": "sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-normalize-whitespace": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz",
- "integrity": "sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-ordered-values": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz",
- "integrity": "sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==",
- "peer": true,
- "requires": {
- "cssnano-utils": "^4.0.0",
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-reduce-initial": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz",
- "integrity": "sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "caniuse-api": "^3.0.0"
- }
- },
- "postcss-reduce-transforms": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz",
- "integrity": "sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0"
- }
- },
- "postcss-rtlcss": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-4.0.6.tgz",
- "integrity": "sha512-YNm6g2Y7Gngqtrpq3GC7cUkzH5Gq7aB+Lw9MSgF9s2ro1BDY7W4zqnd15g2ueatUUpSTg2/F5KDjQoTdjhbAKg==",
- "peer": true,
- "requires": {
- "rtlcss": "4.1.0"
- }
- },
- "postcss-svgo": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.0.tgz",
- "integrity": "sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==",
- "peer": true,
- "requires": {
- "postcss-value-parser": "^4.2.0",
- "svgo": "^3.0.2"
- }
- },
- "postcss-unique-selectors": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz",
- "integrity": "sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==",
- "peer": true,
- "requires": {
- "postcss-selector-parser": "^6.0.5"
- }
- },
- "pretty-error": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
- "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
- "peer": true,
- "requires": {
- "lodash": "^4.17.20",
- "renderkid": "^3.0.0"
- }
- },
- "react-dev-utils": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
- "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==",
- "peer": true,
- "requires": {
- "@babel/code-frame": "^7.16.0",
- "address": "^1.1.2",
- "browserslist": "^4.18.1",
- "chalk": "^4.1.2",
- "cross-spawn": "^7.0.3",
- "detect-port-alt": "^1.1.6",
- "escape-string-regexp": "^4.0.0",
- "filesize": "^8.0.6",
- "find-up": "^5.0.0",
- "fork-ts-checker-webpack-plugin": "^6.5.0",
- "global-modules": "^2.0.0",
- "globby": "^11.0.4",
- "gzip-size": "^6.0.0",
- "immer": "^9.0.7",
- "is-root": "^2.1.0",
- "loader-utils": "^3.2.0",
- "open": "^8.4.0",
- "pkg-up": "^3.1.0",
- "prompts": "^2.4.2",
- "react-error-overlay": "^6.0.11",
- "recursive-readdir": "^2.2.2",
- "shell-quote": "^1.7.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
- },
- "dependencies": {
- "loader-utils": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
- "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
- "peer": true
- }
- }
- },
- "renderkid": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
- "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
- "peer": true,
- "requires": {
- "css-select": "^4.1.3",
- "dom-converter": "^0.2.0",
- "htmlparser2": "^6.1.0",
- "lodash": "^4.17.21",
- "strip-ansi": "^6.0.1"
- },
- "dependencies": {
- "css-select": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "peer": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "^6.0.1",
- "domhandler": "^4.3.1",
- "domutils": "^2.8.0",
- "nth-check": "^2.0.1"
- }
- }
- }
- },
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "peer": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "rtlcss": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.0.tgz",
- "integrity": "sha512-W+N4hh0nVqVrrn3mRkHakxpB+c9cQ4CRT67O39kgA+1DjyhrdsqyCqIuHXyvWaXn4/835n+oX3fYJCi4+G/06A==",
- "peer": true,
- "requires": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0",
- "postcss": "^8.4.21",
- "strip-json-comments": "^3.1.1"
- }
- },
- "sass": {
- "version": "1.62.1",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz",
- "integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==",
- "peer": true,
- "requires": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
- "source-map-js": ">=0.6.2 <2.0.0"
- }
- },
- "sass-loader": {
- "version": "13.3.1",
- "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.1.tgz",
- "integrity": "sha512-cBTxmgyVA1nXPvIK4brjJMXOMJ2v2YrQEuHqLw3LylGb3gsR6jAvdjHMcy/+JGTmmIF9SauTrLLR7bsWDMWqgg==",
- "peer": true,
- "requires": {
- "klona": "^2.0.6",
- "neo-async": "^2.6.2"
- }
- },
- "schema-utils": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz",
- "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==",
- "peer": true,
- "requires": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- }
- },
- "shell-quote": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
- "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
- "peer": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "peer": true
- },
- "style-loader": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz",
- "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==",
- "peer": true,
- "requires": {}
- },
- "stylehacks": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.0.tgz",
- "integrity": "sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==",
- "peer": true,
- "requires": {
- "browserslist": "^4.21.4",
- "postcss-selector-parser": "^6.0.4"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "peer": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "svgo": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
- "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
- "peer": true,
- "requires": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^5.1.0",
- "css-tree": "^2.2.1",
- "csso": "^5.0.5",
- "picocolors": "^1.0.0"
- },
- "dependencies": {
- "commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "peer": true
- }
- }
- },
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "peer": true
- },
- "terser": {
- "version": "5.17.7",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz",
- "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==",
- "peer": true,
- "requires": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "peer": true
- }
- }
- },
- "type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "peer": true
- },
- "webpack": {
- "version": "5.84.1",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.1.tgz",
- "integrity": "sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg==",
- "peer": true,
- "requires": {
- "@types/eslint-scope": "^3.7.3",
- "@types/estree": "^1.0.0",
- "@webassemblyjs/ast": "^1.11.5",
- "@webassemblyjs/wasm-edit": "^1.11.5",
- "@webassemblyjs/wasm-parser": "^1.11.5",
- "acorn": "^8.7.1",
- "acorn-import-assertions": "^1.9.0",
- "browserslist": "^4.14.5",
- "chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.14.1",
- "es-module-lexer": "^1.2.1",
- "eslint-scope": "5.1.1",
- "events": "^3.2.0",
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.2.9",
- "json-parse-even-better-errors": "^2.3.1",
- "loader-runner": "^4.2.0",
- "mime-types": "^2.1.27",
- "neo-async": "^2.6.2",
- "schema-utils": "^3.1.2",
- "tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.7",
- "watchpack": "^2.4.0",
- "webpack-sources": "^3.2.3"
- },
- "dependencies": {
- "webpack-sources": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
- "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
- "peer": true
- }
- }
- },
- "webpack-cli": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.3.tgz",
- "integrity": "sha512-MTuk7NUMvEHQUSXCpvUrF1q2p0FJS40dPFfqQvG3jTWcgv/8plBNz2Kv2HXZiLGPnfmSAA5uCtCILO1JBmmkfw==",
- "peer": true,
- "requires": {
- "@discoveryjs/json-ext": "^0.5.0",
- "@webpack-cli/configtest": "^2.1.1",
- "@webpack-cli/info": "^2.0.2",
- "@webpack-cli/serve": "^2.0.5",
- "colorette": "^2.0.14",
- "commander": "^10.0.1",
- "cross-spawn": "^7.0.3",
- "envinfo": "^7.7.3",
- "fastest-levenshtein": "^1.0.12",
- "import-local": "^3.0.2",
- "interpret": "^3.1.1",
- "rechoir": "^0.8.0",
- "webpack-merge": "^5.7.3"
- },
- "dependencies": {
- "commander": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
- "peer": true
- }
- }
- },
- "webpack-dev-server": {
- "version": "4.15.0",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz",
- "integrity": "sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ==",
- "peer": true,
- "requires": {
- "@types/bonjour": "^3.5.9",
- "@types/connect-history-api-fallback": "^1.3.5",
- "@types/express": "^4.17.13",
- "@types/serve-index": "^1.9.1",
- "@types/serve-static": "^1.13.10",
- "@types/sockjs": "^0.3.33",
- "@types/ws": "^8.5.1",
- "ansi-html-community": "^0.0.8",
- "bonjour-service": "^1.0.11",
- "chokidar": "^3.5.3",
- "colorette": "^2.0.10",
- "compression": "^1.7.4",
- "connect-history-api-fallback": "^2.0.0",
- "default-gateway": "^6.0.3",
- "express": "^4.17.3",
- "graceful-fs": "^4.2.6",
- "html-entities": "^2.3.2",
- "http-proxy-middleware": "^2.0.3",
- "ipaddr.js": "^2.0.1",
- "launch-editor": "^2.6.0",
- "open": "^8.0.9",
- "p-retry": "^4.5.0",
- "rimraf": "^3.0.2",
- "schema-utils": "^4.0.0",
- "selfsigned": "^2.1.1",
- "serve-index": "^1.9.1",
- "sockjs": "^0.3.24",
- "spdy": "^4.0.2",
- "webpack-dev-middleware": "^5.3.1",
- "ws": "^8.13.0"
- },
- "dependencies": {
- "ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "peer": true,
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- }
- },
- "ajv-keywords": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "peer": true,
- "requires": {
- "fast-deep-equal": "^3.1.3"
- }
- },
- "schema-utils": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
- "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
- "peer": true,
- "requires": {
- "@types/json-schema": "^7.0.9",
- "ajv": "^8.9.0",
- "ajv-formats": "^2.1.1",
- "ajv-keywords": "^5.1.0"
- }
- }
- }
- },
- "webpack-merge": {
- "version": "5.9.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
- "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==",
- "peer": true,
- "requires": {
- "clone-deep": "^4.0.1",
- "wildcard": "^2.0.0"
- }
- },
- "ws": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
- "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
- "peer": true,
- "requires": {}
- }
- }
- },
- "fs-constants": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
- },
- "fs-extra": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
- "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
- "requires": {
- "at-least-node": "^1.0.0",
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- }
- },
- "fs-monkey": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
- "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q=="
- },
- "fs-readdir-recursive": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
- "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA=="
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
- },
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "optional": true
- },
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
- },
- "function.prototype.name": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
- "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.3",
- "es-abstract": "^1.19.0",
- "functions-have-names": "^1.2.2"
- }
- },
- "functions-have-names": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
- },
- "gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
- },
- "get-intrinsic": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
- "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
- "requires": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-proto": "^1.0.1",
- "has-symbols": "^1.0.3"
- }
- },
- "get-nonce": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
- "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="
- },
- "get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="
- },
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "get-symbol-description": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
- "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
- "requires": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.1"
- }
- },
- "get-value": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="
- },
- "github-from-package": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
- },
- "glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "glob-to-regexp": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
- },
- "global-modules": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
- "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
- "requires": {
- "global-prefix": "^3.0.0"
- }
- },
- "global-prefix": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
- "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
- "requires": {
- "ini": "^1.3.5",
- "kind-of": "^6.0.2",
- "which": "^1.3.1"
- },
- "dependencies": {
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "requires": {
- "isexe": "^2.0.0"
- }
- }
- }
- },
- "globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
- },
- "globby": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
- "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
- "requires": {
- "array-union": "^1.0.1",
- "glob": "^7.0.3",
- "object-assign": "^4.0.1",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "dependencies": {
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
- }
- }
- },
- "gopd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "requires": {
- "get-intrinsic": "^1.1.3"
- }
- },
- "graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
- },
- "grapheme-splitter": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
- },
- "graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "peer": true
- },
- "growly": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
- "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
- "optional": true
- },
- "gzip-size": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
- "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==",
- "dev": true,
- "requires": {
- "duplexer": "^0.1.1",
- "pify": "^4.0.1"
- }
- },
- "handle-thing": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
- "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg=="
- },
- "harmony-reflect": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
- "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g=="
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-bigints": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
- "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
- },
- "has-property-descriptors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
- "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
- "requires": {
- "get-intrinsic": "^1.1.1"
- }
- },
- "has-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
- "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
- },
- "has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
- },
- "has-tostringtag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
- "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
- "requires": {
- "has-symbols": "^1.0.2"
- }
- },
- "has-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
- "requires": {
- "get-value": "^2.0.6",
- "has-values": "^1.0.0",
- "isobject": "^3.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "has-values": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
- "requires": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
- },
- "dependencies": {
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
- },
- "history": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
- "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
- "requires": {
- "@babel/runtime": "^7.1.2",
- "loose-envify": "^1.2.0",
- "resolve-pathname": "^3.0.0",
- "tiny-invariant": "^1.0.2",
- "tiny-warning": "^1.0.0",
- "value-equal": "^1.0.1"
- }
- },
- "hoist-non-react-statics": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
- "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
- "requires": {
- "react-is": "^16.7.0"
- }
- },
- "hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
- },
- "hpack.js": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
- "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
- "requires": {
- "inherits": "^2.0.1",
- "obuf": "^1.0.0",
- "readable-stream": "^2.0.1",
- "wbuf": "^1.1.0"
- }
- },
- "html-dom-parser": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-3.1.2.tgz",
- "integrity": "sha512-mLTtl3pVn3HnqZSZzW3xVs/mJAKrG1yIw3wlp+9bdoZHHLaBRvELdpfShiPVLyjPypq1Fugv2KMDoGHW4lVXnw==",
- "requires": {
- "domhandler": "5.0.3",
- "htmlparser2": "8.0.1"
- }
- },
- "html-element-map": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.1.tgz",
- "integrity": "sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==",
- "dev": true,
- "requires": {
- "array.prototype.filter": "^1.0.0",
- "call-bind": "^1.0.2"
- }
- },
- "html-encoding-sniffer": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
- "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
- "requires": {
- "whatwg-encoding": "^1.0.5"
- }
- },
- "html-entities": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
- "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
- },
- "html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="
- },
- "html-minifier-terser": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
- "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==",
- "peer": true,
- "requires": {
- "camel-case": "^4.1.1",
- "clean-css": "^4.2.3",
- "commander": "^4.1.1",
- "he": "^1.2.0",
- "param-case": "^3.0.3",
- "relateurl": "^0.2.7",
- "terser": "^4.6.3"
- }
- },
- "html-react-parser": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-3.0.7.tgz",
- "integrity": "sha512-4Nzpp1Lsd6ngOJR8T+Vc4u+Z77OddOgKL3KvwbtA0/U0Yv8v5JF+yewQxIudrdOWGYuO0Borc0vQ2y53pzBAwA==",
- "requires": {
- "domhandler": "5.0.3",
- "html-dom-parser": "3.1.2",
- "react-property": "2.0.0",
- "style-to-js": "1.1.2"
- }
- },
- "html-webpack-plugin": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz",
- "integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==",
- "peer": true,
- "requires": {
- "@types/html-minifier-terser": "^5.0.0",
- "@types/tapable": "^1.0.5",
- "@types/webpack": "^4.41.8",
- "html-minifier-terser": "^5.0.1",
- "loader-utils": "^1.2.3",
- "lodash": "^4.17.15",
- "pretty-error": "^2.1.1",
- "tapable": "^1.1.3",
- "util.promisify": "1.0.0"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "peer": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
- "peer": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- }
- }
- },
- "htmlparser2": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
- "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
- "requires": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "domutils": "^3.0.1",
- "entities": "^4.3.0"
- }
- },
- "http-deceiver": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
- "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw=="
- },
- "http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "requires": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- }
- },
- "http-parser-js": {
- "version": "0.5.8",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
- "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q=="
- },
- "http-proxy": {
- "version": "1.18.1",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
- "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
- "requires": {
- "eventemitter3": "^4.0.0",
- "follow-redirects": "^1.0.0",
- "requires-port": "^1.0.0"
- }
- },
- "http-proxy-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
- "requires": {
- "@tootallnate/once": "1",
- "agent-base": "6",
- "debug": "4"
- }
- },
- "http-proxy-middleware": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
- "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
- "requires": {
- "@types/http-proxy": "^1.17.8",
- "http-proxy": "^1.18.1",
- "is-glob": "^4.0.1",
- "is-plain-obj": "^3.0.0",
- "micromatch": "^4.0.2"
- },
- "dependencies": {
- "is-plain-obj": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
- "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA=="
- }
- }
- },
- "https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "requires": {
- "agent-base": "6",
- "debug": "4"
- }
- },
- "human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
- },
- "husky": {
- "version": "0.14.3",
- "resolved": "https://registry.npmjs.org/husky/-/husky-0.14.3.tgz",
- "integrity": "sha512-e21wivqHpstpoiWA/Yi8eFti8E+sQDSS53cpJsPptPs295QTOQR0ZwnHo2TXy1XOpZFD9rPOd3NpmqTK6uMLJA==",
- "dev": true,
- "requires": {
- "is-ci": "^1.0.10",
- "normalize-path": "^1.0.0",
- "strip-indent": "^2.0.0"
- },
- "dependencies": {
- "normalize-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz",
- "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==",
- "dev": true
- }
- }
- },
- "hyphenate-style-name": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
- "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
- },
- "i18n-iso-countries": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-4.3.1.tgz",
- "integrity": "sha512-yxeCvmT8yO1p/epv93c1OHnnYNNMOX6NUNpNfuvzSIcDyripS7OGeKXgzYGd5QI31UK+GBrMG0nPFNv0jrHggw==",
- "requires": {
- "diacritics": "^1.3.0"
- }
- },
- "iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "icss-utils": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
- "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
- "requires": {}
- },
- "identity-obj-proxy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
- "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
- "requires": {
- "harmony-reflect": "^1.4.6"
- }
- },
- "ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
- },
- "ignore": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
- "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="
- },
- "image-minimizer-webpack-plugin": {
- "version": "3.8.2",
- "resolved": "https://registry.npmjs.org/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.2.tgz",
- "integrity": "sha512-l3nDq/c15y4ViTPtICG6lbFp77SoycSnR1hT/n3ER76uol//OpRptCDl7U1qiDSSEy2AcqPD1T7isRQ8TK27Cw==",
- "requires": {
- "schema-utils": "^4.0.0",
- "serialize-javascript": "^6.0.1"
- },
- "dependencies": {
- "ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- }
- },
- "ajv-keywords": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "requires": {
- "fast-deep-equal": "^3.1.3"
- }
- },
- "json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
- },
- "schema-utils": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
- "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
- "requires": {
- "@types/json-schema": "^7.0.9",
- "ajv": "^8.9.0",
- "ajv-formats": "^2.1.1",
- "ajv-keywords": "^5.1.0"
- }
- }
- }
- },
- "immediate": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
- "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
- },
- "immer": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz",
- "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==",
- "dev": true
- },
- "immutable": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
- "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ=="
- },
- "import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "requires": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "dependencies": {
- "resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
- }
- }
- },
- "import-local": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
- "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
- "requires": {
- "pkg-dir": "^4.2.0",
- "resolve-cwd": "^3.0.0"
- }
- },
- "imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
- },
- "indent-string": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
- "dev": true
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
- },
- "inline-style-parser": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
- "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="
- },
- "internal-slot": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
- "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
- "requires": {
- "get-intrinsic": "^1.2.0",
- "has": "^1.0.3",
- "side-channel": "^1.0.4"
- }
- },
- "interpret": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
- "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ=="
- },
- "intl-messageformat": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz",
- "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/fast-memoize": "1.2.1",
- "@formatjs/icu-messageformat-parser": "2.1.0",
- "tslib": "^2.1.0"
- }
- },
- "intl-messageformat-parser": {
- "version": "5.5.1",
- "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz",
- "integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==",
- "requires": {
- "@formatjs/intl-numberformat": "^5.5.2"
- }
- },
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "ipaddr.js": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
- "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng=="
- },
- "is-accessor-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-alphabetical": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
- "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="
- },
- "is-alphanumerical": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
- "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
- "requires": {
- "is-alphabetical": "^1.0.0",
- "is-decimal": "^1.0.0"
- }
- },
- "is-arguments": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
- "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
- "requires": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-array-buffer": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
- "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
- "requires": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.2.0",
- "is-typed-array": "^1.1.10"
- }
- },
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
- },
- "is-bigint": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
- "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
- "requires": {
- "has-bigints": "^1.0.1"
- }
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-boolean-object": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
- "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
- "requires": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-buffer": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
- "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="
- },
- "is-callable": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
- },
- "is-ci": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
- "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
- "dev": true,
- "requires": {
- "ci-info": "^1.5.0"
- }
- },
- "is-core-module": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
- "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
- "requires": {
- "has": "^1.0.3"
- }
- },
- "is-data-descriptor": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
- "requires": {
- "kind-of": "^6.0.0"
- }
- },
- "is-date-object": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
- "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-decimal": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
- "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="
- },
- "is-descriptor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
- "requires": {
- "is-accessor-descriptor": "^1.0.0",
- "is-data-descriptor": "^1.0.0",
- "kind-of": "^6.0.2"
- }
- },
- "is-docker": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
- },
- "is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "requires": {
- "is-plain-object": "^2.0.4"
- }
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
- },
- "is-generator-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ=="
- },
- "is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-hexadecimal": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
- "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
- },
- "is-invalid-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz",
- "integrity": "sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==",
- "requires": {
- "is-glob": "^2.0.0"
- },
- "dependencies": {
- "is-extglob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
- "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww=="
- },
- "is-glob": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
- "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==",
- "requires": {
- "is-extglob": "^1.0.0"
- }
- }
- }
- },
- "is-map": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
- "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg=="
- },
- "is-negative-zero": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
- "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
- },
- "is-number-object": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
- "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-path-cwd": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
- "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ=="
- },
- "is-path-in-cwd": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
- "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
- "requires": {
- "is-path-inside": "^2.1.0"
- },
- "dependencies": {
- "is-path-inside": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
- "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
- "requires": {
- "path-is-inside": "^1.0.2"
- }
- }
- }
- },
- "is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="
- },
- "is-plain-obj": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
- "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
- "peer": true
- },
- "is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "requires": {
- "isobject": "^3.0.1"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "is-potential-custom-element-name": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
- },
- "is-promise": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
- "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="
- },
- "is-regex": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
- "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
- "requires": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-root": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz",
- "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg=="
- },
- "is-set": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
- "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g=="
- },
- "is-shared-array-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
- "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
- "requires": {
- "call-bind": "^1.0.2"
- }
- },
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="
- },
- "is-string": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
- "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-subset": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
- "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==",
- "dev": true
- },
- "is-symbol": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
- "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
- "requires": {
- "has-symbols": "^1.0.2"
- }
- },
- "is-typed-array": {
- "version": "1.1.10",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
- "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
- "requires": {
- "available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.2",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
- },
- "is-valid-path": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
- "integrity": "sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==",
- "requires": {
- "is-invalid-path": "^0.1.0"
- }
- },
- "is-weakmap": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
- "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA=="
- },
- "is-weakref": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
- "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
- "requires": {
- "call-bind": "^1.0.2"
- }
- },
- "is-weakset": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
- "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
- "requires": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.1"
- }
- },
- "is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
- },
- "is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "requires": {
- "is-docker": "^2.0.0"
- }
- },
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
- },
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
- "requires": {
- "isarray": "1.0.0"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- }
- }
- },
- "istanbul-lib-coverage": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw=="
- },
- "istanbul-lib-instrument": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
- "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
- "requires": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
- }
- },
- "istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
- "requires": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
- "supports-color": "^7.1.0"
- },
- "dependencies": {
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "requires": {
- "semver": "^6.0.0"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "requires": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "istanbul-reports": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
- "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
- "requires": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- }
- },
- "jest": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz",
- "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==",
- "requires": {
- "@jest/core": "^26.6.3",
- "import-local": "^3.0.2",
- "jest-cli": "^26.6.3"
- }
- },
- "jest-canvas-mock": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz",
- "integrity": "sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ==",
- "dev": true,
- "requires": {
- "cssfontparser": "^1.2.1",
- "moo-color": "^1.0.2"
- }
- },
- "jest-changed-files": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz",
- "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==",
- "requires": {
- "@jest/types": "^26.6.2",
- "execa": "^4.0.0",
- "throat": "^5.0.0"
- },
- "dependencies": {
- "execa": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
- "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
- "requires": {
- "cross-spawn": "^7.0.0",
- "get-stream": "^5.0.0",
- "human-signals": "^1.1.1",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.0",
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2",
- "strip-final-newline": "^2.0.0"
- }
- },
- "get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "human-signals": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
- "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
- },
- "is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
- },
- "npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "requires": {
- "path-key": "^3.0.0"
- }
- }
- }
- },
- "jest-cli": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz",
- "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==",
- "requires": {
- "@jest/core": "^26.6.3",
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.4",
- "import-local": "^3.0.2",
- "is-ci": "^2.0.0",
- "jest-config": "^26.6.3",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "prompts": "^2.0.1",
- "yargs": "^15.4.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
- },
- "cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
- },
- "yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "requires": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- }
- },
- "yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- }
- }
- },
- "jest-config": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz",
- "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==",
- "requires": {
- "@babel/core": "^7.1.0",
- "@jest/test-sequencer": "^26.6.3",
- "@jest/types": "^26.6.2",
- "babel-jest": "^26.6.3",
- "chalk": "^4.0.0",
- "deepmerge": "^4.2.2",
- "glob": "^7.1.1",
- "graceful-fs": "^4.2.4",
- "jest-environment-jsdom": "^26.6.2",
- "jest-environment-node": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "jest-jasmine2": "^26.6.3",
- "jest-regex-util": "^26.0.0",
- "jest-resolve": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-environment-jsdom": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz",
- "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==",
- "requires": {
- "@jest/environment": "^26.6.2",
- "@jest/fake-timers": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "jest-mock": "^26.6.2",
- "jest-util": "^26.6.2",
- "jsdom": "^16.4.0"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-diff": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz",
- "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^29.3.1",
- "jest-get-type": "^29.2.0",
- "pretty-format": "^29.3.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.0.0",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- }
- }
- },
- "react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-docblock": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz",
- "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==",
- "requires": {
- "detect-newline": "^3.0.0"
- }
- },
- "jest-each": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz",
- "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==",
- "requires": {
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "jest-util": "^26.6.2",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-environment-jsdom": {
- "version": "26.6.1",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.1.tgz",
- "integrity": "sha512-A17RiXuHYNVlkM+3QNcQ6n5EZyAc6eld8ra9TW26luounGWpku4tj03uqRgHJCI1d4uHr5rJiuCH5JFRtdmrcA==",
- "requires": {
- "@jest/environment": "^26.6.1",
- "@jest/fake-timers": "^26.6.1",
- "@jest/types": "^26.6.1",
- "@types/node": "*",
- "jest-mock": "^26.6.1",
- "jest-util": "^26.6.1",
- "jsdom": "^16.4.0"
- }
- },
- "jest-environment-node": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz",
- "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==",
- "requires": {
- "@jest/environment": "^26.6.2",
- "@jest/fake-timers": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "jest-mock": "^26.6.2",
- "jest-util": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "29.2.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz",
- "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==",
- "dev": true
- },
- "jest-haste-map": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
- "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/graceful-fs": "^4.1.2",
- "@types/node": "*",
- "anymatch": "^3.0.3",
- "fb-watchman": "^2.0.0",
- "fsevents": "^2.1.2",
- "graceful-fs": "^4.2.4",
- "jest-regex-util": "^26.0.0",
- "jest-serializer": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-worker": "^26.6.2",
- "micromatch": "^4.0.2",
- "sane": "^4.0.3",
- "walker": "^1.0.7"
- }
- },
- "jest-jasmine2": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz",
- "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==",
- "requires": {
- "@babel/traverse": "^7.1.0",
- "@jest/environment": "^26.6.2",
- "@jest/source-map": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "co": "^4.6.0",
- "expect": "^26.6.2",
- "is-generator-fn": "^2.0.0",
- "jest-each": "^26.6.2",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-runtime": "^26.6.3",
- "jest-snapshot": "^26.6.2",
- "jest-util": "^26.6.2",
- "pretty-format": "^26.6.2",
- "throat": "^5.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "diff-sequences": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q=="
- },
- "expect": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-styles": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-regex-util": "^26.0.0"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-diff": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- },
- "jest-matcher-utils": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-leak-detector": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz",
- "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==",
- "requires": {
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- }
- }
- },
- "jest-localstorage-mock": {
- "version": "2.4.22",
- "resolved": "https://registry.npmjs.org/jest-localstorage-mock/-/jest-localstorage-mock-2.4.22.tgz",
- "integrity": "sha512-60PWSDFQOS5v7JzSmYLM3dPLg0JLl+2Vc4lIEz/rj2yrXJzegsFLn7anwc5IL0WzJbBa/Las064CHbFg491/DQ==",
- "dev": true
- },
- "jest-matcher-utils": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz",
- "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^29.3.1",
- "jest-get-type": "^29.2.0",
- "pretty-format": "^29.3.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.0.0",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- }
- }
- },
- "react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-message-util": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz",
- "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^29.3.1",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^29.3.1",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "dependencies": {
- "@jest/types": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz",
- "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.0.0",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.13",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz",
- "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "pretty-format": {
- "version": "29.3.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
- "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^29.0.0",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- }
- }
- },
- "react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-mock": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz",
- "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==",
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/node": "*"
- }
- },
- "jest-pnp-resolver": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
- "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
- "requires": {}
- },
- "jest-regex-util": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz",
- "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A=="
- },
- "jest-resolve": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz",
- "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==",
- "requires": {
- "@jest/types": "^26.6.2",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "jest-pnp-resolver": "^1.2.2",
- "jest-util": "^26.6.2",
- "read-pkg-up": "^7.0.1",
- "resolve": "^1.18.1",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-resolve-dependencies": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz",
- "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==",
- "requires": {
- "@jest/types": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-snapshot": "^26.6.2"
- }
- },
- "jest-runner": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz",
- "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==",
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/environment": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "emittery": "^0.7.1",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.4",
- "jest-config": "^26.6.3",
- "jest-docblock": "^26.0.0",
- "jest-haste-map": "^26.6.2",
- "jest-leak-detector": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-resolve": "^26.6.2",
- "jest-runtime": "^26.6.3",
- "jest-util": "^26.6.2",
- "jest-worker": "^26.6.2",
- "source-map-support": "^0.5.6",
- "throat": "^5.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-runtime": {
- "version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz",
- "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==",
- "requires": {
- "@jest/console": "^26.6.2",
- "@jest/environment": "^26.6.2",
- "@jest/fake-timers": "^26.6.2",
- "@jest/globals": "^26.6.2",
- "@jest/source-map": "^26.6.2",
- "@jest/test-result": "^26.6.2",
- "@jest/transform": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/yargs": "^15.0.0",
- "chalk": "^4.0.0",
- "cjs-module-lexer": "^0.6.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.4",
- "jest-config": "^26.6.3",
- "jest-haste-map": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-mock": "^26.6.2",
- "jest-regex-util": "^26.0.0",
- "jest-resolve": "^26.6.2",
- "jest-snapshot": "^26.6.2",
- "jest-util": "^26.6.2",
- "jest-validate": "^26.6.2",
- "slash": "^3.0.0",
- "strip-bom": "^4.0.0",
- "yargs": "^15.4.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
- },
- "yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "requires": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- }
- },
- "yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- }
- }
- },
- "jest-serializer": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz",
- "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==",
- "requires": {
- "@types/node": "*",
- "graceful-fs": "^4.2.4"
- }
- },
- "jest-snapshot": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz",
- "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==",
- "requires": {
- "@babel/types": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/babel__traverse": "^7.0.4",
- "@types/prettier": "^2.0.0",
- "chalk": "^4.0.0",
- "expect": "^26.6.2",
- "graceful-fs": "^4.2.4",
- "jest-diff": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "jest-haste-map": "^26.6.2",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-resolve": "^26.6.2",
- "natural-compare": "^1.4.0",
- "pretty-format": "^26.6.2",
- "semver": "^7.3.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "diff-sequences": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q=="
- },
- "expect": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-styles": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "jest-matcher-utils": "^26.6.2",
- "jest-message-util": "^26.6.2",
- "jest-regex-util": "^26.0.0"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-diff": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- },
- "jest-matcher-utils": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^26.6.2",
- "jest-get-type": "^26.3.0",
- "pretty-format": "^26.6.2"
- }
- },
- "jest-message-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "@jest/types": "^26.6.2",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "micromatch": "^4.0.2",
- "pretty-format": "^26.6.2",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.2"
- }
- },
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-util": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
- "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
- "requires": {
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.4",
- "is-ci": "^2.0.0",
- "micromatch": "^4.0.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "is-ci": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
- "requires": {
- "ci-info": "^2.0.0"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-validate": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz",
- "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==",
- "requires": {
- "@jest/types": "^26.6.2",
- "camelcase": "^6.0.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^26.3.0",
- "leven": "^3.1.0",
- "pretty-format": "^26.6.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-get-type": {
- "version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-watcher": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz",
- "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==",
- "requires": {
- "@jest/test-result": "^26.6.2",
- "@jest/types": "^26.6.2",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "jest-util": "^26.6.2",
- "string-length": "^4.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jest-worker": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
- "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
- "requires": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^7.0.0"
- },
- "dependencies": {
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "jiti": {
- "version": "1.18.2",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
- "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
- "peer": true
- },
- "jquery": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz",
- "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==",
- "peer": true
- },
- "js-sdsl": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
- "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ=="
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
- },
- "js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- }
- },
- "jsdom": {
- "version": "16.7.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
- "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
- "requires": {
- "abab": "^2.0.5",
- "acorn": "^8.2.4",
- "acorn-globals": "^6.0.0",
- "cssom": "^0.4.4",
- "cssstyle": "^2.3.0",
- "data-urls": "^2.0.0",
- "decimal.js": "^10.2.1",
- "domexception": "^2.0.1",
- "escodegen": "^2.0.0",
- "form-data": "^3.0.0",
- "html-encoding-sniffer": "^2.0.1",
- "http-proxy-agent": "^4.0.1",
- "https-proxy-agent": "^5.0.0",
- "is-potential-custom-element-name": "^1.0.1",
- "nwsapi": "^2.2.0",
- "parse5": "6.0.1",
- "saxes": "^5.0.1",
- "symbol-tree": "^3.2.4",
- "tough-cookie": "^4.0.0",
- "w3c-hr-time": "^1.0.2",
- "w3c-xmlserializer": "^2.0.0",
- "webidl-conversions": "^6.1.0",
- "whatwg-encoding": "^1.0.5",
- "whatwg-mimetype": "^2.3.0",
- "whatwg-url": "^8.5.0",
- "ws": "^7.4.6",
- "xml-name-validator": "^3.0.0"
- },
- "dependencies": {
- "parse5": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
- "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
- }
- }
- },
- "jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
- },
- "json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
- },
- "json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
- },
- "json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
- },
- "json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
- },
- "jsonfile": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
- "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
- "requires": {
- "graceful-fs": "^4.1.6",
- "universalify": "^2.0.0"
- }
- },
- "jsx-ast-utils": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
- "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==",
- "requires": {
- "array-includes": "^3.1.5",
- "object.assign": "^4.1.3"
- }
- },
- "just-curry-it": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.2.1.tgz",
- "integrity": "sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg=="
- },
- "jwt-decode": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
- "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
- },
- "kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
- },
- "kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="
- },
- "klona": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
- "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="
- },
- "language-subtag-registry": {
- "version": "0.3.22",
- "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
- "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w=="
- },
- "language-tags": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
- "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
- "requires": {
- "language-subtag-registry": "~0.3.2"
- }
- },
- "launch-editor": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz",
- "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==",
- "requires": {
- "picocolors": "^1.0.0",
- "shell-quote": "^1.7.3"
- },
- "dependencies": {
- "shell-quote": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
- "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="
- }
- }
- },
- "leven": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="
- },
- "levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "requires": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- }
- },
- "lie": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
- "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
- "requires": {
- "immediate": "~3.0.5"
- }
- },
- "lilconfig": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
- "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="
- },
- "line-column": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz",
- "integrity": "sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==",
- "requires": {
- "isarray": "^1.0.0",
- "isobject": "^2.0.0"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- }
- }
- },
- "lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
- },
- "loader-runner": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
- "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="
- },
- "loader-utils": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
- "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^2.1.2"
- }
- },
- "localforage": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
- "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
- "requires": {
- "lie": "3.1.1"
- }
- },
- "localforage-memoryStorageDriver": {
- "version": "0.9.2",
- "resolved": "https://registry.npmjs.org/localforage-memoryStorageDriver/-/localforage-memoryStorageDriver-0.9.2.tgz",
- "integrity": "sha512-DRB4BkkW9o5HIetbsuvtcg98GP7J1JBRDyDMJK13hfr9QsNpnMW6UUWmU9c6bcRg99akR1mGZ/ubUV1Ek0fbpg==",
- "requires": {
- "localforage": ">=1.4.0"
- }
- },
- "locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "requires": {
- "p-locate": "^5.0.0"
- }
- },
- "lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
- },
- "lodash.camelcase": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
- "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
- },
- "lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
- },
- "lodash.escape": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
- "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==",
- "dev": true
- },
- "lodash.flattendeep": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
- "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
- "dev": true
- },
- "lodash.isequal": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
- "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
- "dev": true
- },
- "lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
- },
- "lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
- },
- "lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
- },
- "lodash.snakecase": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
- "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
- },
- "lodash.uniq": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
- },
- "lodash.uniqby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
- "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww=="
- },
- "loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "requires": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- }
- },
- "lower-case": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
- "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
- "requires": {
- "tslib": "^2.0.3"
- }
- },
- "lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "requires": {
- "yallist": "^4.0.0"
- }
- },
- "lz-string": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
- "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ=="
- },
- "mailto-link": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mailto-link/-/mailto-link-2.0.0.tgz",
- "integrity": "sha512-b5FErkZ4t6mpH1IFZSw7Mm2IQHXQ2R0/5Q4xd7Rv8dVkWvE54mFG/UW7HjfFazXFjXTNsM+dSX2tTeIDrV9K9A==",
- "requires": {
- "assert-ok": "~1.0.0",
- "cast-array": "~1.0.1",
- "object-filter": "~1.0.2",
- "query-string": "~7.0.0"
- }
- },
- "make-dir": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
- "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
- "requires": {
- "pify": "^4.0.1",
- "semver": "^5.6.0"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- }
- }
- },
- "make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "devOptional": true
- },
- "makeerror": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
- "requires": {
- "tmpl": "1.0.5"
- }
- },
- "map-cache": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg=="
- },
- "map-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
- "requires": {
- "object-visit": "^1.0.0"
- }
- },
- "matchmediaquery": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz",
- "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==",
- "requires": {
- "css-mediaquery": "^0.1.2"
- }
- },
- "md5": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
- "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
- "requires": {
- "charenc": "0.0.2",
- "crypt": "0.0.2",
- "is-buffer": "~1.1.6"
- },
- "dependencies": {
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- }
- }
- },
- "mdast-util-definitions": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz",
- "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==",
- "requires": {
- "unist-util-visit": "^2.0.0"
- }
- },
- "mdast-util-from-markdown": {
- "version": "0.8.5",
- "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz",
- "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==",
- "requires": {
- "@types/mdast": "^3.0.0",
- "mdast-util-to-string": "^2.0.0",
- "micromark": "~2.11.0",
- "parse-entities": "^2.0.0",
- "unist-util-stringify-position": "^2.0.0"
- }
- },
- "mdast-util-to-hast": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz",
- "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==",
- "requires": {
- "@types/mdast": "^3.0.0",
- "@types/unist": "^2.0.0",
- "mdast-util-definitions": "^4.0.0",
- "mdurl": "^1.0.0",
- "unist-builder": "^2.0.0",
- "unist-util-generated": "^1.0.0",
- "unist-util-position": "^3.0.0",
- "unist-util-visit": "^2.0.0"
- }
- },
- "mdast-util-to-string": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz",
- "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w=="
- },
- "mdn-data": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
- "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
- "dev": true
- },
- "mdurl": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
- "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
- },
- "media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
- },
- "memfs": {
- "version": "3.4.13",
- "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz",
- "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==",
- "requires": {
- "fs-monkey": "^1.0.3"
- }
- },
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
- },
- "merge-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
- },
- "merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
- },
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
- },
- "microevent.ts": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
- "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==",
- "dev": true
- },
- "micromark": {
- "version": "2.11.4",
- "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz",
- "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==",
- "requires": {
- "debug": "^4.0.0",
- "parse-entities": "^2.0.0"
- }
- },
- "micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
- "requires": {
- "braces": "^3.0.2",
- "picomatch": "^2.3.1"
- }
- },
- "mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
- },
- "mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
- },
- "mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "requires": {
- "mime-db": "1.52.0"
- }
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
- },
- "mimic-response": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
- },
- "min-indent": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
- "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
- "dev": true
- },
- "mini-create-react-context": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
- "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
- "requires": {
- "@babel/runtime": "^7.12.1",
- "tiny-warning": "^1.0.3"
- }
- },
- "mini-css-extract-plugin": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.2.tgz",
- "integrity": "sha512-h2LknfX4U1kScXxH8xE9LCOqT5B+068EAj36qicMb8l4dqdJoyHcmWmpd+ueyZfgu/POvIn+teoUnTtei2ikug==",
- "peer": true,
- "requires": {
- "loader-utils": "^1.1.0",
- "normalize-url": "1.9.1",
- "schema-utils": "^1.0.0",
- "webpack-sources": "^1.1.0"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "peer": true,
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
- "peer": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
- "schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
- "peer": true,
- "requires": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
- }
- }
- }
- },
- "minimalistic-assert": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
- },
- "minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "minimist": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
- "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
- },
- "mixin-deep": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
- "requires": {
- "for-in": "^1.0.2",
- "is-extendable": "^1.0.1"
- }
- },
- "mkdirp-classic": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
- },
- "moment": {
- "version": "2.27.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
- "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
- },
- "moo": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
- "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
- "dev": true
- },
- "moo-color": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz",
- "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==",
- "dev": true,
- "requires": {
- "color-name": "^1.1.4"
- },
- "dependencies": {
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- }
- }
- },
- "mrmime": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
- "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw=="
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "multicast-dns": {
- "version": "7.2.5",
- "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
- "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
- "requires": {
- "dns-packet": "^5.2.2",
- "thunky": "^1.0.2"
- }
- },
- "nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA=="
- },
- "nanomatch": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "fragment-cache": "^0.2.1",
- "is-windows": "^1.0.2",
- "kind-of": "^6.0.2",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- }
- },
- "napi-build-utils": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
- "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
- },
- "natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
- },
- "natural-compare-lite": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
- "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
- "dev": true
- },
- "nearley": {
- "version": "2.20.1",
- "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
- "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
- "dev": true,
- "requires": {
- "commander": "^2.19.0",
- "moo": "^0.5.0",
- "railroad-diagrams": "^1.0.0",
- "randexp": "0.4.6"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- }
- }
- },
- "negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
- },
- "neo-async": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
- "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
- },
- "nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
- },
- "no-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
- "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
- "requires": {
- "lower-case": "^2.0.2",
- "tslib": "^2.0.3"
- }
- },
- "node-abi": {
- "version": "3.43.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.43.0.tgz",
- "integrity": "sha512-QB0MMv+tn9Ur2DtJrc8y09n0n6sw88CyDniWSX2cHW10goQXYPK9ZpFJOktDS4ron501edPX6h9i7Pg+RnH5nQ==",
- "requires": {
- "semver": "^7.3.5"
- },
- "dependencies": {
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "node-addon-api": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
- "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
- },
- "node-forge": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
- "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
- },
- "node-int64": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="
- },
- "node-notifier": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz",
- "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==",
- "optional": true,
- "requires": {
- "growly": "^1.3.0",
- "is-wsl": "^2.2.0",
- "semver": "^7.3.2",
- "shellwords": "^0.1.1",
- "uuid": "^8.3.0",
- "which": "^2.0.2"
- },
- "dependencies": {
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "optional": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
- "uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "optional": true
- }
- }
- },
- "node-releases": {
- "version": "2.0.12",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
- "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ=="
- },
- "normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "requires": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- }
- }
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
- },
- "normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="
- },
- "normalize-url": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
- "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==",
- "peer": true,
- "requires": {
- "object-assign": "^4.0.1",
- "prepend-http": "^1.0.0",
- "query-string": "^4.1.0",
- "sort-keys": "^1.0.0"
- },
- "dependencies": {
- "query-string": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
- "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
- "peer": true,
- "requires": {
- "object-assign": "^4.1.0",
- "strict-uri-encode": "^1.0.0"
- }
- }
- }
- },
- "npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
- "requires": {
- "path-key": "^2.0.0"
- },
- "dependencies": {
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="
- }
- }
- },
- "nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "requires": {
- "boolbase": "^1.0.0"
- }
- },
- "nwsapi": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
- "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw=="
- },
- "object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
- },
- "object-code": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/object-code/-/object-code-1.2.4.tgz",
- "integrity": "sha512-uGq4ETUuWe+GA586NXEriiaozNuff+YNFXlpD8cVrM1GoiuTZpCABP+bZCWDrvQDoCiSTyiWAFHD/HF/iwhb2w=="
- },
- "object-copy": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
- "requires": {
- "copy-descriptor": "^0.1.0",
- "define-property": "^0.2.5",
- "kind-of": "^3.0.3"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- },
- "dependencies": {
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
- }
- }
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "object-filter": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz",
- "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA=="
- },
- "object-inspect": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
- "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
- },
- "object-is": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
- "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.3"
- }
- },
- "object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
- },
- "object-visit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
- "requires": {
- "isobject": "^3.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "object.assign": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
- "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "has-symbols": "^1.0.3",
- "object-keys": "^1.1.1"
- }
- },
- "object.entries": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
- "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "object.fromentries": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
- "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "object.getownpropertydescriptors": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz",
- "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==",
- "peer": true,
- "requires": {
- "array.prototype.reduce": "^1.0.5",
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "object.hasown": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz",
- "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==",
- "requires": {
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "object.pick": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
- "requires": {
- "isobject": "^3.0.1"
- },
- "dependencies": {
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "object.values": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
- "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "obuf": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
- "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
- },
- "on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "requires": {
- "ee-first": "1.1.1"
- }
- },
- "on-headers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "requires": {
- "wrappy": "1"
- }
- },
- "onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
- "open": {
- "version": "7.4.2",
- "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
- "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
- "dev": true,
- "requires": {
- "is-docker": "^2.0.0",
- "is-wsl": "^2.1.1"
- }
- },
- "opener": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
- "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
- },
- "optionator": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
- "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
- "requires": {
- "deep-is": "^0.1.3",
- "fast-levenshtein": "^2.0.6",
- "levn": "^0.4.1",
- "prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.3"
- }
- },
- "p-each-series": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
- "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA=="
- },
- "p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="
- },
- "p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "requires": {
- "yocto-queue": "^0.1.0"
- }
- },
- "p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "requires": {
- "p-limit": "^3.0.2"
- }
- },
- "p-map": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
- "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="
- },
- "p-retry": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
- "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
- "requires": {
- "@types/retry": "0.12.0",
- "retry": "^0.13.1"
- }
- },
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
- },
- "param-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
- "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
- "requires": {
- "dot-case": "^3.0.4",
- "tslib": "^2.0.3"
- }
- },
- "parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "requires": {
- "callsites": "^3.0.0"
- }
- },
- "parse-entities": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
- "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
- "requires": {
- "character-entities": "^1.0.0",
- "character-entities-legacy": "^1.0.0",
- "character-reference-invalid": "^1.0.0",
- "is-alphanumerical": "^1.0.0",
- "is-decimal": "^1.0.0",
- "is-hexadecimal": "^1.0.0"
- }
- },
- "parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- }
- },
- "parse-srcset": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
- "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
- },
- "parse5": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz",
- "integrity": "sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==",
- "dev": true,
- "requires": {
- "entities": "^4.4.0"
- }
- },
- "parse5-htmlparser2-tree-adapter": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
- "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
- "dev": true,
- "requires": {
- "domhandler": "^5.0.2",
- "parse5": "^7.0.0"
- }
- },
- "parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
- },
- "pascal-case": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
- "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
- "requires": {
- "no-case": "^3.0.4",
- "tslib": "^2.0.3"
- }
- },
- "pascalcase": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
- },
- "path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="
- },
- "path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
- },
- "path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
- },
- "path-to-regexp": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
- "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
- "requires": {
- "isarray": "0.0.1"
- }
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
- },
- "performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
- "dev": true
- },
- "picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
- },
- "picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
- },
- "pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
- },
- "pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg=="
- },
- "pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
- "requires": {
- "pinkie": "^2.0.0"
- }
- },
- "pirates": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
- "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ=="
- },
- "pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "requires": {
- "find-up": "^4.0.0"
- },
- "dependencies": {
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "requires": {
- "p-limit": "^2.2.0"
- }
- }
- }
- },
- "pkg-up": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
- "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
- "requires": {
- "find-up": "^3.0.0"
- },
- "dependencies": {
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
- }
- }
- },
- "popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "peer": true
- },
- "posix-character-classes": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="
- },
- "postcss": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.1.0.tgz",
- "integrity": "sha512-d3RppIo1DI66oHxA1vdckr5qciQbMIrHvyzuvp2cLJHOLwJHg7X9ncrfw2Ri6Sgiwv/GoXtOwEHJ9E9VSRxXWQ==",
- "requires": {
- "colorette": "^1.2.1",
- "line-column": "^1.0.2",
- "nanoid": "^3.1.12",
- "source-map": "^0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "postcss-loader": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
- "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==",
- "dev": true,
- "requires": {
- "cosmiconfig": "^7.0.0",
- "klona": "^2.0.5",
- "semver": "^7.3.5"
- },
- "dependencies": {
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "postcss-modules-extract-imports": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
- "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
- "requires": {}
- },
- "postcss-modules-local-by-default": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
- "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
- "requires": {
- "icss-utils": "^5.0.0",
- "postcss-selector-parser": "^6.0.2",
- "postcss-value-parser": "^4.1.0"
- }
- },
- "postcss-modules-scope": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
- "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
- "requires": {
- "postcss-selector-parser": "^6.0.4"
- }
- },
- "postcss-modules-values": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
- "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
- "requires": {
- "icss-utils": "^5.0.0"
- }
- },
- "postcss-rtlcss": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-3.7.2.tgz",
- "integrity": "sha512-GurrGedCKvOTe1QrifI+XpDKXA3bJky1v8KiOa/TYYHs1bfJOxI53GIRvVSqLJLly7e1WcNMz8KMESTN01vbZQ==",
- "dev": true,
- "requires": {
- "rtlcss": "^3.5.0"
- }
- },
- "postcss-selector-parser": {
- "version": "6.0.13",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
- "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
- "requires": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- }
- },
- "postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
- },
- "prebuild-install": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
- "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
- "requires": {
- "detect-libc": "^2.0.0",
- "expand-template": "^2.0.3",
- "github-from-package": "0.0.0",
- "minimist": "^1.2.3",
- "mkdirp-classic": "^0.5.3",
- "napi-build-utils": "^1.0.1",
- "node-abi": "^3.3.0",
- "pump": "^3.0.0",
- "rc": "^1.2.7",
- "simple-get": "^4.0.0",
- "tar-fs": "^2.0.0",
- "tunnel-agent": "^0.6.0"
- }
- },
- "prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
- },
- "prepend-http": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
- "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==",
- "peer": true
- },
- "pretty-error": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz",
- "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==",
- "peer": true,
- "requires": {
- "lodash": "^4.17.20",
- "renderkid": "^2.0.4"
- }
- },
- "pretty-format": {
- "version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
- "requires": {
- "@jest/types": "^26.6.2",
- "ansi-regex": "^5.0.0",
- "ansi-styles": "^4.0.0",
- "react-is": "^17.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
- }
- }
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
- },
- "prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "requires": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- }
- },
- "prop-types": {
- "version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.8.1"
- }
- },
- "prop-types-exact": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz",
- "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==",
- "dev": true,
- "requires": {
- "has": "^1.0.3",
- "object.assign": "^4.1.0",
- "reflect.ownkeys": "^0.2.0"
- }
- },
- "prop-types-extra": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
- "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
- "requires": {
- "react-is": "^16.3.2",
- "warning": "^4.0.0"
- }
- },
- "property-information": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
- "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
- "requires": {
- "xtend": "^4.0.0"
- }
- },
- "proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "requires": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- },
- "dependencies": {
- "ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
- }
- }
- },
- "psl": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
- "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
- },
- "pubsub-js": {
- "version": "1.9.4",
- "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz",
- "integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A=="
- },
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
- },
- "purgecss": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-5.0.0.tgz",
- "integrity": "sha512-RAnuxrGuVyLLTr8uMbKaxDRGWMgK5CCYDfRyUNNcaz5P3kGgD2b7ymQGYEyo2ST7Tl/ScwFgf5l3slKMxHSbrw==",
- "requires": {
- "commander": "^9.0.0",
- "glob": "^8.0.3",
- "postcss": "^8.4.4",
- "postcss-selector-parser": "^6.0.7"
- },
- "dependencies": {
- "brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "requires": {
- "balanced-match": "^1.0.0"
- }
- },
- "commander": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
- "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
- },
- "glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- }
- },
- "minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "requires": {
- "brace-expansion": "^2.0.1"
- }
- },
- "postcss": {
- "version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
- "requires": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- }
- }
- },
- "qs": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
- "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
- "requires": {
- "side-channel": "^1.0.4"
- }
- },
- "query-string": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
- "integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
- "requires": {
- "decode-uri-component": "^0.2.0",
- "filter-obj": "^1.1.0",
- "split-on-first": "^1.0.0",
- "strict-uri-encode": "^2.0.0"
- },
- "dependencies": {
- "strict-uri-encode": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
- "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
- }
- }
- },
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="
- },
- "querystringify": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
- },
- "queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
- },
- "raf": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
- "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
- "dev": true,
- "requires": {
- "performance-now": "^2.1.0"
- }
- },
- "railroad-diagrams": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
- "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==",
- "dev": true
- },
- "ramda": {
- "version": "0.26.1",
- "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
- "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ=="
- },
- "randexp": {
- "version": "0.4.6",
- "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
- "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
- "dev": true,
- "requires": {
- "discontinuous-range": "1.0.0",
- "ret": "~0.1.10"
- }
- },
- "randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "requires": {
- "safe-buffer": "^5.1.0"
- }
- },
- "range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
- },
- "raw-body": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
- "requires": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- },
- "dependencies": {
- "bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
- }
- }
- },
- "rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
- }
- }
- },
- "react": {
- "version": "16.14.0",
- "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
- "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2"
- }
- },
- "react-bootstrap": {
- "version": "1.6.6",
- "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.6.tgz",
- "integrity": "sha512-pSzYyJT5u4rc8+5myM8Vid2JG52L8AmYSkpznReH/GM4+FhLqEnxUa0+6HRTaGwjdEixQNGchwY+b3xCdYWrDA==",
- "requires": {
- "@babel/runtime": "^7.14.0",
- "@restart/context": "^2.1.4",
- "@restart/hooks": "^0.4.7",
- "@types/invariant": "^2.2.33",
- "@types/prop-types": "^15.7.3",
- "@types/react": ">=16.14.8",
- "@types/react-transition-group": "^4.4.1",
- "@types/warning": "^3.0.0",
- "classnames": "^2.3.1",
- "dom-helpers": "^5.2.1",
- "invariant": "^2.2.4",
- "prop-types": "^15.7.2",
- "prop-types-extra": "^1.1.0",
- "react-overlays": "^5.1.2",
- "react-transition-group": "^4.4.1",
- "uncontrollable": "^7.2.1",
- "warning": "^4.0.3"
- },
- "dependencies": {
- "classnames": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
- "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
- }
- }
- },
- "react-clientside-effect": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
- "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
- "requires": {
- "@babel/runtime": "^7.12.13"
- }
- },
- "react-colorful": {
- "version": "5.6.1",
- "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
- "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
- "requires": {}
- },
- "react-dev-utils": {
- "version": "11.0.4",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
- "integrity": "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "7.10.4",
- "address": "1.1.2",
- "browserslist": "4.14.2",
- "chalk": "2.4.2",
- "cross-spawn": "7.0.3",
- "detect-port-alt": "1.1.6",
- "escape-string-regexp": "2.0.0",
- "filesize": "6.1.0",
- "find-up": "4.1.0",
- "fork-ts-checker-webpack-plugin": "4.1.6",
- "global-modules": "2.0.0",
- "globby": "11.0.1",
- "gzip-size": "5.1.1",
- "immer": "8.0.1",
- "is-root": "2.1.0",
- "loader-utils": "2.0.0",
- "open": "^7.0.2",
- "pkg-up": "3.1.0",
- "prompts": "2.4.0",
- "react-error-overlay": "^6.0.9",
- "recursive-readdir": "2.2.2",
- "shell-quote": "1.7.2",
- "strip-ansi": "6.0.0",
- "text-table": "0.2.0"
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
- "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
- "dev": true,
- "requires": {
- "@babel/highlight": "^7.10.4"
- }
- },
- "array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true
- },
- "browserslist": {
- "version": "4.14.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
- "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
- "dev": true,
- "requires": {
- "caniuse-lite": "^1.0.30001125",
- "electron-to-chromium": "^1.3.564",
- "escalade": "^3.0.2",
- "node-releases": "^1.1.61"
- }
- },
- "escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "dev": true
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "globby": {
- "version": "11.0.1",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
- "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
- "dev": true,
- "requires": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.1.1",
- "ignore": "^5.1.4",
- "merge2": "^1.3.0",
- "slash": "^3.0.0"
- }
- },
- "loader-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
- "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^2.1.2"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "node-releases": {
- "version": "1.1.77",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz",
- "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==",
- "dev": true
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "prompts": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
- "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==",
- "dev": true,
- "requires": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- }
- },
- "slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true
- },
- "strip-ansi": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.0"
- }
- }
- }
- },
- "react-dom": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
- "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.19.1"
- },
- "dependencies": {
- "scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
- }
- }
- }
- },
- "react-dropzone": {
- "version": "14.2.3",
- "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz",
- "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==",
- "requires": {
- "attr-accept": "^2.2.2",
- "file-selector": "^0.6.0",
- "prop-types": "^15.8.1"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- }
- }
- },
- "react-error-boundary": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
- "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.12.5"
- }
- },
- "react-error-overlay": {
- "version": "6.0.11",
- "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
- "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
- },
- "react-fast-compare": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
- "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
- },
- "react-focus-lock": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.2.tgz",
- "integrity": "sha512-5JfrsOKyA5Zn3h958mk7bAcfphr24jPoMoznJ8vaJF6fUrPQ8zrtEd3ILLOK8P5jvGxdMd96OxWNjDzATfR2qw==",
- "requires": {
- "@babel/runtime": "^7.0.0",
- "focus-lock": "^0.11.2",
- "prop-types": "^15.6.2",
- "react-clientside-effect": "^1.2.6",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- }
- },
- "react-focus-on": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/react-focus-on/-/react-focus-on-3.7.0.tgz",
- "integrity": "sha512-TsCnbJr4qjqFatJ4U1N8qGSZH+FUzxJ5mJ5ta7TY2YnDmUbGGmcvZMTZgGjQ1fl6vlztsMyg6YyZlPAeeIhEUg==",
- "requires": {
- "aria-hidden": "^1.2.2",
- "react-focus-lock": "^2.9.2",
- "react-remove-scroll": "^2.5.5",
- "react-style-singleton": "^2.2.0",
- "tslib": "^2.3.1",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- }
- },
- "react-helmet": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.1.tgz",
- "integrity": "sha512-CnwD822LU8NDBnjCpZ4ySh8L6HYyngViTZLfBBb3NjtrpN8m49clH8hidHouq20I51Y6TpCTISCBbqiY5GamwA==",
- "requires": {
- "object-assign": "^4.1.1",
- "prop-types": "^15.5.4",
- "react-fast-compare": "^2.0.2",
- "react-side-effect": "^1.1.0"
- }
- },
- "react-instantsearch-core": {
- "version": "6.38.1",
- "resolved": "https://registry.npmjs.org/react-instantsearch-core/-/react-instantsearch-core-6.38.1.tgz",
- "integrity": "sha512-14gy/jsakJELVeMEO+QmsHcugIyaU1pRyyuQjuXuBvF+TMHiWUjfYw7de3Lc4oYcTYIeSllYIxLHxdUoxLWZaA==",
- "requires": {
- "@babel/runtime": "^7.1.2",
- "algoliasearch-helper": "^3.11.1",
- "prop-types": "^15.6.2",
- "react-fast-compare": "^3.0.0"
- },
- "dependencies": {
- "react-fast-compare": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
- "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
- }
- }
- },
- "react-instantsearch-dom": {
- "version": "6.8.3",
- "resolved": "https://registry.npmjs.org/react-instantsearch-dom/-/react-instantsearch-dom-6.8.3.tgz",
- "integrity": "sha512-+Z0zgARvnfDbMnPIZX8CfwKE+TCX7E751Ty4RDnjnmpgnrfYzoodMr4/By/3vZ2KRk308gpyx9yyXZ18/3Ay/g==",
- "requires": {
- "@babel/runtime": "^7.1.2",
- "algoliasearch-helper": "^3.1.0",
- "classnames": "^2.2.5",
- "prop-types": "^15.6.2",
- "react-fast-compare": "^3.0.0",
- "react-instantsearch-core": "^6.8.3"
- },
- "dependencies": {
- "react-fast-compare": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
- "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
- }
- }
- },
- "react-intl": {
- "version": "5.25.1",
- "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz",
- "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==",
- "requires": {
- "@formatjs/ecma402-abstract": "1.11.4",
- "@formatjs/icu-messageformat-parser": "2.1.0",
- "@formatjs/intl": "2.2.1",
- "@formatjs/intl-displaynames": "5.4.3",
- "@formatjs/intl-listformat": "6.5.3",
- "@types/hoist-non-react-statics": "^3.3.1",
- "@types/react": "16 || 17 || 18",
- "hoist-non-react-statics": "^3.3.2",
- "intl-messageformat": "9.13.0",
- "tslib": "^2.1.0"
- }
- },
- "react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
- },
- "react-lifecycles-compat": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
- "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
- },
- "react-loading-skeleton": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.1.0.tgz",
- "integrity": "sha512-j1U1CWWs68nBPOg7tkQqnlFcAMFF6oEK6MgqAo15f8A5p7mjH6xyKn2gHbkcimpwfO0VQXqxAswnSYVr8lWzjw==",
- "requires": {}
- },
- "react-markdown": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.0.tgz",
- "integrity": "sha512-MC+zljUJeoLb4RbDm/wRbfoQFEZGz4TDOt/wb4dEehdaJWxLMn/T2IgwhQy0VYhuPEd2fhd7iOayE8lmENU0FA==",
- "requires": {
- "@types/hast": "^2.0.0",
- "@types/unist": "^2.0.3",
- "comma-separated-tokens": "^1.0.0",
- "prop-types": "^15.7.2",
- "property-information": "^5.0.0",
- "react-is": "^17.0.0",
- "remark-parse": "^9.0.0",
- "remark-rehype": "^8.0.0",
- "space-separated-tokens": "^1.1.0",
- "style-to-object": "^0.3.0",
- "unified": "^9.0.0",
- "unist-util-visit": "^2.0.0"
- },
- "dependencies": {
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
- }
- }
- },
- "react-overlays": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
- "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
- "requires": {
- "@babel/runtime": "^7.13.8",
- "@popperjs/core": "^2.11.6",
- "@restart/hooks": "^0.4.7",
- "@types/warning": "^3.0.0",
- "dom-helpers": "^5.2.0",
- "prop-types": "^15.7.2",
- "uncontrollable": "^7.2.1",
- "warning": "^4.0.3"
- }
- },
- "react-popper": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
- "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
- "requires": {
- "react-fast-compare": "^3.0.1",
- "warning": "^4.0.2"
- },
- "dependencies": {
- "react-fast-compare": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
- "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
- }
- }
- },
- "react-property": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz",
- "integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw=="
- },
- "react-proptype-conditional-require": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz",
- "integrity": "sha512-nopsRn7KnGgazBe2c3H2+Kf+Csp6PGDRLiBkYEDMKY8o/EIgft/WnIm/OnAKTawZiLnJXHAqhpFBddvs6NiXlw=="
- },
- "react-redux": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.1.tgz",
- "integrity": "sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg==",
- "requires": {
- "@babel/runtime": "^7.5.5",
- "hoist-non-react-statics": "^3.3.0",
- "invariant": "^2.2.4",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.7.2",
- "react-is": "^16.9.0"
- }
- },
- "react-refresh": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
- "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ=="
- },
- "react-remove-scroll": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
- "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
- "requires": {
- "react-remove-scroll-bar": "^2.3.3",
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.1.0",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- }
- },
- "react-remove-scroll-bar": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz",
- "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==",
- "requires": {
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.0.0"
- }
- },
- "react-responsive": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
- "integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
- "requires": {
- "hyphenate-style-name": "^1.0.0",
- "matchmediaquery": "^0.3.0",
- "prop-types": "^15.6.1",
- "shallow-equal": "^1.1.0"
- }
- },
- "react-router": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
- "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
- "requires": {
- "@babel/runtime": "^7.1.2",
- "history": "^4.9.0",
- "hoist-non-react-statics": "^3.1.0",
- "loose-envify": "^1.3.1",
- "mini-create-react-context": "^0.4.0",
- "path-to-regexp": "^1.7.0",
- "prop-types": "^15.6.2",
- "react-is": "^16.6.0",
- "tiny-invariant": "^1.0.2",
- "tiny-warning": "^1.0.0"
- }
- },
- "react-router-dom": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
- "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
- "requires": {
- "@babel/runtime": "^7.1.2",
- "history": "^4.9.0",
- "loose-envify": "^1.3.1",
- "prop-types": "^15.6.2",
- "react-router": "5.2.0",
- "tiny-invariant": "^1.0.2",
- "tiny-warning": "^1.0.0"
- }
- },
- "react-side-effect": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.2.0.tgz",
- "integrity": "sha512-v1ht1aHg5k/thv56DRcjw+WtojuuDHFUgGfc+bFHOWsF4ZK6C2V57DO0Or0GPsg6+LSTE0M6Ry/gfzhzSwbc5w==",
- "requires": {
- "shallowequal": "^1.0.1"
- }
- },
- "react-style-singleton": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
- "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
- "requires": {
- "get-nonce": "^1.0.0",
- "invariant": "^2.2.4",
- "tslib": "^2.0.0"
- }
- },
- "react-table": {
- "version": "7.8.0",
- "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
- "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==",
- "requires": {}
- },
- "react-test-renderer": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz",
- "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==",
- "dev": true,
- "requires": {
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "react-is": "^16.8.6",
- "scheduler": "^0.19.1"
- },
- "dependencies": {
- "scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
- }
- }
- }
- },
- "react-textarea-autosize": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz",
- "integrity": "sha512-uH3ORCsCa3C6LHxExExhF4jHoXYCQwE5oECmrRsunlspaDAbS4mGKNlWZqjLfInWtFQcf0o1n1jC/NGXFdUBCg==",
- "requires": {
- "@babel/runtime": "^7.1.2",
- "prop-types": "^15.6.0"
- }
- },
- "react-transition-group": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
- "requires": {
- "@babel/runtime": "^7.5.5",
- "dom-helpers": "^5.0.1",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.6.2"
- }
- },
- "react-truncate": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/react-truncate/-/react-truncate-2.4.0.tgz",
- "integrity": "sha512-3QW11/COYwi6iPUaunUhl06DW5NJBJD1WkmxW5YxqqUu6kvP+msB3jfoLg8WRbu57JqgebjVW8Lknw6T5/QZdA==",
- "requires": {}
- },
- "read-pkg": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
- "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
- "requires": {
- "@types/normalize-package-data": "^2.4.0",
- "normalize-package-data": "^2.5.0",
- "parse-json": "^5.0.0",
- "type-fest": "^0.6.0"
- },
- "dependencies": {
- "type-fest": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
- "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="
- }
- }
- },
- "read-pkg-up": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
- "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
- "requires": {
- "find-up": "^4.1.0",
- "read-pkg": "^5.2.0",
- "type-fest": "^0.8.1"
- },
- "dependencies": {
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
- }
- }
- },
- "readable-stream": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- }
- }
- },
- "readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "requires": {
- "picomatch": "^2.2.1"
- }
- },
- "rechoir": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
- "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
- "requires": {
- "resolve": "^1.20.0"
- }
- },
- "recursive-readdir": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
- "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
- "requires": {
- "minimatch": "3.0.4"
- },
- "dependencies": {
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- }
- }
- },
- "redent": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
- "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
- "dev": true,
- "requires": {
- "indent-string": "^4.0.0",
- "strip-indent": "^3.0.0"
- },
- "dependencies": {
- "strip-indent": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
- "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
- "dev": true,
- "requires": {
- "min-indent": "^1.0.0"
- }
- }
- }
- },
- "reduce-reducers": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/reduce-reducers/-/reduce-reducers-0.4.3.tgz",
- "integrity": "sha512-+CNMnI8QhgVMtAt54uQs3kUxC3Sybpa7Y63HR14uGLgI9/QR5ggHvpxwhGGe3wmx5V91YwqQIblN9k5lspAmGw=="
- },
- "redux": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz",
- "integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==",
- "requires": {
- "loose-envify": "^1.4.0",
- "symbol-observable": "^1.2.0"
- }
- },
- "redux-actions": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/redux-actions/-/redux-actions-2.6.5.tgz",
- "integrity": "sha512-pFhEcWFTYNk7DhQgxMGnbsB1H2glqhQJRQrtPb96kD3hWiZRzXHwwmFPswg6V2MjraXRXWNmuP9P84tvdLAJmw==",
- "requires": {
- "invariant": "^2.2.4",
- "just-curry-it": "^3.1.0",
- "loose-envify": "^1.4.0",
- "reduce-reducers": "^0.4.3",
- "to-camel-case": "^1.0.0"
- }
- },
- "redux-devtools-extension": {
- "version": "2.13.8",
- "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz",
- "integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==",
- "requires": {}
- },
- "redux-form": {
- "version": "8.3.8",
- "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-8.3.8.tgz",
- "integrity": "sha512-PzXhA0d+awIc4PkuhbDa6dCEiraMrGMyyDlYEVNX6qEyW/G2SqZXrjav5zrpXb0CCeqQSc9iqwbMtYQXbJbOAQ==",
- "requires": {
- "@babel/runtime": "^7.9.2",
- "es6-error": "^4.1.1",
- "hoist-non-react-statics": "^3.3.2",
- "invariant": "^2.2.4",
- "is-promise": "^2.1.0",
- "lodash": "^4.17.15",
- "prop-types": "^15.6.1",
- "react-is": "^16.4.2"
- }
- },
- "redux-logger": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
- "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==",
- "requires": {
- "deep-diff": "^0.3.5"
- }
- },
- "redux-mock-store": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz",
- "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==",
- "requires": {
- "lodash.isplainobject": "^4.0.6"
- }
- },
- "redux-thunk": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
- "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
- },
- "reflect.ownkeys": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
- "integrity": "sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg==",
- "dev": true
- },
- "regenerate": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
- "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="
- },
- "regenerate-unicode-properties": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
- "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
- "requires": {
- "regenerate": "^1.4.2"
- }
- },
- "regenerator-runtime": {
- "version": "0.13.7",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
- "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
- },
- "regenerator-transform": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
- "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
- "requires": {
- "@babel/runtime": "^7.8.4"
- }
- },
- "regex-not": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
- "requires": {
- "extend-shallow": "^3.0.2",
- "safe-regex": "^1.1.0"
- }
- },
- "regex-parser": {
- "version": "2.2.11",
- "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
- "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q=="
- },
- "regexp.prototype.flags": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
- "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "functions-have-names": "^1.2.3"
- }
- },
- "regexpu-core": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
- "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
- "requires": {
- "@babel/regjsgen": "^0.8.0",
- "regenerate": "^1.4.2",
- "regenerate-unicode-properties": "^10.1.0",
- "regjsparser": "^0.9.1",
- "unicode-match-property-ecmascript": "^2.0.0",
- "unicode-match-property-value-ecmascript": "^2.1.0"
- }
- },
- "regjsparser": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
- "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
- "requires": {
- "jsesc": "~0.5.0"
- },
- "dependencies": {
- "jsesc": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
- "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA=="
- }
- }
- },
- "relateurl": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
- "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
- },
- "remark-parse": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz",
- "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==",
- "requires": {
- "mdast-util-from-markdown": "^0.8.0"
- }
- },
- "remark-rehype": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz",
- "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==",
- "requires": {
- "mdast-util-to-hast": "^10.2.0"
- }
- },
- "remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
- },
- "renderkid": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz",
- "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==",
- "peer": true,
- "requires": {
- "css-select": "^4.1.3",
- "dom-converter": "^0.2.0",
- "htmlparser2": "^6.1.0",
- "lodash": "^4.17.21",
- "strip-ansi": "^3.0.1"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
- "peer": true
- },
- "css-select": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "peer": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "^6.0.1",
- "domhandler": "^4.3.1",
- "domutils": "^2.8.0",
- "nth-check": "^2.0.1"
- }
- },
- "dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "peer": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- }
- },
- "domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "peer": true,
- "requires": {
- "domelementtype": "^2.2.0"
- }
- },
- "domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "peer": true,
- "requires": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- }
- },
- "entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "peer": true
- },
- "htmlparser2": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
- "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
- "peer": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.0.0",
- "domutils": "^2.5.2",
- "entities": "^2.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
- "peer": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- }
- }
- },
- "repeat-element": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
- "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="
- },
- "repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="
- },
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
- },
- "require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
- },
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
- },
- "requires-port": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
- },
- "resize-observer-polyfill": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
- "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
- "dev": true
- },
- "resolve": {
- "version": "1.22.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
- "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
- "requires": {
- "is-core-module": "^2.9.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- }
- },
- "resolve-cwd": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
- "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
- "requires": {
- "resolve-from": "^5.0.0"
- }
- },
- "resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
- },
- "resolve-pathname": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
- "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
- },
- "resolve-url": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
- },
- "resolve-url-loader": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz",
- "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==",
- "requires": {
- "adjust-sourcemap-loader": "^4.0.0",
- "convert-source-map": "^1.7.0",
- "loader-utils": "^2.0.0",
- "postcss": "^8.2.14",
- "source-map": "0.6.1"
- },
- "dependencies": {
- "postcss": {
- "version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
- "requires": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
- },
- "retry": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
- "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
- },
- "reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "rst-selector-parser": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
- "integrity": "sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==",
- "dev": true,
- "requires": {
- "lodash.flattendeep": "^4.4.0",
- "nearley": "^2.7.10"
- }
- },
- "rsvp": {
- "version": "4.8.5",
- "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
- "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA=="
- },
- "rtlcss": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz",
- "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==",
- "dev": true,
- "requires": {
- "find-up": "^5.0.0",
- "picocolors": "^1.0.0",
- "postcss": "^8.3.11",
- "strip-json-comments": "^3.1.1"
- },
- "dependencies": {
- "postcss": {
- "version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
- "dev": true,
- "requires": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- }
- }
- },
- "run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "requires": {
- "queue-microtask": "^1.2.2"
- }
- },
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
- },
- "safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
- "requires": {
- "ret": "~0.1.10"
- }
- },
- "safe-regex-test": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
- "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
- "requires": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.3",
- "is-regex": "^1.1.4"
- }
- },
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "sane": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz",
- "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==",
- "requires": {
- "@cnakazawa/watch": "^1.0.3",
- "anymatch": "^2.0.0",
- "capture-exit": "^2.0.0",
- "exec-sh": "^0.3.2",
- "execa": "^1.0.0",
- "fb-watchman": "^2.0.0",
- "micromatch": "^3.1.4",
- "minimist": "^1.1.1",
- "walker": "~1.0.5"
- },
- "dependencies": {
- "anymatch": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
- "requires": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
- }
- },
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- },
- "micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- }
- },
- "normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
- "requires": {
- "remove-trailing-separator": "^1.0.1"
- }
- },
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- }
- }
- }
- },
- "sanitize-html": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.8.1.tgz",
- "integrity": "sha512-qK5neD0SaMxGwVv5txOYv05huC3o6ZAA4h5+7nJJgWMNFUNRjcjLO6FpwAtKzfKCZ0jrG6xTk6eVFskbvOGblg==",
- "requires": {
- "deepmerge": "^4.2.2",
- "escape-string-regexp": "^4.0.0",
- "htmlparser2": "^8.0.0",
- "is-plain-object": "^5.0.0",
- "parse-srcset": "^1.0.2",
- "postcss": "^8.3.11"
- },
- "dependencies": {
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
- },
- "is-plain-object": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
- "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
- },
- "postcss": {
- "version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
- "requires": {
- "nanoid": "^3.3.4",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- }
- }
- },
- "sass": {
- "version": "1.62.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.0.tgz",
- "integrity": "sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==",
- "dev": true,
- "requires": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
- "source-map-js": ">=0.6.2 <2.0.0"
- }
- },
- "sass-loader": {
- "version": "12.6.0",
- "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
- "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==",
- "dev": true,
- "requires": {
- "klona": "^2.0.4",
- "neo-async": "^2.6.2"
- }
- },
- "saxes": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
- "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
- "requires": {
- "xmlchars": "^2.2.0"
- }
- },
- "scheduler": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
- "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
- "requires": {
- "loose-envify": "^1.1.0"
- }
- },
- "schema-utils": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
- "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
- "requires": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- }
- },
- "select-hose": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
- "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg=="
- },
- "selfsigned": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz",
- "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==",
- "requires": {
- "node-forge": "^1"
- }
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
- },
- "send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
- "requires": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- },
- "dependencies": {
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- }
- }
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- }
- }
- },
- "serialize-javascript": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
- "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
- "requires": {
- "randombytes": "^2.1.0"
- }
- },
- "serve-index": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
- "requires": {
- "accepts": "~1.3.4",
- "batch": "0.6.1",
- "debug": "2.6.9",
- "escape-html": "~1.0.3",
- "http-errors": "~1.6.2",
- "mime-types": "~2.1.17",
- "parseurl": "~1.3.2"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "depd": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
- },
- "http-errors": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
- "requires": {
- "depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.0",
- "statuses": ">= 1.4.0 < 2"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "setprototypeof": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
- },
- "statuses": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
- }
- }
- },
- "serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.18.0"
- }
- },
- "set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
- },
- "set-value": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.3",
- "split-string": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
- }
- }
- },
- "setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
- },
- "shallow-clone": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
- "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
- "requires": {
- "kind-of": "^6.0.2"
- }
- },
- "shallow-equal": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
- "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
- },
- "shallowequal": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
- "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
- },
- "sharp": {
- "version": "0.32.1",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.1.tgz",
- "integrity": "sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==",
- "requires": {
- "color": "^4.2.3",
- "detect-libc": "^2.0.1",
- "node-addon-api": "^6.1.0",
- "prebuild-install": "^7.1.1",
- "semver": "^7.5.0",
- "simple-get": "^4.0.1",
- "tar-fs": "^2.1.1",
- "tunnel-agent": "^0.6.0"
- },
- "dependencies": {
- "color": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
- "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
- "requires": {
- "color-convert": "^2.0.1",
- "color-string": "^1.9.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "requires": {
- "shebang-regex": "^3.0.0"
- }
- },
- "shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
- },
- "shell-quote": {
- "version": "1.7.2",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
- "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
- "dev": true
- },
- "shellwords": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
- "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
- "optional": true
- },
- "side-channel": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
- "requires": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
- }
- },
- "signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
- },
- "simple-concat": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
- },
- "simple-get": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
- "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
- "requires": {
- "decompress-response": "^6.0.0",
- "once": "^1.3.1",
- "simple-concat": "^1.0.0"
- }
- },
- "simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
- "requires": {
- "is-arrayish": "^0.3.1"
- },
- "dependencies": {
- "is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
- }
- }
- },
- "sirv": {
- "version": "1.0.19",
- "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz",
- "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==",
- "requires": {
- "@polka/url": "^1.0.0-next.20",
- "mrmime": "^1.0.0",
- "totalist": "^1.0.0"
- }
- },
- "sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
- },
- "slash": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
- "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
- },
- "snake-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
- "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
- "peer": true,
- "requires": {
- "dot-case": "^3.0.4",
- "tslib": "^2.0.3"
- }
- },
- "snapdragon": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
- "requires": {
- "base": "^0.11.1",
- "debug": "^2.2.0",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "map-cache": "^0.2.2",
- "source-map": "^0.5.6",
- "source-map-resolve": "^0.5.0",
- "use": "^3.1.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "source-map-resolve": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
- "requires": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0",
- "resolve-url": "^0.2.1",
- "source-map-url": "^0.4.0",
- "urix": "^0.1.0"
- }
- }
- }
- },
- "snapdragon-node": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
- "requires": {
- "define-property": "^1.0.0",
- "isobject": "^3.0.0",
- "snapdragon-util": "^3.0.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "requires": {
- "is-descriptor": "^1.0.0"
- }
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "snapdragon-util": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
- "requires": {
- "kind-of": "^3.2.0"
- },
- "dependencies": {
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "sockjs": {
- "version": "0.3.24",
- "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
- "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
- "requires": {
- "faye-websocket": "^0.11.3",
- "uuid": "^8.3.2",
- "websocket-driver": "^0.7.4"
- },
- "dependencies": {
- "uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
- }
- }
- },
- "sort-keys": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
- "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==",
- "peer": true,
- "requires": {
- "is-plain-obj": "^1.0.0"
- }
- },
- "source-list-map": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
- "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
- },
- "source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
- },
- "source-map-loader": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz",
- "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==",
- "requires": {
- "abab": "^2.0.6",
- "iconv-lite": "^0.6.3",
- "source-map-js": "^1.0.2"
- },
- "dependencies": {
- "iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- }
- }
- }
- },
- "source-map-resolve": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
- "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
- "dev": true,
- "requires": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0"
- }
- },
- "source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "source-map-url": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
- "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
- },
- "space-separated-tokens": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
- "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="
- },
- "spdx-correct": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
- "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
- "requires": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "spdx-exceptions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
- "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
- },
- "spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "requires": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "spdx-license-ids": {
- "version": "3.0.12",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
- "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA=="
- },
- "spdy": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
- "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
- "requires": {
- "debug": "^4.1.0",
- "handle-thing": "^2.0.0",
- "http-deceiver": "^1.2.7",
- "select-hose": "^2.0.0",
- "spdy-transport": "^3.0.0"
- }
- },
- "spdy-transport": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
- "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
- "requires": {
- "debug": "^4.1.0",
- "detect-node": "^2.0.4",
- "hpack.js": "^2.1.6",
- "obuf": "^1.1.2",
- "readable-stream": "^3.0.6",
- "wbuf": "^1.7.3"
- },
- "dependencies": {
- "readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
- }
- },
- "split-on-first": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
- "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
- },
- "split-string": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
- "requires": {
- "extend-shallow": "^3.0.0"
- }
- },
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
- },
- "stable": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
- "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
- "dev": true
- },
- "stack-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
- "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
- "requires": {
- "escape-string-regexp": "^2.0.0"
- },
- "dependencies": {
- "escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="
- }
- }
- },
- "stackframe": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
- "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
- },
- "static-extend": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
- "requires": {
- "define-property": "^0.2.5",
- "object-copy": "^0.1.0"
- },
- "dependencies": {
- "define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "requires": {
- "is-descriptor": "^0.1.0"
- }
- },
- "is-accessor-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-data-descriptor": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "is-descriptor": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
- "requires": {
- "is-accessor-descriptor": "^0.1.6",
- "is-data-descriptor": "^0.1.4",
- "kind-of": "^5.0.0"
- }
- },
- "kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
- }
- }
- },
- "statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
- },
- "stop-iteration-iterator": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
- "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
- "requires": {
- "internal-slot": "^1.0.4"
- }
- },
- "strict-uri-encode": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
- "peer": true
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- },
- "dependencies": {
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- }
- }
- },
- "string-length": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
- "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "requires": {
- "char-regex": "^1.0.2",
- "strip-ansi": "^6.0.0"
- }
- },
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "dependencies": {
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- }
- }
- },
- "string.prototype.matchall": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
- "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "get-intrinsic": "^1.1.3",
- "has-symbols": "^1.0.3",
- "internal-slot": "^1.0.3",
- "regexp.prototype.flags": "^1.4.3",
- "side-channel": "^1.0.4"
- }
- },
- "string.prototype.trim": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
- "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "string.prototype.trimend": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
- "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "string.prototype.trimstart": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
- "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
- "requires": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4"
- }
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
- "strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="
- },
- "strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q=="
- },
- "strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
- },
- "strip-indent": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
- "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==",
- "dev": true
- },
- "strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
- },
- "style-loader": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.2.tgz",
- "integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==",
- "dev": true,
- "requires": {}
- },
- "style-to-js": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.2.tgz",
- "integrity": "sha512-aMG8jJpEF0SCGbQFY8W8CT+EjQ9ubp35FOZG3prWkNjxW/a1bEeSod0tkWiP+6iiOCDIIrQykUDkPY5LbNF87g==",
- "requires": {
- "style-to-object": "0.4.0"
- },
- "dependencies": {
- "style-to-object": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.0.tgz",
- "integrity": "sha512-dAjq2m87tPn/TcYTeqMhXJRhu96WYWcxMFQxs3Y9jfYpq2jG+38u4tj0Lst6DOiYXmDuNxVJ2b1Z2uPC6wTEeg==",
- "requires": {
- "inline-style-parser": "0.1.1"
- }
- }
- }
- },
- "style-to-object": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
- "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==",
- "requires": {
- "inline-style-parser": "0.1.1"
- }
- },
- "superagent": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
- "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==",
- "requires": {
- "component-emitter": "^1.2.0",
- "cookiejar": "^2.1.0",
- "debug": "^3.1.0",
- "extend": "^3.0.0",
- "form-data": "^2.3.1",
- "formidable": "^1.2.0",
- "methods": "^1.1.1",
- "mime": "^1.4.1",
- "qs": "^6.5.1",
- "readable-stream": "^2.3.5"
- },
- "dependencies": {
- "debug": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "form-data": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
- "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
- "mime-types": "^2.1.12"
- }
- }
- }
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "requires": {
- "has-flag": "^3.0.0"
- }
- },
- "supports-hyperlinks": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
- "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
- "requires": {
- "has-flag": "^4.0.0",
- "supports-color": "^7.0.0"
- },
- "dependencies": {
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
- },
- "svg-parser": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
- "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="
- },
- "svgo": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
- "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
- "dev": true,
- "requires": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^4.1.3",
- "css-tree": "^1.1.3",
- "csso": "^4.2.0",
- "picocolors": "^1.0.0",
- "stable": "^0.1.8"
- },
- "dependencies": {
- "commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "dev": true
- },
- "css-select": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
- "dev": true,
- "requires": {
- "boolbase": "^1.0.0",
- "css-what": "^6.0.1",
- "domhandler": "^4.3.1",
- "domutils": "^2.8.0",
- "nth-check": "^2.0.1"
- }
- },
- "dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- }
- },
- "domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.2.0"
- }
- },
- "domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "dev": true,
- "requires": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- }
- },
- "entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "dev": true
- }
- }
- },
- "symbol-observable": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
- "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
- },
- "symbol-tree": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
- },
- "tabbable": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
- "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA=="
- },
- "tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA=="
- },
- "tar-fs": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
- "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
- "requires": {
- "chownr": "^1.1.1",
- "mkdirp-classic": "^0.5.2",
- "pump": "^3.0.0",
- "tar-stream": "^2.1.4"
- }
- },
- "tar-stream": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
- "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
- "requires": {
- "bl": "^4.0.3",
- "end-of-stream": "^1.4.1",
- "fs-constants": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^3.1.1"
- },
- "dependencies": {
- "readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- }
- }
- }
- },
- "terminal-link": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
- "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
- "requires": {
- "ansi-escapes": "^4.2.1",
- "supports-hyperlinks": "^2.0.0"
- }
- },
- "terser": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
- "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
- "peer": true,
- "requires": {
- "commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "peer": true
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "peer": true
- }
- }
- },
- "terser-webpack-plugin": {
- "version": "5.3.9",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
- "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.17",
- "jest-worker": "^27.4.5",
- "schema-utils": "^3.1.1",
- "serialize-javascript": "^6.0.1",
- "terser": "^5.16.8"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "jest-worker": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
- "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
- "requires": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- }
- },
- "supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "terser": {
- "version": "5.17.7",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz",
- "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==",
- "requires": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- }
- }
- }
- },
- "test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "requires": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- }
- },
- "text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
- },
- "throat": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
- "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="
- },
- "thunky": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
- "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
- },
- "timeago.js": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz",
- "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w=="
- },
- "tiny-invariant": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
- "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
- },
- "tiny-warning": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
- "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
- },
- "tmpl": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
- "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="
- },
- "to-camel-case": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz",
- "integrity": "sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==",
- "requires": {
- "to-space-case": "^1.0.0"
- }
- },
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
- },
- "to-no-case": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
- "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg=="
- },
- "to-object-path": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "to-regex": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
- "requires": {
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "regex-not": "^1.0.2",
- "safe-regex": "^1.1.0"
- }
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "to-space-case": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
- "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==",
- "requires": {
- "to-no-case": "^1.0.0"
- }
- },
- "toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
- },
- "totalist": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz",
- "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g=="
- },
- "tough-cookie": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
- "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
- "requires": {
- "psl": "^1.1.33",
- "punycode": "^2.1.1",
- "universalify": "^0.2.0",
- "url-parse": "^1.5.3"
- },
- "dependencies": {
- "universalify": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="
- }
- }
- },
- "tr46": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
- "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
- "requires": {
- "punycode": "^2.1.1"
- }
- },
- "trough": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz",
- "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA=="
- },
- "ts-jest": {
- "version": "26.5.6",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
- "integrity": "sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==",
- "devOptional": true,
- "requires": {
- "bs-logger": "0.x",
- "buffer-from": "1.x",
- "fast-json-stable-stringify": "2.x",
- "jest-util": "^26.1.0",
- "json5": "2.x",
- "lodash": "4.x",
- "make-error": "1.x",
- "mkdirp": "1.x",
- "semver": "7.x",
- "yargs-parser": "20.x"
- },
- "dependencies": {
- "mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "devOptional": true
- },
- "semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "devOptional": true,
- "requires": {
- "lru-cache": "^6.0.0"
- }
- }
- }
- },
- "tsconfig-paths": {
- "version": "3.14.2",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
- "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
- "requires": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.2",
- "minimist": "^1.2.6",
- "strip-bom": "^3.0.0"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="
- }
- }
- },
- "tslib": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
- "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
- },
- "tsutils": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
- "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
- "dev": true,
- "requires": {
- "tslib": "^1.8.1"
- },
- "dependencies": {
- "tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
- }
- }
- },
- "tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
- "requires": {
- "safe-buffer": "^5.0.1"
- }
- },
- "type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "requires": {
- "prelude-ls": "^1.2.1"
- }
- },
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
- },
- "type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
- },
- "type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "requires": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- }
- },
- "typedarray-to-buffer": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
- "requires": {
- "is-typedarray": "^1.0.0"
- }
- },
- "typescript": {
- "version": "4.9.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
- "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="
- },
- "unbox-primitive": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
- "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
- "requires": {
- "call-bind": "^1.0.2",
- "has-bigints": "^1.0.2",
- "has-symbols": "^1.0.3",
- "which-boxed-primitive": "^1.0.2"
- }
- },
- "uncontrollable": {
- "version": "7.2.1",
- "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
- "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
- "requires": {
- "@babel/runtime": "^7.6.3",
- "@types/react": ">=16.9.11",
- "invariant": "^2.2.4",
- "react-lifecycles-compat": "^3.0.4"
- }
- },
- "unicode-canonical-property-names-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
- "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ=="
- },
- "unicode-match-property-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
- "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
- "requires": {
- "unicode-canonical-property-names-ecmascript": "^2.0.0",
- "unicode-property-aliases-ecmascript": "^2.0.0"
- }
- },
- "unicode-match-property-value-ecmascript": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
- "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA=="
- },
- "unicode-property-aliases-ecmascript": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
- "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w=="
- },
- "unified": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz",
- "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==",
- "requires": {
- "bail": "^1.0.0",
- "extend": "^3.0.0",
- "is-buffer": "^2.0.0",
- "is-plain-obj": "^2.0.0",
- "trough": "^1.0.0",
- "vfile": "^4.0.0"
- },
- "dependencies": {
- "is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="
- }
- }
- },
- "union-value": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
- "requires": {
- "arr-union": "^3.1.0",
- "get-value": "^2.0.6",
- "is-extendable": "^0.1.1",
- "set-value": "^2.0.1"
- },
- "dependencies": {
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
- }
- }
- },
- "unist-builder": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz",
- "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw=="
- },
- "unist-util-generated": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz",
- "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg=="
- },
- "unist-util-is": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz",
- "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg=="
- },
- "unist-util-position": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz",
- "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA=="
- },
- "unist-util-stringify-position": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
- "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
- "requires": {
- "@types/unist": "^2.0.2"
- }
- },
- "unist-util-visit": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz",
- "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==",
- "requires": {
- "@types/unist": "^2.0.0",
- "unist-util-is": "^4.0.0",
- "unist-util-visit-parents": "^3.0.0"
- }
- },
- "unist-util-visit-parents": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
- "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==",
- "requires": {
- "@types/unist": "^2.0.0",
- "unist-util-is": "^4.0.0"
- }
- },
- "universal-cookie": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
- "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
- "requires": {
- "@types/cookie": "^0.3.3",
- "cookie": "^0.4.0"
- }
- },
- "universalify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
- "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
- },
- "unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
- },
- "unset-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
- "requires": {
- "has-value": "^0.3.1",
- "isobject": "^3.0.0"
- },
- "dependencies": {
- "has-value": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
- "requires": {
- "get-value": "^2.0.3",
- "has-values": "^0.1.4",
- "isobject": "^2.0.0"
- },
- "dependencies": {
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
- "requires": {
- "isarray": "1.0.0"
- }
- }
- }
- },
- "has-values": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- },
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
- }
- }
- },
- "update-browserslist-db": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
- "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
- "requires": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- }
- },
- "uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "requires": {
- "punycode": "^2.1.0"
- }
- },
- "urix": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg=="
- },
- "url": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
- "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==",
- "requires": {
- "punycode": "1.3.2",
- "querystring": "0.2.0"
- },
- "dependencies": {
- "punycode": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
- }
- }
- },
- "url-loader": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
- "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==",
- "requires": {
- "loader-utils": "^2.0.0",
- "mime-types": "^2.1.27",
- "schema-utils": "^3.0.0"
- }
- },
- "url-parse": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "requires": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
- }
- },
- "use": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
- },
- "use-callback-ref": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
- "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
- "requires": {
- "tslib": "^2.0.0"
- }
- },
- "use-context-selector": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/use-context-selector/-/use-context-selector-1.4.1.tgz",
- "integrity": "sha512-Io2ArvcRO+6MWIhkdfMFt+WKQX+Vb++W8DS2l03z/Vw/rz3BclKpM0ynr4LYGyU85Eke+Yx5oIhTY++QR0ZDoA==",
- "requires": {}
- },
- "use-sidecar": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
- "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
- "requires": {
- "detect-node-es": "^1.1.0",
- "tslib": "^2.0.0"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
- },
- "util.promisify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
- "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
- "peer": true,
- "requires": {
- "define-properties": "^1.1.2",
- "object.getownpropertydescriptors": "^2.0.3"
- }
- },
- "utila": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
- "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="
- },
- "utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
- },
- "uuid": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
- "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
- },
- "v8-to-istanbul": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
- "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==",
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^1.6.0",
- "source-map": "^0.7.3"
- },
- "dependencies": {
- "source-map": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
- }
- }
- },
- "validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "requires": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "validator": {
- "version": "10.11.0",
- "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
- "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw=="
- },
- "value-equal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
- "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
- },
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
- },
- "vfile": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz",
- "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==",
- "requires": {
- "@types/unist": "^2.0.0",
- "is-buffer": "^2.0.0",
- "unist-util-stringify-position": "^2.0.0",
- "vfile-message": "^2.0.0"
- }
- },
- "vfile-message": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
- "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
- "requires": {
- "@types/unist": "^2.0.0",
- "unist-util-stringify-position": "^2.0.0"
- }
- },
- "viz.js": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-2.1.2.tgz",
- "integrity": "sha512-UO6CPAuEMJ8oNR0gLLNl+wUiIzQUsyUOp8SyyDKTqVRBtq7kk1VnFmIZW8QufjxGrGEuI+LVR7p/C7uEKy0LQw=="
- },
- "w3c-hr-time": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
- "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
- "requires": {
- "browser-process-hrtime": "^1.0.0"
- }
- },
- "w3c-xmlserializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
- "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
- "requires": {
- "xml-name-validator": "^3.0.0"
- }
- },
- "walker": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
- "requires": {
- "makeerror": "1.0.12"
- }
- },
- "warning": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
- "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "watchpack": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
- "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
- "requires": {
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.1.2"
- }
- },
- "wbuf": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
- "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
- "requires": {
- "minimalistic-assert": "^1.0.0"
- }
- },
- "webidl-conversions": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
- "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w=="
- },
- "webpack": {
- "version": "5.79.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.79.0.tgz",
- "integrity": "sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg==",
- "requires": {
- "@types/eslint-scope": "^3.7.3",
- "@types/estree": "^1.0.0",
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/wasm-edit": "1.11.1",
- "@webassemblyjs/wasm-parser": "1.11.1",
- "acorn": "^8.7.1",
- "acorn-import-assertions": "^1.7.6",
- "browserslist": "^4.14.5",
- "chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.10.0",
- "es-module-lexer": "^1.2.1",
- "eslint-scope": "5.1.1",
- "events": "^3.2.0",
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.2.9",
- "json-parse-even-better-errors": "^2.3.1",
- "loader-runner": "^4.2.0",
- "mime-types": "^2.1.27",
- "neo-async": "^2.6.2",
- "schema-utils": "^3.1.0",
- "tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.7",
- "watchpack": "^2.4.0",
- "webpack-sources": "^3.2.3"
- },
- "dependencies": {
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
- },
- "webpack-sources": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
- "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="
- }
- }
- },
- "webpack-bundle-analyzer": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz",
- "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==",
- "requires": {
- "@discoveryjs/json-ext": "0.5.7",
- "acorn": "^8.0.4",
- "acorn-walk": "^8.0.0",
- "chalk": "^4.1.0",
- "commander": "^7.2.0",
- "gzip-size": "^6.0.0",
- "lodash": "^4.17.20",
- "opener": "^1.5.2",
- "sirv": "^1.0.7",
- "ws": "^7.3.1"
- },
- "dependencies": {
- "acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
- },
- "gzip-size": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
- "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
- "requires": {
- "duplexer": "^0.1.2"
- }
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "webpack-cli": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz",
- "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==",
- "requires": {
- "@discoveryjs/json-ext": "^0.5.0",
- "@webpack-cli/configtest": "^2.0.1",
- "@webpack-cli/info": "^2.0.1",
- "@webpack-cli/serve": "^2.0.1",
- "colorette": "^2.0.14",
- "commander": "^9.4.1",
- "cross-spawn": "^7.0.3",
- "envinfo": "^7.7.3",
- "fastest-levenshtein": "^1.0.12",
- "import-local": "^3.0.2",
- "interpret": "^3.1.1",
- "rechoir": "^0.8.0",
- "webpack-merge": "^5.7.3"
- },
- "dependencies": {
- "colorette": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
- "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
- },
- "commander": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
- "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
- },
- "webpack-merge": {
- "version": "5.9.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
- "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==",
- "requires": {
- "clone-deep": "^4.0.1",
- "wildcard": "^2.0.0"
- }
- }
- }
- },
- "webpack-dev-middleware": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
- "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
- "requires": {
- "colorette": "^2.0.10",
- "memfs": "^3.4.3",
- "mime-types": "^2.1.31",
- "range-parser": "^1.2.1",
- "schema-utils": "^4.0.0"
- },
- "dependencies": {
- "ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- }
- },
- "ajv-keywords": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "requires": {
- "fast-deep-equal": "^3.1.3"
- }
- },
- "colorette": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
- "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
- },
- "json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
- },
- "schema-utils": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
- "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
- "requires": {
- "@types/json-schema": "^7.0.9",
- "ajv": "^8.8.0",
- "ajv-formats": "^2.1.1",
- "ajv-keywords": "^5.0.0"
- }
- }
- }
- },
- "webpack-dev-server": {
- "version": "4.13.2",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz",
- "integrity": "sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==",
- "devOptional": true,
- "requires": {
- "@types/bonjour": "^3.5.9",
- "@types/connect-history-api-fallback": "^1.3.5",
- "@types/express": "^4.17.13",
- "@types/serve-index": "^1.9.1",
- "@types/serve-static": "^1.13.10",
- "@types/sockjs": "^0.3.33",
- "@types/ws": "^8.5.1",
- "ansi-html-community": "^0.0.8",
- "bonjour-service": "^1.0.11",
- "chokidar": "^3.5.3",
- "colorette": "^2.0.10",
- "compression": "^1.7.4",
- "connect-history-api-fallback": "^2.0.0",
- "default-gateway": "^6.0.3",
- "express": "^4.17.3",
- "graceful-fs": "^4.2.6",
- "html-entities": "^2.3.2",
- "http-proxy-middleware": "^2.0.3",
- "ipaddr.js": "^2.0.1",
- "launch-editor": "^2.6.0",
- "open": "^8.0.9",
- "p-retry": "^4.5.0",
- "rimraf": "^3.0.2",
- "schema-utils": "^4.0.0",
- "selfsigned": "^2.1.1",
- "serve-index": "^1.9.1",
- "sockjs": "^0.3.24",
- "spdy": "^4.0.2",
- "webpack-dev-middleware": "^5.3.1",
- "ws": "^8.13.0"
- },
- "dependencies": {
- "ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
- "devOptional": true,
- "requires": {
- "fast-deep-equal": "^3.1.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
- }
- },
- "ajv-keywords": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
- "devOptional": true,
- "requires": {
- "fast-deep-equal": "^3.1.3"
- }
- },
- "colorette": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
- "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
- "devOptional": true
- },
- "json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "devOptional": true
- },
- "open": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
- "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
- "devOptional": true,
- "requires": {
- "define-lazy-prop": "^2.0.0",
- "is-docker": "^2.1.1",
- "is-wsl": "^2.2.0"
- }
- },
- "rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "devOptional": true,
- "requires": {
- "glob": "^7.1.3"
- }
- },
- "schema-utils": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
- "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
- "devOptional": true,
- "requires": {
- "@types/json-schema": "^7.0.9",
- "ajv": "^8.8.0",
- "ajv-formats": "^2.1.1",
- "ajv-keywords": "^5.0.0"
- }
- },
- "ws": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
- "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
- "devOptional": true,
- "requires": {}
- }
- }
- },
- "webpack-merge": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.4.0.tgz",
- "integrity": "sha512-/scBgu8LVPlHDgqH95Aw1xS+L+PHrpHKOwYVGFaNOQl4Q4wwwWDarwB1WdZAbLQ24SKhY3Awe7VZGYAdp+N+gQ==",
- "peer": true,
- "requires": {
- "clone-deep": "^4.0.1",
- "wildcard": "^2.0.0"
- }
- },
- "webpack-sources": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
- "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
- "requires": {
- "source-list-map": "^2.0.0",
- "source-map": "~0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
- "websocket-driver": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
- "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
- "requires": {
- "http-parser-js": ">=0.5.1",
- "safe-buffer": ">=5.1.0",
- "websocket-extensions": ">=0.1.1"
- }
- },
- "websocket-extensions": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
- "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
- },
- "whatwg-encoding": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
- "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
- "requires": {
- "iconv-lite": "0.4.24"
- }
- },
- "whatwg-mimetype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
- "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
- },
- "whatwg-url": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
- "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
- "requires": {
- "lodash": "^4.7.0",
- "tr46": "^2.1.0",
- "webidl-conversions": "^6.1.0"
- }
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "which-boxed-primitive": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
- "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
- "requires": {
- "is-bigint": "^1.0.1",
- "is-boolean-object": "^1.1.0",
- "is-number-object": "^1.0.4",
- "is-string": "^1.0.5",
- "is-symbol": "^1.0.3"
- }
- },
- "which-collection": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
- "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
- "requires": {
- "is-map": "^2.0.1",
- "is-set": "^2.0.1",
- "is-weakmap": "^2.0.1",
- "is-weakset": "^2.0.1"
- }
- },
- "which-module": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q=="
- },
- "which-typed-array": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
- "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
- "requires": {
- "available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.2",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-tostringtag": "^1.0.0",
- "is-typed-array": "^1.1.10"
- }
- },
- "wildcard": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
- "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
- },
- "word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
- },
- "worker-rpc": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
- "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
- "dev": true,
- "requires": {
- "microevent.ts": "~0.1.1"
- }
- },
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- }
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
- },
- "write-file-atomic": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
- "requires": {
- "imurmurhash": "^0.1.4",
- "is-typedarray": "^1.0.0",
- "signal-exit": "^3.0.2",
- "typedarray-to-buffer": "^3.1.5"
- }
- },
- "ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
- "requires": {}
- },
- "xml-name-validator": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
- "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
- },
- "xmlchars": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
- },
- "xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
- },
- "y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
- },
- "yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
- "yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
- },
- "yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "requires": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- }
- },
- "yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
- },
- "yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
}
}
}
diff --git a/package.json b/package.json
index 00a5ebdbb3..f7abed1292 100644
--- a/package.json
+++ b/package.json
@@ -26,11 +26,11 @@
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
- "@edx/frontend-enterprise-catalog-search": "3.1.5",
- "@edx/frontend-enterprise-hotjar": "1.2.0",
- "@edx/frontend-enterprise-logistration": "2.1.0",
- "@edx/frontend-enterprise-utils": "2.1.0",
- "@edx/frontend-platform": "2.4.0",
+ "@edx/frontend-enterprise-catalog-search": "4.2.0",
+ "@edx/frontend-enterprise-hotjar": "1.3.0",
+ "@edx/frontend-enterprise-logistration": "3.2.0",
+ "@edx/frontend-enterprise-utils": "3.2.0",
+ "@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
"@fortawesome/fontawesome-svg-core": "1.2.35",
"@fortawesome/free-brands-svg-icons": "5.15.3",
@@ -93,8 +93,8 @@
"@edx/browserslist-config": "1.0.0",
"@edx/frontend-build": "^12.9.0-alpha.1",
"@faker-js/faker": "^7.6.0",
- "@testing-library/dom": "7.31.2",
- "@testing-library/jest-dom": "5.11.9",
+ "@testing-library/dom": "9.3.1",
+ "@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "11.2.7",
"@testing-library/react-hooks": "5.0.3",
"@testing-library/user-event": "12.8.3",
@@ -105,10 +105,10 @@
"identity-obj-proxy": "3.0.0",
"jest-canvas-mock": "^2.4.0",
"jest-localstorage-mock": "^2.4.22",
- "postcss": "8.1.0",
+ "postcss": "8.4.24",
"react-dev-utils": "11.0.4",
"react-test-renderer": "16.13.1",
"resize-observer-polyfill": "1.5.1",
"ts-jest": "^26.5.0"
}
-}
\ No newline at end of file
+}
From 30545f99e8481f288f4d541ca0371aec55ba06b3 Mon Sep 17 00:00:00 2001
From: Emily Aquin
Date: Wed, 26 Jul 2023 22:36:43 +0000
Subject: [PATCH 009/124] chore: portal appearance tab appears by default
---
src/components/settings/SettingsTabs.jsx | 30 +++++++++----------
src/components/settings/data/constants.js | 2 +-
.../settings/tests/SettingsPage.test.jsx | 4 +--
3 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/components/settings/SettingsTabs.jsx b/src/components/settings/SettingsTabs.jsx
index 3b4f292ffd..9fe2bca15d 100644
--- a/src/components/settings/SettingsTabs.jsx
+++ b/src/components/settings/SettingsTabs.jsx
@@ -48,6 +48,21 @@ const SettingsTabs = ({
const tabArray = useMemo(() => {
const initialTabs = [];
+ if (SETTINGS_PAGE_APPEARANCE_TAB) {
+ initialTabs.push(
+
+
+ ,
+ );
+ }
if (enableLearnerPortal) {
initialTabs.push(
,
);
}
- if (SETTINGS_PAGE_APPEARANCE_TAB) {
- initialTabs.push(
-
-
- ,
- );
- }
return initialTabs;
}, [
FEATURE_SSO_SETTINGS_TAB,
diff --git a/src/components/settings/data/constants.js b/src/components/settings/data/constants.js
index e5b15ff434..02edae89e9 100644
--- a/src/components/settings/data/constants.js
+++ b/src/components/settings/data/constants.js
@@ -71,7 +71,7 @@ export const SETTINGS_TAB_LABELS = {
};
/** Default tab when no parameter is given */
-export const DEFAULT_TAB = ACCESS_TAB;
+export const DEFAULT_TAB = APPEARANCE_TAB;
/**
* Url parameter that the set in the router
diff --git a/src/components/settings/tests/SettingsPage.test.jsx b/src/components/settings/tests/SettingsPage.test.jsx
index 8e15c00437..a0fae5b066 100644
--- a/src/components/settings/tests/SettingsPage.test.jsx
+++ b/src/components/settings/tests/SettingsPage.test.jsx
@@ -36,10 +36,10 @@ describe(' ', () => {
jest.clearAllMocks();
});
- it('Redirects to access tab when no param given', () => {
+ it('Redirects to appearance tab when no param given', () => {
render(settingsPageWithRouter('/settings'));
expect(screen.queryByText('404')).toBeFalsy();
- expect(screen.queryByText('access')).toBeTruthy();
+ expect(screen.queryByText('appearance')).toBeTruthy();
});
it('Does not redirect when access is passed', () => {
From d24bd6197e0e87ce993b1d7420ef1b9b1a4e736f Mon Sep 17 00:00:00 2001
From: Omar Al-Ithawi
Date: Tue, 1 Aug 2023 17:56:11 +0300
Subject: [PATCH 010/124] chore: add i18n_extract to package.json for
openedx-translations (#1013)
This step is needed for openedx-translations repo to extract source files.
This contribution is part of the [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) which is sparked by the [Translation Infrastructure update OEP-58](https://open-edx-proposals.readthedocs.io/en/latest/architectural-decisions/oep-0058-arch-translations-management.html#specification).
---
package.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/package.json b/package.json
index f7abed1292..b38707ffd8 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
],
"scripts": {
"build": "fedx-scripts webpack",
+ "i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"build:with-theme": "THEME=npm:@edx/brand-edx.org@latest npm run install-theme && fedx-scripts webpack",
"check-types": "tsc --noemit",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .; npm run check-types",
From b39c87a9dd5272aca198f8e1d057ff6be0d27b11 Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Tue, 1 Aug 2023 15:06:12 -0600
Subject: [PATCH 011/124] fix: removing moment for dayjs (#1011)
* fix: removing moment for dayjs
* fix: package lock changes
* fix: adding ADR and test fixes
* fix: removing moment
---
docs/decisions/0005-moment-to-dayjs.rst | 46 +++++++++++++++++++
package-lock.json | 15 +++---
package.json | 2 +-
src/components/Admin/AdminSearchForm.jsx | 4 +-
src/components/Admin/EmbeddedSubscription.jsx | 4 +-
src/components/Admin/SubscriptionDetails.jsx | 4 +-
.../licenses/LicenseManagementTable/index.jsx | 11 +++--
.../table/CourseSearchResultsCells.jsx | 6 +--
.../CodeManagement/ManageRequestsTab.jsx | 7 +--
.../tests/ManageRequestsTab.test.jsx | 16 +++----
.../CodeSearchResultsTable.jsx | 4 +-
src/components/Coupon/Coupon.test.jsx | 12 ++---
.../EnterpriseSubsidiesContext/data/hooks.js | 12 +++--
.../LearnerCreditAllocationTable.jsx | 4 +-
.../LearnerCreditManagement.jsx | 4 +-
.../learner-credit-management/OfferDates.jsx | 11 ++---
.../tests/LearnerCreditManagement.test.jsx | 4 +-
.../tests/OfferDates.test.jsx | 12 ++---
.../SettingsAccessLinkManagement.test.jsx | 14 +++---
.../CustomThemeModal.jsx | 2 +-
.../tests/SettingsAppearanceTab.test.jsx | 24 +++++-----
.../subscriptions/SubscriptionCard.jsx | 17 +++----
.../subscriptions/SubscriptionDetails.jsx | 8 ++--
.../subscriptions/SubscriptionPlanRoutes.jsx | 4 +-
src/components/subscriptions/data/utils.js | 6 +--
.../LicenseManagementRemindModal.jsx | 6 +--
.../LicenseManagementRevokeModal.jsx | 5 +-
.../LicenseManagementRemindModal.test.jsx | 6 +--
.../LicenseManagementRevokeModal.test.jsx | 4 +-
.../bulk-actions/RemindBulkAction.test.jsx | 4 +-
.../bulk-actions/RevokeBulkAction.test.jsx | 9 ++--
.../licenses/LicenseManagementTable/index.jsx | 6 +--
...icenseManagementTableActionColumn.test.jsx | 4 +-
.../tests/index.test.jsx | 6 +--
.../tests/SubscriptionCard.test.jsx | 22 ++++-----
.../tests/SubscriptionManagementPage.test.jsx | 12 ++---
.../subscriptions/tests/TestUtilities.jsx | 10 ++--
.../subscriptions/tests/data/utils.test.js | 18 ++++----
.../NoAvailableCodesBanner.jsx | 8 ++--
.../tests/NoAvailableCodesBanner.test.jsx | 18 ++++----
.../subsidy-request-modals/data/hooks.js | 6 +--
.../tests/hooks.test.jsx | 8 ++--
src/utils.js | 4 +-
43 files changed, 222 insertions(+), 187 deletions(-)
create mode 100644 docs/decisions/0005-moment-to-dayjs.rst
diff --git a/docs/decisions/0005-moment-to-dayjs.rst b/docs/decisions/0005-moment-to-dayjs.rst
new file mode 100644
index 0000000000..edc8a2cdf3
--- /dev/null
+++ b/docs/decisions/0005-moment-to-dayjs.rst
@@ -0,0 +1,46 @@
+5. Replacing Moment.js library with Day.js
+============================================================
+
+Status
+******
+
+In progress
+
+Context
+*******
+
+Moment.js is a widely used time and date library, but the creators have decided to make it a legacy project that no
+longer fixes bugs or adds new features. Because of this, and because of the large size that the Moment.js package takes up
+(4.23 MB according to npm and the minified size is 6.3KB), we are choosing to replace all instances of Moment.js
+in our enterprise repositories.
+
+Decisions
+*********
+
+In its stead, we are choosing to replace this library with the Day.js project. This is one of the projects that were
+explicitly recommended from the Moment.js team as a recommended alternative. Out of the box, it supports basic usage,
+and additional plugins have been identified based on a look through our current usages of Moment.js in our codebase.
+The plugins that we will need to add are Duration, UTC, and Timezone, with more possibilities available for future use.
+
+Day.js also has almost identical functions to Moment, and was definitely created with this in mind. So most files will
+just need to replace the package name and nothing else
+
+``moment(date).format('MMMM D, YYYY') -> dayjs(date).format('MMMM D, YYYY')``
+
+Consequences
+************
+
+By choosing Day.js over another, larger library like date-fns, we sacrifice more of the out of the box functionality
+that comes from having a bigger library. Also, day.js does not support tree-shaking like the date-fns library does.
+However, by only installing needed plugins and starting from a much smaller package size, Day.js will considerably
+decrease the JS bundle size while still maintaining the core functionality.
+
+Alternatives Considered
+***********************
+
+date-fns
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Date-fns has a 162.1 kB bundle size compared to 6.4 kB of day.js bundle. Since the majority of the functionality
+we are using with these libraries is basic, and the intended goal is to ultimately decrease the bundle size,
+opting for the more lightweight library was the preferred path forward.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 7cb996d04f..2df6b36851 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,6 +29,7 @@
"color-contrast-checker": "^2.1.0",
"core-js": "3.7.0",
"dash-embedded-component": "file:packages/dash-embedded-component-2.0.2.tgz",
+ "dayjs": "^1.11.9",
"file-saver": "1.3.8",
"font-awesome": "4.7.0",
"frontend-platform-shim": "file:packages/frontend-platform-shim",
@@ -37,7 +38,6 @@
"jest-environment-jsdom": "26.6.1",
"lodash": "4.17.21",
"lodash.debounce": "4.0.8",
- "moment": "2.27.0",
"prop-types": "15.7.2",
"react": "16.14.0",
"react-dom": "16.13.1",
@@ -9285,6 +9285,11 @@
"node": ">=10"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.9",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
+ "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -16838,14 +16843,6 @@
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
- "node_modules/moment": {
- "version": "2.27.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
- "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==",
- "engines": {
- "node": "*"
- }
- },
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
diff --git a/package.json b/package.json
index b38707ffd8..87ddae89d0 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"color-contrast-checker": "^2.1.0",
"core-js": "3.7.0",
"dash-embedded-component": "file:packages/dash-embedded-component-2.0.2.tgz",
+ "dayjs": "^1.11.9",
"file-saver": "1.3.8",
"font-awesome": "4.7.0",
"frontend-platform-shim": "file:packages/frontend-platform-shim",
@@ -53,7 +54,6 @@
"jest-environment-jsdom": "26.6.1",
"lodash": "4.17.21",
"lodash.debounce": "4.0.8",
- "moment": "2.27.0",
"prop-types": "15.7.2",
"react": "16.14.0",
"react-dom": "16.13.1",
diff --git a/src/components/Admin/AdminSearchForm.jsx b/src/components/Admin/AdminSearchForm.jsx
index 92d14f7ed8..953929321e 100644
--- a/src/components/Admin/AdminSearchForm.jsx
+++ b/src/components/Admin/AdminSearchForm.jsx
@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
import React from 'react';
-import moment from 'moment';
+import dayjs from 'dayjs';
import PropTypes from 'prop-types';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
@@ -101,7 +101,7 @@ class AdminSearchForm extends React.Component {
value={date}
key={date}
>
- {moment(date).format('MMMM D, YYYY')}
+ {dayjs(date).format('MMMM D, YYYY')}
))}
diff --git a/src/components/Admin/EmbeddedSubscription.jsx b/src/components/Admin/EmbeddedSubscription.jsx
index 22d73e3a28..24bf7575bc 100644
--- a/src/components/Admin/EmbeddedSubscription.jsx
+++ b/src/components/Admin/EmbeddedSubscription.jsx
@@ -1,7 +1,7 @@
import React, { useContext, useState } from 'react';
+import dayjs from 'dayjs';
import { useParams, Link } from 'react-router-dom';
import { Form, Icon } from '@edx/paragon';
-import moment from 'moment';
import { Lightbulb, ArrowOutward } from '@edx/paragon/icons';
import ConnectedSubscriptionDetailPage from './SubscriptionDetailPage';
import { SubscriptionContext } from '../subscriptions/SubscriptionData';
@@ -15,7 +15,7 @@ const EmbeddedSubscription = () => {
const [subscriptionUUID, setSubscriptionUUID] = useState(null);
const [firstLoad, setFirstLoad] = useState(true);
const sortedSubscriptions = sortSubscriptionsByStatus(subscriptions);
- const activeSubscriptions = sortedSubscriptions.filter(c => !moment().isAfter(c.expirationDate));
+ const activeSubscriptions = sortedSubscriptions.filter(c => !dayjs().isAfter(c.expirationDate));
const match = { params: { subscriptionUUID } };
if (!loading && activeSubscriptions.length > 0 && firstLoad) {
match.params.subscriptionUUID = activeSubscriptions[0].uuid;
diff --git a/src/components/Admin/SubscriptionDetails.jsx b/src/components/Admin/SubscriptionDetails.jsx
index 71d556ac3e..2c94bedf5b 100644
--- a/src/components/Admin/SubscriptionDetails.jsx
+++ b/src/components/Admin/SubscriptionDetails.jsx
@@ -1,7 +1,7 @@
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import {
Row, Col, Toast, Button,
} from '@edx/paragon';
@@ -37,7 +37,7 @@ const SubscriptionDetails = ({ enterpriseSlug }) => {
- {moment(subscription.startDate).format('MMMM D, YYYY')} - {moment(subscription.expirationDate).format('MMMM D, YYYY')}
+ {dayjs(subscription.startDate).format('MMMM D, YYYY')} - {dayjs(subscription.expirationDate).format('MMMM D, YYYY')}
diff --git a/src/components/Admin/licenses/LicenseManagementTable/index.jsx b/src/components/Admin/licenses/LicenseManagementTable/index.jsx
index e39c4fa291..e15ed547ea 100644
--- a/src/components/Admin/licenses/LicenseManagementTable/index.jsx
+++ b/src/components/Admin/licenses/LicenseManagementTable/index.jsx
@@ -2,17 +2,18 @@ import _ from 'lodash';
import React, {
useCallback, useMemo, useContext, useState,
} from 'react';
+import PropTypes from 'prop-types';
+import debounce from 'lodash.debounce';
+import dayjs from 'dayjs';
+
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import {
DataTable,
TextFilter,
CheckboxFilter,
Toast,
} from '@edx/paragon';
-import debounce from 'lodash.debounce';
-import moment from 'moment';
-import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-import PropTypes from 'prop-types';
import { SubscriptionContext } from '../../../subscriptions/SubscriptionData';
import { SubscriptionDetailContext } from '../../../subscriptions/SubscriptionDetailContextProvider';
import {
@@ -77,7 +78,7 @@ const LicenseManagementTable = ({ subscriptionUUID }) => {
setUserStatusFilter,
} = useContext(SubscriptionDetailContext);
- const isExpired = moment().isAfter(subscription.expirationDate);
+ const isExpired = dayjs().isAfter(subscription.expirationDate);
const sendStatusFilterEvent = useCallback((statusFilter) => {
sendEnterpriseTrackEvent(
diff --git a/src/components/BulkEnrollmentPage/table/CourseSearchResultsCells.jsx b/src/components/BulkEnrollmentPage/table/CourseSearchResultsCells.jsx
index a0a28d69a3..62a4035ff4 100644
--- a/src/components/BulkEnrollmentPage/table/CourseSearchResultsCells.jsx
+++ b/src/components/BulkEnrollmentPage/table/CourseSearchResultsCells.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Popover, Button, OverlayTrigger } from '@edx/paragon';
+import dayjs from 'dayjs';
-import moment from 'moment';
+import { Popover, Button, OverlayTrigger } from '@edx/paragon';
import { configuration } from '../../../config';
@@ -51,7 +51,7 @@ CourseNameCell.propTypes = {
export const FormattedDateCell = ({ startValue, endValue }) => (
- {moment(startValue).format('MMM D, YYYY')} - {moment(endValue).format('MMM D, YYYY')}
+ {dayjs(startValue).format('MMM D, YYYY')} - {dayjs(endValue).format('MMM D, YYYY')}
);
diff --git a/src/components/CodeManagement/ManageRequestsTab.jsx b/src/components/CodeManagement/ManageRequestsTab.jsx
index 4525567dad..2d51a43111 100644
--- a/src/components/CodeManagement/ManageRequestsTab.jsx
+++ b/src/components/CodeManagement/ManageRequestsTab.jsx
@@ -1,8 +1,9 @@
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
+import dayjs from 'dayjs';
+
import { Stack } from '@edx/paragon';
-import moment from 'moment';
import { camelCaseObject } from '@edx/frontend-platform';
import SubsidyRequestManagementTable, {
@@ -41,9 +42,9 @@ const ManageRequestsTab = ({
return ;
}
- const now = moment();
+ const now = dayjs();
const coupons = couponsData.results;
- const hasAvailableCodes = coupons.some(coupon => moment(coupon.endDate) > now && coupon.numUnassigned > 0);
+ const hasAvailableCodes = coupons.some(coupon => dayjs(coupon.endDate) > now && coupon.numUnassigned > 0);
return (
diff --git a/src/components/CodeManagement/tests/ManageRequestsTab.test.jsx b/src/components/CodeManagement/tests/ManageRequestsTab.test.jsx
index a4a1be4e60..d658fb68a6 100644
--- a/src/components/CodeManagement/tests/ManageRequestsTab.test.jsx
+++ b/src/components/CodeManagement/tests/ManageRequestsTab.test.jsx
@@ -3,13 +3,9 @@ import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
-import moment from 'moment';
+import dayjs from 'dayjs';
import userEvent from '@testing-library/user-event';
-import {
- screen,
- render,
- cleanup,
-} from '@testing-library/react';
+import { screen, render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import ManageRequestsTab from '../ManageRequestsTab';
@@ -254,7 +250,7 @@ describe(' ', () => {
loading: false,
data: {
results: [{
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
numUnassigned: 3,
}],
},
@@ -274,7 +270,7 @@ describe(' ', () => {
loading: false,
data: {
results: [{
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
numUnassigned: 3,
}],
},
@@ -312,7 +308,7 @@ describe(' ', () => {
loading: false,
data: {
results: [{
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
numUnassigned: 3,
}],
},
@@ -374,7 +370,7 @@ describe(' ', () => {
loading: false,
data: {
results: [{
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
numUnassigned: 3,
}],
},
diff --git a/src/components/CodeSearchResults/CodeSearchResultsTable.jsx b/src/components/CodeSearchResults/CodeSearchResultsTable.jsx
index 49db162711..e43469218d 100644
--- a/src/components/CodeSearchResults/CodeSearchResultsTable.jsx
+++ b/src/components/CodeSearchResults/CodeSearchResultsTable.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import moment from 'moment';
+import dayjs from 'dayjs';
import { withRouter } from 'react-router-dom';
import { Icon } from '@edx/paragon';
@@ -42,7 +42,7 @@ const getFormattedDate = (date) => {
if (!date) {
return null;
}
- return moment(date).format('MMMM D, YYYY');
+ return dayjs(date).format('MMMM D, YYYY');
};
const transformSearchResults = results => results.map(({
diff --git a/src/components/Coupon/Coupon.test.jsx b/src/components/Coupon/Coupon.test.jsx
index e0a7bd0730..149d7bbf97 100644
--- a/src/components/Coupon/Coupon.test.jsx
+++ b/src/components/Coupon/Coupon.test.jsx
@@ -67,7 +67,7 @@ describe(' ', () => {
expect(coupon).toMatchSnapshot();
});
- it("without max uses", () => {
+ it('without max uses', () => {
const coupon = renderer
.create(
', () => {
...initialCouponData,
max_uses: null,
}}
- />
+ />,
)
.toJSON();
expect(coupon).toMatchSnapshot();
});
- it("with error state", () => {
+ it('with error state', () => {
const coupon = renderer
.create(
+ />,
)
.toJSON();
expect(coupon).toMatchSnapshot();
@@ -138,7 +138,7 @@ describe(' ', () => {
+ />,
);
fireEvent.keyDown(screen.getByRole('button'), { key: 'A', code: 'KeyA' });
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index ff4ad6ff3d..9b2d2e0579 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -1,8 +1,11 @@
import { useEffect, useState } from 'react';
+import dayjs from 'dayjs';
+import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
+import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
+
import { logError } from '@edx/frontend-platform/logging';
import { getConfig } from '@edx/frontend-platform/config';
import { camelCaseObject } from '@edx/frontend-platform/utils';
-import moment from 'moment';
import EcommerceApiService from '../../../data/services/EcommerceApiService';
import LicenseManagerApiService from '../../../data/services/LicenseManagerAPIService';
@@ -13,6 +16,9 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen,
const [isLoading, setIsLoading] = useState(true);
const [canManageLearnerCredit, setCanManageLearnerCredit] = useState(false);
+ dayjs.extend(isSameOrBefore);
+ dayjs.extend(isSameOrAfter);
+
useEffect(() => {
setIsLoading(true);
const fetchOffers = async () => {
@@ -37,8 +43,8 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen,
const subsidy = results[0];
const isCurrent = source === 'ecommerceApi'
? subsidy.isCurrent
- : moment().isSameOrBefore(subsidy.expirationDatetime)
- && moment().isSameOrAfter(subsidy.activeDatetime);
+ : dayjs().isSameOrBefore(subsidy.expirationDatetime)
+ && dayjs().isSameOrAfter(subsidy.activeDatetime);
const offerData = {
id: subsidy.uuid || subsidy.id,
name: subsidy.title || subsidy.displayName,
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index 12eada0677..ab01eb3d40 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -1,9 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
+import dayjs from 'dayjs';
import {
DataTable, useMediaQuery, breakpoints,
} from '@edx/paragon';
-import moment from 'moment';
import TableTextFilter from './TableTextFilter';
import EmailAddressTableCell from './EmailAddressTableCell';
@@ -50,7 +50,7 @@ const LearnerCreditAllocationTable = ({
{
Header: 'Date Spent',
accessor: 'enrollmentDate',
- Cell: ({ row }) => moment(row.values.enrollmentDate).format('MMMM DD, YYYY'),
+ Cell: ({ row }) => dayjs(row.values.enrollmentDate).format('MMMM DD, YYYY'),
disableFilters: true,
},
{
diff --git a/src/components/learner-credit-management/LearnerCreditManagement.jsx b/src/components/learner-credit-management/LearnerCreditManagement.jsx
index 788ce48e23..5f3ce77d4c 100644
--- a/src/components/learner-credit-management/LearnerCreditManagement.jsx
+++ b/src/components/learner-credit-management/LearnerCreditManagement.jsx
@@ -4,7 +4,7 @@ import React, {
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import {
Badge,
Container,
@@ -86,7 +86,7 @@ const LearnerCreditManagement = ({ enterpriseUUID }) => {
{isLoadingOfferSummary || isLoadingOfferRedemptions ? (
) : (
-
+
)}
moment(timestamp).isBefore(moment());
+const isDatePast = timestamp => dayjs(timestamp).isBefore(dayjs());
/**
* Formats provided dates for display, handling when `start` and/or
@@ -22,13 +21,13 @@ const isDatePast = timestamp => moment(timestamp).isBefore(moment());
const formatDates = (start, end) => {
let formattedDates = null;
if (start && end) {
- formattedDates = `${moment(start).format(DATE_FORMAT)} - ${moment(end).format(DATE_FORMAT)}`;
+ formattedDates = `${dayjs(start).format(DATE_FORMAT)} - ${dayjs(end).format(DATE_FORMAT)}`;
} else if (start && !end) {
const startLabel = isDatePast(start) ? 'Started' : 'Starts';
- formattedDates = `${startLabel} ${moment(start).format(DATE_FORMAT)}`;
+ formattedDates = `${startLabel} ${dayjs(start).format(DATE_FORMAT)}`;
} else if (!start && end) {
const endLabel = isDatePast(end) ? 'Ended' : 'Ends';
- formattedDates = `${endLabel} ${moment(end).format(DATE_FORMAT)}`;
+ formattedDates = `${endLabel} ${dayjs(end).format(DATE_FORMAT)}`;
}
return formattedDates;
};
diff --git a/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx
index be0430a93e..f268b2faf7 100644
--- a/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx
+++ b/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx
@@ -3,7 +3,7 @@ import React from 'react';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
-import moment from 'moment';
+import dayjs from 'dayjs';
import {
screen,
render,
@@ -165,7 +165,7 @@ describe(' ', () => {
expect(screen.getByText(mockOffer.start));
expect(screen.getByText(mockOffer.end));
- expect(screen.getByText(`Data last updated on ${moment(mockOfferRedemption.created).format(DATE_FORMAT)}`, { exact: false }));
+ expect(screen.getByText(`Data last updated on ${dayjs(mockOfferRedemption.created).format(DATE_FORMAT)}`, { exact: false }));
expect(screen.getByTestId('learner-credit-allocation--is-loading')).toHaveTextContent('is NOT loading');
expect(screen.getByTestId('learner-credit-allocation--table-data')).toHaveTextContent(mockOfferRedemption.enterpriseEnrollmentId);
diff --git a/src/components/learner-credit-management/tests/OfferDates.test.jsx b/src/components/learner-credit-management/tests/OfferDates.test.jsx
index 15b0aa6d1a..be917d8cca 100644
--- a/src/components/learner-credit-management/tests/OfferDates.test.jsx
+++ b/src/components/learner-credit-management/tests/OfferDates.test.jsx
@@ -3,7 +3,7 @@ import {
screen,
render,
} from '@testing-library/react';
-import moment from 'moment';
+import dayjs from 'dayjs';
import OfferDates from '../OfferDates';
import { DATE_FORMAT } from '../data/constants';
@@ -16,7 +16,7 @@ describe(' ', () => {
describe('with start date only', () => {
it('date is past', () => {
- const startDate = moment('2022-01-31');
+ const startDate = dayjs('2022-01-31');
const props = {
start: startDate.toISOString(),
};
@@ -25,7 +25,7 @@ describe(' ', () => {
expect(screen.getByText(`Started ${startDate.format(DATE_FORMAT)}`, { exact: false }));
});
it('date is current/future', () => {
- const startDate = moment().add(5, 'd');
+ const startDate = dayjs().add(5, 'd');
const props = {
start: startDate.toISOString(),
};
@@ -37,7 +37,7 @@ describe(' ', () => {
describe('with end date only', () => {
it('date is past', () => {
- const endDate = moment('2022-01-31');
+ const endDate = dayjs('2022-01-31');
const props = {
end: endDate.toISOString(),
};
@@ -46,7 +46,7 @@ describe(' ', () => {
expect(screen.getByText(`Ended ${endDate.format(DATE_FORMAT)}`, { exact: false }));
});
it('date is current/future', () => {
- const endDate = moment().add(5, 'd');
+ const endDate = dayjs().add(5, 'd');
const props = {
end: endDate.toISOString(),
};
@@ -58,7 +58,7 @@ describe(' ', () => {
describe('with both start and end dates', () => {
it('displays both dates', () => {
- const startDate = moment('2022-01-31');
+ const startDate = dayjs('2022-01-31');
const endDate = startDate.add(1, 'y');
const props = {
start: startDate.toISOString(),
diff --git a/src/components/settings/SettingsAccessTab/tests/SettingsAccessLinkManagement.test.jsx b/src/components/settings/SettingsAccessTab/tests/SettingsAccessLinkManagement.test.jsx
index 4d42fb0ef9..0c8b43d695 100644
--- a/src/components/settings/SettingsAccessTab/tests/SettingsAccessLinkManagement.test.jsx
+++ b/src/components/settings/SettingsAccessTab/tests/SettingsAccessLinkManagement.test.jsx
@@ -1,14 +1,12 @@
/* eslint-disable react/prop-types */
import React from 'react';
-import {
- screen,
- render,
- act,
-} from '@testing-library/react';
+import dayjs from 'dayjs';
+import { screen, render, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-import moment from 'moment';
import { IntlProvider } from '@edx/frontend-platform/i18n';
+
import LmsApiService from '../../../../data/services/LmsApiService';
import MockSettingsContext, { MOCK_CONSTANTS, generateStore } from './TestUtils';
import SettingsAccessLinkManagement from '../SettingsAccessLinkManagement';
@@ -110,8 +108,8 @@ describe(' ', () => {
test('Toggle Universal Link On', async () => {
LmsApiService.toggleEnterpriseCustomerUniversalLink.mockReturnValue({ data: {} });
- const subExpirationDate = moment().add(1, 'days').format();
- const couponExpirationDate = moment().add(3, 'days').format();
+ const subExpirationDate = dayjs().add(1, 'days').format();
+ const couponExpirationDate = dayjs().add(3, 'days').format();
render(
{
@@ -66,7 +66,7 @@ describe('Portal Appearance Tab', () => {
@@ -85,7 +85,7 @@ describe('Portal Appearance Tab', () => {
@@ -110,7 +110,7 @@ describe('Portal Appearance Tab', () => {
@@ -130,7 +130,7 @@ describe('Portal Appearance Tab', () => {
});
});
test('autoselects correct brand card', async () => {
- entepriseBranding = {
+ enterpriseBranding = {
primary_color: SAGE_THEME.button,
secondary_color: SAGE_THEME.banner,
tertiary_color: SAGE_THEME.accent,
@@ -141,7 +141,7 @@ describe('Portal Appearance Tab', () => {
@@ -152,7 +152,7 @@ describe('Portal Appearance Tab', () => {
});
test('creating custom theme card', async () => {
const spy = jest.spyOn(LmsApiService, 'updateEnterpriseCustomerBranding');
- entepriseBranding = {
+ enterpriseBranding = {
primary_color: SAGE_THEME.button,
secondary_color: SAGE_THEME.banner,
tertiary_color: SAGE_THEME.accent,
@@ -163,7 +163,7 @@ describe('Portal Appearance Tab', () => {
@@ -175,7 +175,7 @@ describe('Portal Appearance Tab', () => {
});
expect(screen.getByText('Customize the admin and learner edX experience using your own brand colors. Enter color values in hexadecimal code.')).toBeInTheDocument();
userEvent.type(screen.getByLabelText('Banner color'), 'bad number');
- expect(screen.getByText('Must be hexidecimal starting with # (Ex: #1e0b57)')).toBeInTheDocument();
+ expect(screen.getByText('Must be hexadecimal starting with # (Ex: #1e0b57)')).toBeInTheDocument();
userEvent.type(screen.getByLabelText('Button color'), '#023E8A');
userEvent.type(screen.getByLabelText('Accent color'), '#0077b6');
expect(screen.getByText('Add theme')).toBeDisabled();
@@ -199,7 +199,7 @@ describe('Portal Appearance Tab', () => {
});
});
test('editing and deleting custom card', async () => {
- entepriseBranding = {
+ enterpriseBranding = {
primary_color: '#03045e',
secondary_color: '#023E8A',
tertiary_color: '#0077b6',
@@ -210,7 +210,7 @@ describe('Portal Appearance Tab', () => {
diff --git a/src/components/subscriptions/SubscriptionCard.jsx b/src/components/subscriptions/SubscriptionCard.jsx
index 99a02117e2..af8345ed6a 100644
--- a/src/components/subscriptions/SubscriptionCard.jsx
+++ b/src/components/subscriptions/SubscriptionCard.jsx
@@ -1,14 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
-import moment from 'moment';
+import dayjs from 'dayjs';
import { Link } from 'react-router-dom';
import {
- Card,
- Badge,
- Button,
- Stack,
- Row,
- Col,
+ Card, Badge, Button, Stack, Row, Col,
} from '@edx/paragon';
import classNames from 'classnames';
@@ -26,8 +21,8 @@ const SubscriptionCard = ({
licenses = {},
} = subscription;
- const formattedStartDate = moment(startDate).format('MMMM D, YYYY');
- const formattedExpirationDate = moment(expirationDate).format('MMMM D, YYYY');
+ const formattedStartDate = dayjs(startDate).format('MMMM D, YYYY');
+ const formattedExpirationDate = dayjs(expirationDate).format('MMMM D, YYYY');
const subscriptionStatus = getSubscriptionStatus(subscription);
const renderDaysUntilPlanStartText = (className) => {
@@ -35,8 +30,8 @@ const SubscriptionCard = ({
return null;
}
- const now = moment();
- const planStart = moment(startDate);
+ const now = dayjs();
+ const planStart = dayjs(startDate);
const daysUntilPlanStart = planStart.diff(now, 'days');
const hoursUntilPlanStart = planStart.diff(now, 'hours');
diff --git a/src/components/subscriptions/SubscriptionDetails.jsx b/src/components/subscriptions/SubscriptionDetails.jsx
index 54763c704d..3ef662d0c2 100644
--- a/src/components/subscriptions/SubscriptionDetails.jsx
+++ b/src/components/subscriptions/SubscriptionDetails.jsx
@@ -2,7 +2,7 @@ import React, { useContext, useState } from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faAngleLeft } from '@fortawesome/free-solid-svg-icons';
import {
@@ -74,7 +74,7 @@ const SubscriptionDetails = ({ enterpriseSlug }) => {
Purchase Date
- {moment(subscription.priorRenewals[0].priorSubscriptionPlanStartDate).format('MMMM D, YYYY')}
+ {dayjs(subscription.priorRenewals[0].priorSubscriptionPlanStartDate).format('MMMM D, YYYY')}
)}
@@ -83,7 +83,7 @@ const SubscriptionDetails = ({ enterpriseSlug }) => {
Start Date
- {moment(subscription.startDate).format('MMMM D, YYYY')}
+ {dayjs(subscription.startDate).format('MMMM D, YYYY')}
@@ -91,7 +91,7 @@ const SubscriptionDetails = ({ enterpriseSlug }) => {
End Date
- {moment(subscription.expirationDate).format('MMMM D, YYYY')}
+ {dayjs(subscription.expirationDate).format('MMMM D, YYYY')}
diff --git a/src/components/subscriptions/SubscriptionPlanRoutes.jsx b/src/components/subscriptions/SubscriptionPlanRoutes.jsx
index 5613a3bb6f..3cc3f85ad6 100644
--- a/src/components/subscriptions/SubscriptionPlanRoutes.jsx
+++ b/src/components/subscriptions/SubscriptionPlanRoutes.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import moment from 'moment';
+import dayjs from 'dayjs';
import { Route } from 'react-router-dom';
import { connect } from 'react-redux';
@@ -11,7 +11,7 @@ import { MANAGE_LEARNERS_TAB } from './data/constants';
const SubscriptionPlanRoutes = ({ enterpriseSlug }) => {
const multipleSubsCreateActions = (subscription) => {
- const now = moment();
+ const now = dayjs();
const isScheduled = now.isBefore(subscription.startDate);
const isExpired = now.isAfter(subscription.expirationDate);
const buttonText = `${isExpired ? 'View' : 'Manage'} learners`;
diff --git a/src/components/subscriptions/data/utils.js b/src/components/subscriptions/data/utils.js
index 88abf1fc6e..2829929b24 100644
--- a/src/components/subscriptions/data/utils.js
+++ b/src/components/subscriptions/data/utils.js
@@ -1,4 +1,4 @@
-import moment from 'moment';
+import dayjs from 'dayjs';
import {
SEEN_SUBSCRIPTION_EXPIRATION_MODAL_COOKIE_PREFIX,
ASSIGNED,
@@ -23,7 +23,7 @@ export const canRemindLicense = (licenseStatus) => licenseStatus === ASSIGNED;
export const canEnrollLicense = (licenseStatus) => ENROLLABLE_STATUSES.includes(licenseStatus);
export const getSubscriptionStatus = (subscription) => {
- const now = moment();
+ const now = dayjs();
if (now.isBefore(subscription.startDate)) {
return SCHEDULED;
@@ -46,7 +46,7 @@ export const sortSubscriptionsByStatus = (subscriptions) => subscriptions.slice(
const sub2Status = getSubscriptionStatus(sub2);
if (sub1Status === sub2Status) {
- return moment(sub1.startDate) - moment(sub2.startDate);
+ return dayjs(sub1.startDate) - dayjs(sub2.startDate);
}
return orderByStatus[sub1Status] - orderByStatus[sub2Status];
diff --git a/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRemindModal.jsx b/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRemindModal.jsx
index 208a849da3..6186701924 100644
--- a/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRemindModal.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRemindModal.jsx
@@ -1,5 +1,7 @@
import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import dayjs from 'dayjs';
import {
Alert,
StatefulButton,
@@ -10,8 +12,6 @@ import {
Hyperlink,
} from '@edx/paragon';
import { logError } from '@edx/frontend-platform/logging';
-import { connect } from 'react-redux';
-import moment from 'moment';
import { useRequestState } from './LicenseManagementModalHook';
import { validateEmailTemplateForm } from '../../../../data/validation/email';
@@ -63,7 +63,7 @@ const LicenseManagementRemindModal = ({
const [requestState, setRequestState, initialRequestState] = useRequestState(isOpen);
const [emailTemplate, setEmailTemplate] = useState(generateEmailTemplate(contactEmail));
- const isExpired = moment().isAfter(subscription.expirationDate);
+ const isExpired = dayjs().isAfter(subscription.expirationDate);
const buttonLabels = generateRemindModalSubmitLabel(totalToRemind);
diff --git a/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRevokeModal.jsx b/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRevokeModal.jsx
index 953e56eb66..0feb9ebb23 100644
--- a/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRevokeModal.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementModals/LicenseManagementRevokeModal.jsx
@@ -1,5 +1,7 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
+import dayjs from 'dayjs';
+
import {
Alert,
StatefulButton,
@@ -13,7 +15,6 @@ import {
RemoveCircle,
} from '@edx/paragon/icons';
import { logError } from '@edx/frontend-platform/logging';
-import moment from 'moment';
import { useRequestState } from './LicenseManagementModalHook';
import { configuration } from '../../../../config';
@@ -74,7 +75,7 @@ const LicenseManagementRevokeModal = ({
const title = `Revoke License${revokeAllUsers || totalToRevoke > 1 ? 's' : ''}`;
- const isExpired = moment().isAfter(subscription.expirationDate);
+ const isExpired = dayjs().isAfter(subscription.expirationDate);
const handleSubmit = useCallback(async () => {
if (onSubmit) {
diff --git a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx
index 00a80e0987..73fb27b88e 100644
--- a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx
@@ -1,4 +1,5 @@
import React from 'react';
+import dayjs from 'dayjs';
import {
screen,
render,
@@ -7,10 +8,9 @@ import {
waitFor,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { logError } from '@edx/frontend-platform/logging';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
-import moment from 'moment';
+import { logError } from '@edx/frontend-platform/logging';
import LicenseManagerApiService from '../../../../../data/services/LicenseManagerAPIService';
import LicenseManagementRemindModal from '../LicenseManagementRemindModal';
@@ -38,7 +38,7 @@ const basicProps = {
onSubmit: onSubmitMock,
subscription: {
uuid: 'lorem',
- expirationDate: moment().add(1, 'days').format(), // tomorrow
+ expirationDate: dayjs().add(1, 'days').format(), // tomorrow
},
usersToRemind: [],
activeFilters: [],
diff --git a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx
index 09fc0b9da7..d9bb7b769c 100644
--- a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx
@@ -1,4 +1,5 @@
import React from 'react';
+import dayjs from 'dayjs';
import {
screen,
render,
@@ -8,7 +9,6 @@ import {
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { logError } from '@edx/frontend-platform/logging';
-import moment from 'moment';
import LicenseManagerApiService from '../../../../../data/services/LicenseManagerAPIService';
import LicenseManagementRevokeModal from '../LicenseManagementRevokeModal';
@@ -30,7 +30,7 @@ const basicProps = {
onSubmit: onSubmitMock,
subscription: {
uuid: 'lorem',
- expirationDate: moment().add(1, 'days').format(), // tomorrow
+ expirationDate: dayjs().add(1, 'days').format(), // tomorrow
isRevocationCapEnabled: false,
revocations: {
applied: 1,
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx
index 1468b242a4..e01e23160d 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx
@@ -6,7 +6,7 @@ import {
import userEvent from '@testing-library/user-event';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import '@testing-library/jest-dom/extend-expect';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
@@ -55,7 +55,7 @@ const basicProps = {
uuid: TEST_SUBSCRIPTION_PLAN_UUID,
enterpriseCustomerUuid: TEST_ENTERPRISE_CUSTOMER_UUID,
enterpriseCatalogUuid: TEST_ENTERPRISE_CUSTOMER_CATALOG_UUID,
- expirationDate: moment().add(1, 'year').toISOString(),
+ expirationDate: dayjs().add(1, 'year').toISOString(),
},
onRemindSuccess: mockOnRemindSuccess,
activatedUsersCount: 0,
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx
index bb333be882..2e2bbcf624 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx
@@ -1,12 +1,9 @@
import React from 'react';
-import {
- screen,
- render,
-} from '@testing-library/react';
+import { screen, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import '@testing-library/jest-dom/extend-expect';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
@@ -55,7 +52,7 @@ const basicProps = {
uuid: TEST_SUBSCRIPTION_PLAN_UUID,
enterpriseCustomerUuid: TEST_ENTERPRISE_CUSTOMER_UUID,
enterpriseCatalogUuid: TEST_ENTERPRISE_CUSTOMER_CATALOG_UUID,
- expirationDate: moment().add(1, 'year').toISOString(),
+ expirationDate: dayjs().add(1, 'year').toISOString(),
isRevocationCapEnabled: false,
},
onRevokeSuccess: mockOnRevokeSuccess,
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/index.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/index.jsx
index 71e2dd74bc..a3e8376bbe 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/index.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/index.jsx
@@ -1,6 +1,8 @@
import React, {
useCallback, useMemo, useContext, useState,
} from 'react';
+import dayjs from 'dayjs';
+import debounce from 'lodash.debounce';
import {
DataTable,
TextFilter,
@@ -9,8 +11,6 @@ import {
breakpoints,
Toast,
} from '@edx/paragon';
-import debounce from 'lodash.debounce';
-import moment from 'moment';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { SubscriptionContext } from '../../SubscriptionData';
@@ -76,7 +76,7 @@ const LicenseManagementTable = () => {
setUserStatusFilter,
} = useContext(SubscriptionDetailContext);
- const isExpired = moment().isAfter(subscription.expirationDate);
+ const isExpired = dayjs().isAfter(subscription.expirationDate);
const sendStatusFilterEvent = useCallback((statusFilter) => {
sendEnterpriseTrackEvent(
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/tests/LicenseManagementTableActionColumn.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/tests/LicenseManagementTableActionColumn.test.jsx
index babf83da16..17ceea5511 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/tests/LicenseManagementTableActionColumn.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/tests/LicenseManagementTableActionColumn.test.jsx
@@ -6,7 +6,7 @@ import {
cleanup,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import moment from 'moment';
+import dayjs from 'dayjs';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
@@ -51,7 +51,7 @@ const basicProps = {
subscription: {
uuid: TEST_SUBSCRIPTION_PLAN_UUID,
enterpriseCustomerUuid: TEST_ENTERPRISE_CUSTOMER_UUID,
- expirationDate: moment().add(1, 'days').format(),
+ expirationDate: dayjs().add(1, 'days').format(),
isRevocationCapEnabled: false,
revocations: {
applied: 0,
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx
index 23bf65d3b1..6ac5cc5086 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx
@@ -7,7 +7,7 @@ import {
fireEvent,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import moment from 'moment';
+import dayjs from 'dayjs';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { IntlProvider } from '@edx/frontend-platform/i18n';
@@ -121,9 +121,9 @@ describe(' ', () => {
mockSubscriptionHooks(
subscriptionPlan,
[{
- activationDate: moment(),
+ activationDate: dayjs(),
activationKey: 'test-activation-key',
- lastRemindDate: moment(),
+ lastRemindDate: dayjs(),
revokedDate: null,
status: 'activated',
subscriptionPlan: {},
diff --git a/src/components/subscriptions/tests/SubscriptionCard.test.jsx b/src/components/subscriptions/tests/SubscriptionCard.test.jsx
index 89afa0e46f..3c70a73240 100644
--- a/src/components/subscriptions/tests/SubscriptionCard.test.jsx
+++ b/src/components/subscriptions/tests/SubscriptionCard.test.jsx
@@ -1,10 +1,8 @@
-import {
- screen,
-} from '@testing-library/react';
+import React from 'react';
+import dayjs from 'dayjs';
+import { screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
-import React from 'react';
-import moment from 'moment';
import {
breakpoints,
ResponsiveContext,
@@ -30,11 +28,11 @@ const defaultProps = {
};
const responsiveContextValue = { width: breakpoints.extraSmall.maxWidth };
-jest.mock('moment', () => (date) => {
+jest.mock('dayjs', () => (date) => {
if (date) {
- return jest.requireActual('moment')(date);
+ return jest.requireActual('dayjs')(date);
}
- return jest.requireActual('moment')('2020-01-01T00:00:00.000Z');
+ return jest.requireActual('dayjs')('2020-01-01T00:00:00.000Z');
});
describe('SubscriptionCard', () => {
@@ -45,10 +43,10 @@ describe('SubscriptionCard', () => {
});
it.each([
- [moment().add(1, 'days').toISOString(), '1 day'],
- [moment().add(3, 'days').toISOString(), '3 days'],
- [moment().add(1, 'hours').toISOString(), '1 hour'],
- [moment().add(3, 'hours').toISOString(), '3 hours'],
+ [dayjs().add(1, 'days').toISOString(), '1 day'],
+ [dayjs().add(3, 'days').toISOString(), '3 days'],
+ [dayjs().add(1, 'hours').toISOString(), '1 hour'],
+ [dayjs().add(3, 'hours').toISOString(), '3 hours'],
])('displays days until plan starts text if there are no actions and the plan is scheduled', (startDate, expectedText) => {
renderWithRouter(
diff --git a/src/components/subscriptions/tests/SubscriptionManagementPage.test.jsx b/src/components/subscriptions/tests/SubscriptionManagementPage.test.jsx
index 74331eaaa6..b23d9f5d9a 100644
--- a/src/components/subscriptions/tests/SubscriptionManagementPage.test.jsx
+++ b/src/components/subscriptions/tests/SubscriptionManagementPage.test.jsx
@@ -1,10 +1,10 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Provider } from 'react-redux';
-import moment from 'moment';
+import dayjs from 'dayjs';
import { screen } from '@testing-library/react';
-import { IntlProvider } from '@edx/frontend-platform/i18n';
import '@testing-library/jest-dom/extend-expect';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
import {
TEST_ENTERPRISE_CUSTOMER_SLUG, createMockStore,
@@ -23,8 +23,8 @@ describe('SubscriptionManagementPage', () => {
{
uuid: 'active',
title: 'Enterprise A',
- startDate: moment().toISOString(),
- expirationDate: moment().add(3, 'days').toISOString(),
+ startDate: dayjs().toISOString(),
+ expirationDate: dayjs().add(3, 'days').toISOString(),
licenses: {
activated: 5,
assigned: 5,
@@ -37,8 +37,8 @@ describe('SubscriptionManagementPage', () => {
{
uuid: 'expired',
title: 'Enterprise B',
- startDate: moment().toISOString(),
- expirationDate: moment().subtract(3, 'days').toISOString(),
+ startDate: dayjs().toISOString(),
+ expirationDate: dayjs().subtract(3, 'days').toISOString(),
licenses: {
activated: 6,
assigned: 5,
diff --git a/src/components/subscriptions/tests/TestUtilities.jsx b/src/components/subscriptions/tests/TestUtilities.jsx
index c1b37cf0c8..774467c4f0 100644
--- a/src/components/subscriptions/tests/TestUtilities.jsx
+++ b/src/components/subscriptions/tests/TestUtilities.jsx
@@ -4,9 +4,9 @@ import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { createMemoryHistory } from 'history';
-import moment from 'moment';
-
+import dayjs from 'dayjs';
import PropTypes from 'prop-types';
+
import SubscriptionData from '../SubscriptionData';
import { ASSIGNED } from '../data/constants';
import SubscriptionDetailContextProvider from '../SubscriptionDetailContextProvider';
@@ -221,7 +221,7 @@ export const generateSubscriptionPlan = (
daysUntilExpiration = 2,
agreementNetDaysUntilExpiration = daysUntilExpiration,
) => {
- const startDate = moment().subtract(1, 'days');
+ const startDate = dayjs().subtract(1, 'days');
return {
title: TEST_SUBSCRIPTION_PLAN_TITLE,
uuid: TEST_SUBSCRIPTION_PLAN_UUID,
@@ -259,9 +259,9 @@ export const generateSubscriptionUser = ({
userEmail = 'edx@example.com',
status = 'activated',
}) => ({
- activationDate: moment(),
+ activationDate: dayjs(),
activationKey: 'test-activation-key',
- lastRemindDate: moment(),
+ lastRemindDate: dayjs(),
revokedDate: null,
status,
userEmail,
diff --git a/src/components/subscriptions/tests/data/utils.test.js b/src/components/subscriptions/tests/data/utils.test.js
index f7a1db5a3b..27a23a9061 100644
--- a/src/components/subscriptions/tests/data/utils.test.js
+++ b/src/components/subscriptions/tests/data/utils.test.js
@@ -1,19 +1,19 @@
-import moment from 'moment';
+import dayjs from 'dayjs';
import { ACTIVE, SCHEDULED, ENDED } from '../../data/constants';
import { sortSubscriptionsByStatus, getSubscriptionStatus } from '../../data/utils';
describe('utils', () => {
const scheduledSub = {
- startDate: moment().add(1, 'days'),
- expirationDate: moment().add(10, 'days'),
+ startDate: dayjs().add(1, 'days'),
+ expirationDate: dayjs().add(10, 'days'),
};
const activeSub = {
- startDate: moment().subtract(1, 'days'),
- expirationDate: moment().add(10, 'days'),
+ startDate: dayjs().subtract(1, 'days'),
+ expirationDate: dayjs().add(10, 'days'),
};
const expiredSub = {
- startDate: moment().subtract(10, 'days'),
- expirationDate: moment().subtract(1, 'days'),
+ startDate: dayjs().subtract(10, 'days'),
+ expirationDate: dayjs().subtract(1, 'days'),
};
describe('getSubscriptionStatus', () => {
@@ -34,8 +34,8 @@ describe('utils', () => {
it('should sort subscriptions by start date after status', () => {
const activeSub2 = {
- startDate: moment().subtract(2, 'days'),
- expirationDate: moment().add(10, 'days'),
+ startDate: dayjs().subtract(2, 'days'),
+ expirationDate: dayjs().add(10, 'days'),
};
const initialOrder = [expiredSub, activeSub, scheduledSub, activeSub2];
const expectedOrder = [activeSub2, activeSub, scheduledSub, expiredSub];
diff --git a/src/components/subsidy-request-management-alerts/NoAvailableCodesBanner.jsx b/src/components/subsidy-request-management-alerts/NoAvailableCodesBanner.jsx
index f003229737..001de41d99 100644
--- a/src/components/subsidy-request-management-alerts/NoAvailableCodesBanner.jsx
+++ b/src/components/subsidy-request-management-alerts/NoAvailableCodesBanner.jsx
@@ -1,8 +1,8 @@
import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import dayjs from 'dayjs';
import { Alert } from '@edx/paragon';
-import PropTypes from 'prop-types';
-import moment from 'moment';
import ContactCustomerSupportButton from '../ContactCustomerSupportButton';
export const NoAvailableCodesBanner = ({ coupons }) => {
@@ -16,8 +16,8 @@ export const NoAvailableCodesBanner = ({ coupons }) => {
return null;
}
- const now = moment();
- const nonExpiredCoupons = coupons.filter(coupon => moment(coupon.endDate) > now);
+ const now = dayjs();
+ const nonExpiredCoupons = coupons.filter(coupon => dayjs(coupon.endDate) > now);
const renderAlert = (heading, body) => (
(
@@ -26,7 +26,7 @@ describe(' ', () => {
coupons={[
{
numUnassigned: 1,
- endDate: moment().subtract(1, 'days').toISOString(),
+ endDate: dayjs().subtract(1, 'days').toISOString(),
},
]}
/>,
@@ -41,11 +41,11 @@ describe(' ', () => {
coupons={[
{
numUnassigned: 0,
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
},
{
numUnassigned: 1,
- endDate: moment().subtract(1, 'days').toISOString(),
+ endDate: dayjs().subtract(1, 'days').toISOString(),
},
]}
/>
@@ -59,11 +59,11 @@ describe(' ', () => {
coupons={[
{
numUnassigned: 0,
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
},
{
numUnassigned: 1,
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
},
]}
/>,
@@ -71,12 +71,12 @@ describe(' ', () => {
expect(container.childElementCount).toEqual(0);
});
- it('should dimiss banner', async () => {
+ it('should dismiss banner', async () => {
const { getByText, container } = render((
));
diff --git a/src/components/subsidy-request-modals/data/hooks.js b/src/components/subsidy-request-modals/data/hooks.js
index 490b026eba..5d17548f76 100644
--- a/src/components/subsidy-request-modals/data/hooks.js
+++ b/src/components/subsidy-request-modals/data/hooks.js
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
+import dayjs from 'dayjs';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import { logError } from '@edx/frontend-platform/logging';
-import moment from 'moment';
import EnterpriseCatalogApiService from '../../../data/services/EnterpriseCatalogApiService';
/**
@@ -91,11 +91,11 @@ export const useApplicableCoupons = ({
useEffect(() => {
if (applicableCatalogs.length > 0 && coupons.results.length > 0) {
- const now = moment();
+ const now = dayjs();
const applicableCoups = coupons.results.filter(
coupon => applicableCatalogs.includes(
coupon.enterpriseCatalogUuid,
- ) && moment(coupon.endDate) > now && coupon.numUnassigned > 0,
+ ) && dayjs(coupon.endDate) > now && coupon.numUnassigned > 0,
);
setApplicableCoupons(applicableCoups);
diff --git a/src/components/subsidy-request-modals/tests/hooks.test.jsx b/src/components/subsidy-request-modals/tests/hooks.test.jsx
index 369e49df68..84d8691a4e 100644
--- a/src/components/subsidy-request-modals/tests/hooks.test.jsx
+++ b/src/components/subsidy-request-modals/tests/hooks.test.jsx
@@ -1,5 +1,5 @@
+import dayjs from 'dayjs';
import { renderHook } from '@testing-library/react-hooks/dom';
-import moment from 'moment';
import { useApplicableCatalogs, useApplicableSubscriptions, useApplicableCoupons } from '../data/hooks';
import EnterpriseCatalogApiService from '../../../data/services/EnterpriseCatalogApiService';
@@ -116,21 +116,21 @@ describe('useApplicableCoupons', () => {
id: 1,
numUnassigned: 1,
enterpriseCatalogUuid: TEST_CATALOG_UUID,
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
maxUses: 3,
},
{
id: 2,
numUnassigned: 1,
enterpriseCatalogUuid: 'abc',
- endDate: moment().add(1, 'days').toISOString(),
+ endDate: dayjs().add(1, 'days').toISOString(),
maxUses: 3,
},
{
id: 3,
numUnassigned: 3,
enterpriseCatalogUuid: TEST_CATALOG_UUID,
- endDate: moment().subtract(1, 'days').toISOString(),
+ endDate: dayjs().subtract(1, 'days').toISOString(),
maxUses: 3,
}],
};
diff --git a/src/utils.js b/src/utils.js
index f92dff63b7..44fe64bc94 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,4 +1,4 @@
-import moment from 'moment';
+import dayjs from 'dayjs';
import camelCase from 'lodash/camelCase';
import snakeCase from 'lodash/snakeCase';
import isArray from 'lodash/isArray';
@@ -28,7 +28,7 @@ import LmsApiService from './data/services/LmsApiService';
const formatTimestamp = ({ timestamp, format = 'MMMM D, YYYY' }) => {
if (timestamp) {
- return moment(timestamp).format(format);
+ return dayjs(timestamp).format(format);
}
return null;
};
From 21489d06d0c7af12f2ec0bad2d55a4bdb960f29c Mon Sep 17 00:00:00 2001
From: irfanuddinahmad <34648393+irfanuddinahmad@users.noreply.github.com>
Date: Wed, 2 Aug 2023 15:41:04 +0500
Subject: [PATCH 012/124] feat: ENT-7445 Added support for picking first active
subsidy when multiple available (#1015)
Co-authored-by: IrfanUddinAhmad
---
.../EnterpriseSubsidiesContext/data/hooks.js | 40 ++++++-----
.../data/tests/hooks.test.js | 71 ++++++++++++++++---
2 files changed, 85 insertions(+), 26 deletions(-)
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index 9b2d2e0579..123d022df1 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -38,25 +38,29 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen,
results = camelCaseObject(ecommerceApiResponse.data.results);
source = 'ecommerceApi';
}
-
+ let activeSubsidyFound = false;
if (results.length !== 0) {
- const subsidy = results[0];
- const isCurrent = source === 'ecommerceApi'
- ? subsidy.isCurrent
- : dayjs().isSameOrBefore(subsidy.expirationDatetime)
- && dayjs().isSameOrAfter(subsidy.activeDatetime);
- const offerData = {
- id: subsidy.uuid || subsidy.id,
- name: subsidy.title || subsidy.displayName,
- start: subsidy.activeDatetime || subsidy.startDatetime,
- end: subsidy.expirationDatetime || subsidy.endDatetime,
- isCurrent,
- };
- setOffers([offerData]);
- }
- // We only released learner credit management to customers with 1 offer for the MVP.
- if (results.length === 1) {
- setCanManageLearnerCredit(true);
+ let subsidy = results[0];
+ for (let i = 0; i < results.length; i++) {
+ subsidy = results[i];
+ activeSubsidyFound = source === 'ecommerceApi'
+ ? subsidy.isCurrent
+ : subsidy.isActive;
+ if (activeSubsidyFound === true) {
+ break;
+ }
+ }
+ if (activeSubsidyFound === true) {
+ const offerData = {
+ id: subsidy.uuid || subsidy.id,
+ name: subsidy.title || subsidy.displayName,
+ start: subsidy.activeDatetime || subsidy.startDatetime,
+ end: subsidy.expirationDatetime || subsidy.endDatetime,
+ isCurrent: activeSubsidyFound,
+ };
+ setOffers([offerData]);
+ setCanManageLearnerCredit(true);
+ }
}
} catch (error) {
logError(error);
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
index 668fa9d386..adf8580b52 100644
--- a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
+++ b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
@@ -94,6 +94,7 @@ describe('useEnterpriseOffers', () => {
title: 'offer-name',
active_datetime: '2021-05-15T19:56:09Z',
expiration_datetime: '2100-05-15T19:56:09Z',
+ is_active: true,
}];
SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
data: {
@@ -119,25 +120,79 @@ describe('useEnterpriseOffers', () => {
});
});
- it('should set canManageLearnerCredit to false if enterprise offer or subsidy does not have exactly 1 offer', async () => {
+ it('should set canManageLearnerCredit to false if active enterprise offer or subsidy not found', async () => {
const mockOffers = [{ subsidyUuid: 'offer-1' }, { subsidyUuid: 'offer-2' }];
const mockSubsidyServiceResponse = [
{
uuid: 'offer-1',
title: 'offer-name',
- active_datetime: '2021-05-15T19:56:09Z',
- expiration_datetime: '2100-05-15T19:56:09Z',
+ active_datetime: '2005-05-15T19:56:09Z',
+ expiration_datetime: '2006-05-15T19:56:09Z',
+ is_active: false,
},
{
uuid: 'offer-2',
+ title: 'offer-name-2',
+ active_datetime: '2006-05-15T19:56:09Z',
+ expiration_datetime: '2007-05-15T19:56:09Z',
+ is_active: false,
+ },
+ ];
+ const mockOfferData = [];
+
+ EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
+ data: {
+ results: mockOffers,
+ },
+ });
+ SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
+ data: {
+ results: mockSubsidyServiceResponse,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers({
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ }));
+
+ await waitForNextUpdate();
+
+ expect(SubsidyApiService.getSubsidyByCustomerUUID).toHaveBeenCalledWith(
+ TEST_ENTERPRISE_UUID,
+ { subsidyType: 'learner_credit' },
+ );
+ expect(result.current).toEqual({
+ offers: mockOfferData,
+ isLoading: false,
+ canManageLearnerCredit: false,
+ });
+ });
+
+ it('should return the active enterprise offer or subsidy when multiple available', async () => {
+ const mockOffers = [{ subsidyUuid: 'offer-1' }, { subsidyUuid: 'offer-2' }];
+ const mockSubsidyServiceResponse = [
+ {
+ uuid: 'offer-1',
+ title: 'offer-name',
+ active_datetime: '2005-05-15T19:56:09Z',
+ expiration_datetime: '2006-05-15T19:56:09Z',
+ is_active: false,
+ },
+ {
+ uuid: 'offer-2',
+ title: 'offer-name-2',
+ active_datetime: '2006-05-15T19:56:09Z',
+ expiration_datetime: '2099-05-15T19:56:09Z',
+ is_active: true,
},
];
const mockOfferData = [
{
- id: 'offer-1',
- name: 'offer-name',
- start: '2021-05-15T19:56:09Z',
- end: '2100-05-15T19:56:09Z',
+ id: 'offer-2',
+ name: 'offer-name-2',
+ start: '2006-05-15T19:56:09Z',
+ end: '2099-05-15T19:56:09Z',
isCurrent: true,
},
];
@@ -167,7 +222,7 @@ describe('useEnterpriseOffers', () => {
expect(result.current).toEqual({
offers: mockOfferData,
isLoading: false,
- canManageLearnerCredit: false,
+ canManageLearnerCredit: true,
});
});
});
From 8500f4dbf4ec55ccf4338c75cc92d23fd74d0fc9 Mon Sep 17 00:00:00 2001
From: irfanuddinahmad <34648393+irfanuddinahmad@users.noreply.github.com>
Date: Thu, 3 Aug 2023 14:17:49 +0500
Subject: [PATCH 013/124] feat: ENT-7309 Added budget category based page for
learner credit (#1003)
* feat: ENT-7309 Added budget category-based page for learner credit
Co-authored-by: IrfanUddinAhmad
Co-authored-by: zamanafzal
---
.../learner-credit-management/BudgetCard.jsx | 175 ++++++++++++++++++
.../LearnerCreditAllocationTable.jsx | 9 +
.../MultipleBudgetsPage.jsx | 84 +++++++++
.../MultipleBudgetsPicker.jsx | 38 ++++
.../learner-credit-management/data/hooks.js | 4 +
.../data/tests/hooks.test.js | 2 +
.../data/tests/utils.test.js | 2 +
.../learner-credit-management/data/utils.js | 6 +
.../learner-credit-management/index.js | 4 +-
.../tests/BudgetCard.test.jsx | 140 ++++++++++++++
.../LearnerCreditAllocationTable.test.jsx | 19 ++
.../tests/MultipleBudgetsPage.test.jsx | 47 +++++
src/utils.js | 7 +
13 files changed, 535 insertions(+), 2 deletions(-)
create mode 100644 src/components/learner-credit-management/BudgetCard.jsx
create mode 100644 src/components/learner-credit-management/MultipleBudgetsPage.jsx
create mode 100644 src/components/learner-credit-management/MultipleBudgetsPicker.jsx
create mode 100644 src/components/learner-credit-management/tests/BudgetCard.test.jsx
create mode 100644 src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
new file mode 100644
index 0000000000..138113f964
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -0,0 +1,175 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import dayjs from 'dayjs';
+import {
+ Card,
+ Button,
+ Stack,
+ Row,
+ Col,
+ Breadcrumb,
+} from '@edx/paragon';
+
+import { getCourseProductLineAbbreviation } from '../../utils';
+import { useOfferRedemptions, useOfferSummary } from './data/hooks';
+import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
+import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
+import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+
+const BudgetCard = ({
+ offer,
+ enterpriseUUID,
+ enterpriseSlug,
+}) => {
+ const {
+ start,
+ end,
+ } = offer;
+
+ const {
+ isLoading: isLoadingOfferSummary,
+ offerSummary,
+ } = useOfferSummary(enterpriseUUID, offer);
+
+ const {
+ isLoading: isLoadingOfferRedemptions,
+ offerRedemptions,
+ fetchOfferRedemptions,
+ } = useOfferRedemptions(enterpriseUUID, offer?.id);
+ const [detailPage, setDetailPage] = useState(false);
+ const [activeLabel, setActiveLabel] = useState('');
+ const links = [
+ { label: 'Budgets', url: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
+ ];
+ const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
+ const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
+ const navigateToBudgetRedemptions = (budgetType) => {
+ setDetailPage(true);
+ links.push({ label: budgetType, url: `/${enterpriseSlug}/admin/learner-credit` });
+ setActiveLabel(budgetType);
+ };
+
+ const renderActions = (budgetType) => (
+ navigateToBudgetRedemptions(budgetType)}
+ >
+ View Budget
+
+ );
+
+ const renderCardHeader = (budgetType) => {
+ const subtitle = (
+
+
+ {formattedStartDate} - {formattedExpirationDate}
+
+
+ );
+
+ return (
+
+ {renderActions(budgetType)}
+
+ )}
+ />
+ );
+ };
+
+ const renderCardSection = (available, spent) => (
+
+
+
+ Available
+ {available}
+
+
+ Spent
+ {spent}
+
+
+
+ );
+
+ const renderCardAggregate = () => (
+
+
+
+ );
+
+ return (
+
+
+
+
+
+
+ {!detailPage
+ ? (
+ <>
+ {renderCardAggregate()}
+ Budgets
+
+
+
+ {renderCardHeader('Open Courses Marketplace')}
+ {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFundsOcm)}
+
+
+
+
+
+
+ {renderCardHeader('Executive Education')}
+ {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFundsExecEd)}
+
+
+
+ >
+ )
+ : (
+
+ )}
+
+ );
+};
+
+BudgetCard.propTypes = {
+ offer: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ start: PropTypes.string.isRequired,
+ end: PropTypes.string.isRequired,
+ }).isRequired,
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+};
+
+export default BudgetCard;
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index ab01eb3d40..e463487937 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -17,8 +17,11 @@ const LearnerCreditAllocationTable = ({
tableData,
fetchTableData,
enterpriseUUID,
+ budgetType,
}) => {
const isDesktopTable = useMediaQuery({ minWidth: breakpoints.extraLarge.minWidth });
+ const defaultFilter = budgetType ? [{ id: 'courseProductLine', value: budgetType }] : [];
+
return (
getCourseProductLineText(row.values.courseProductLine),
+ disableFilters: true,
},
]}
initialTableOptions={{
@@ -68,6 +72,7 @@ const LearnerCreditAllocationTable = ({
sortBy: [
{ id: 'enrollmentDate', desc: true },
],
+ filters: defaultFilter,
}}
fetchData={fetchTableData}
data={tableData.results}
@@ -85,6 +90,9 @@ const LearnerCreditAllocationTable = ({
/>
);
};
+LearnerCreditAllocationTable.defaultProps = {
+ budgetType: null,
+};
LearnerCreditAllocationTable.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
@@ -101,6 +109,7 @@ LearnerCreditAllocationTable.propTypes = {
pageCount: PropTypes.number.isRequired,
}).isRequired,
fetchTableData: PropTypes.func.isRequired,
+ budgetType: PropTypes.string,
};
export default LearnerCreditAllocationTable;
diff --git a/src/components/learner-credit-management/MultipleBudgetsPage.jsx b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
new file mode 100644
index 0000000000..cec507a231
--- /dev/null
+++ b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
@@ -0,0 +1,84 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Stack,
+ Row,
+ Col,
+ Card,
+ Hyperlink,
+} from '@edx/paragon';
+import { connect } from 'react-redux';
+import { Helmet } from 'react-helmet';
+import Hero from '../Hero';
+
+import LoadingMessage from '../LoadingMessage';
+import MultipleBudgetsPicker from './MultipleBudgetsPicker';
+import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
+
+import { configuration } from '../../config';
+
+const PAGE_TITLE = 'Learner Credit';
+
+const MultipleBudgetsPage = ({
+ enterpriseUUID,
+ enterpriseSlug,
+}) => {
+ const { offers, isLoading } = useContext(EnterpriseSubsidiesContext);
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (offers.length === 0) {
+ return (
+
+
+
+
+
+
+
+ No budgets for your organization
+
+ We were unable to find any budgets for your organization. Please contact
+ Customer Support if you have questions.
+
+
+ Contact support
+
+
+
+
+
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseUUID: state.portalConfiguration.enterpriseId,
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+MultipleBudgetsPage.propTypes = {
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+};
+
+export default connect(mapStateToProps)(MultipleBudgetsPage);
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
new file mode 100644
index 0000000000..a7b4bc814e
--- /dev/null
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Stack,
+ Row,
+ Col,
+} from '@edx/paragon';
+
+import BudgetCard from './BudgetCard';
+
+const MultipleBudgetsPicker = ({
+ offers,
+ enterpriseUUID,
+ enterpriseSlug,
+}) => (
+
+
+
+ {offers.map(offer => (
+
+ ))}
+
+
+
+);
+
+MultipleBudgetsPicker.propTypes = {
+ offers: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+};
+
+export default MultipleBudgetsPicker;
diff --git a/src/components/learner-credit-management/data/hooks.js b/src/components/learner-credit-management/data/hooks.js
index 45b3d73934..7ff74aaa69 100644
--- a/src/components/learner-credit-management/data/hooks.js
+++ b/src/components/learner-credit-management/data/hooks.js
@@ -65,12 +65,16 @@ const applySortByToOptions = (sortBy, options) => {
const applyFiltersToOptions = (filters, options) => {
const userSearchQuery = filters?.find(filter => filter.id === 'userEmail')?.value;
const courseTitleSearchQuery = filters?.find(filter => filter.id === 'courseTitle')?.value;
+ const courseProductLineSearchQuery = filters?.find(filter => filter.id === 'courseProductLine')?.value;
if (userSearchQuery) {
Object.assign(options, { search: userSearchQuery });
}
if (courseTitleSearchQuery) {
Object.assign(options, { searchCourse: courseTitleSearchQuery });
}
+ if (courseProductLineSearchQuery) {
+ Object.assign(options, { courseProductLine: courseProductLineSearchQuery });
+ }
};
export const useOfferRedemptions = (enterpriseUUID, offerId) => {
diff --git a/src/components/learner-credit-management/data/tests/hooks.test.js b/src/components/learner-credit-management/data/tests/hooks.test.js
index b6bf511d57..fa03c60f9e 100644
--- a/src/components/learner-credit-management/data/tests/hooks.test.js
+++ b/src/components/learner-credit-management/data/tests/hooks.test.js
@@ -68,6 +68,8 @@ describe('useOfferSummary', () => {
const expectedResult = {
totalFunds: 5000,
redeemedFunds: 200,
+ redeemedFundsExecEd: NaN,
+ redeemedFundsOcm: NaN,
remainingFunds: 4800,
percentUtilized: 0.04,
};
diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js
index 2b82930015..88773efbcc 100644
--- a/src/components/learner-credit-management/data/tests/utils.test.js
+++ b/src/components/learner-credit-management/data/tests/utils.test.js
@@ -16,6 +16,8 @@ describe('transformOfferSummary', () => {
expect(transformOfferSummary(offerSummary)).toEqual({
totalFunds: 1,
redeemedFunds: 1,
+ redeemedFundsExecEd: NaN,
+ redeemedFundsOcm: NaN,
remainingFunds: 0.0,
percentUtilized: 1.0,
});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 7a9101ba16..22cff6cd3a 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -15,11 +15,15 @@ export const transformOfferSummary = (offerSummary) => {
const totalFunds = offerSummary.maxDiscount && parseFloat(offerSummary.maxDiscount);
let redeemedFunds = offerSummary.amountOfOfferSpent && parseFloat(offerSummary.amountOfOfferSpent);
+ let redeemedFundsOcm = offerSummary.amountOfferSpentOcm && parseFloat(offerSummary.amountOfferSpentOcm);
+ let redeemedFundsExecEd = offerSummary.amountOfferSpentExecEd && parseFloat(offerSummary.amountOfferSpentExecEd);
// cap redeemed funds at the maximum funds available (`maxDiscount`), if applicable, so we
// don't display redeemed funds > funds available.
if (totalFunds) {
redeemedFunds = Math.min(redeemedFunds, totalFunds);
+ redeemedFundsOcm = Math.min(redeemedFundsOcm, totalFunds);
+ redeemedFundsExecEd = Math.min(redeemedFundsExecEd, totalFunds);
}
let remainingFunds = offerSummary.remainingBalance && parseFloat(offerSummary.remainingBalance);
@@ -37,6 +41,8 @@ export const transformOfferSummary = (offerSummary) => {
return {
totalFunds,
redeemedFunds,
+ redeemedFundsOcm,
+ redeemedFundsExecEd,
remainingFunds,
percentUtilized,
};
diff --git a/src/components/learner-credit-management/index.js b/src/components/learner-credit-management/index.js
index e691d21a11..271f4453ed 100644
--- a/src/components/learner-credit-management/index.js
+++ b/src/components/learner-credit-management/index.js
@@ -1,3 +1,3 @@
-import LearnerCreditManagement from './LearnerCreditManagement';
+import MultipleBudgetsPage from './MultipleBudgetsPage';
-export default LearnerCreditManagement;
+export default MultipleBudgetsPage;
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
new file mode 100644
index 0000000000..afa6c60620
--- /dev/null
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -0,0 +1,140 @@
+/* eslint-disable react/prop-types */
+import React from 'react';
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import userEvent from '@testing-library/user-event';
+import configureMockStore from 'redux-mock-store';
+import dayjs from 'dayjs';
+import {
+ screen,
+ render,
+ waitFor,
+} from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import BudgetCard from '../BudgetCard';
+import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
+
+jest.mock('../data/hooks');
+useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: null,
+});
+useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+});
+
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseId = 'test-enterprise';
+const enterpriseUUID = '1234';
+const initialStore = {
+ portalConfiguration: {
+ enterpriseId,
+ },
+};
+const store = getMockStore({ ...initialStore });
+
+const mockEnterpriseOfferId = '123';
+const mockEnterpriseOfferEnrollmentId = 456;
+
+const mockOfferDisplayName = 'Test Enterprise Offer';
+const mockOfferSummary = {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+};
+
+const BudgetCardWrapper = ({ ...rest }) => (
+
+
+
+
+
+);
+
+describe(' ', () => {
+ describe('with enterprise offer', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('displays correctly', () => {
+ const mockOffer = {
+ id: mockEnterpriseOfferId,
+ name: mockOfferDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ };
+ const mockOfferRedemption = {
+ created: '2022-02-01',
+ enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
+ };
+ useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: mockOfferSummary,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ results: [mockOfferRedemption],
+ itemCount: 1,
+ pageCount: 1,
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+ render( );
+ expect(screen.getByText('Open Courses Marketplace'));
+ expect(screen.getByText('Executive Education'));
+ expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
+ const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('offer-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+ });
+
+ it('displays table on clicking view budget', async () => {
+ const mockOffer = {
+ id: mockEnterpriseOfferId,
+ name: mockOfferDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ };
+ useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: mockOfferSummary,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+ render( );
+ const elementsWithTestId = screen.getAllByTestId('view-budget');
+ const firstElementWithTestId = elementsWithTestId[0];
+ await waitFor(() => userEvent.click(firstElementWithTestId));
+ expect(screen.getByText('Filters'));
+ expect(screen.getByText('No results found'));
+ });
+ });
+});
diff --git a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
index 00ebf8f3fc..fb79748c70 100644
--- a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
+++ b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
@@ -18,6 +18,7 @@ describe(' ', () => {
const props = {
enterpriseUUID: 'test-enterprise-id',
isLoading: false,
+ budgetType: 'OCM',
tableData: {
results: [{
userEmail: 'test@example.com',
@@ -47,4 +48,22 @@ describe(' ', () => {
}));
expect(screen.getByText('February', { exact: false }));
});
+ it('renders with empty table data', () => {
+ const props = {
+ enterpriseUUID: 'test-enterprise-id',
+ isLoading: false,
+ budgetType: 'OCM',
+ tableData: {
+ results: [],
+ itemCount: 0,
+ pageCount: 0,
+ },
+ fetchTableData: jest.fn(),
+ };
+ props.fetchTableData.mockReturnValue(props.tableData);
+
+ render( );
+
+ expect(screen.getByText('No results found', { exact: false }));
+ });
});
diff --git a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
new file mode 100644
index 0000000000..8b22a9d4ec
--- /dev/null
+++ b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
@@ -0,0 +1,47 @@
+/* eslint-disable react/prop-types */
+import React from 'react';
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import configureMockStore from 'redux-mock-store';
+import {
+ screen,
+ render,
+} from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
+import MultipleBudgetsPage from '../MultipleBudgetsPage';
+
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseId = 'test-enterprise';
+const initialStore = {
+ portalConfiguration: {
+ enterpriseId,
+ },
+};
+const store = getMockStore({ ...initialStore });
+const enterpriseUUID = '1234';
+
+const defaultEnterpriseSubsidiesContextValue = {
+ offers: [],
+};
+
+const MultipleBudgetsPageWrapper = ({
+ enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
+ ...rest
+}) => (
+
+
+
+
+
+);
+
+describe(' ', () => {
+ it('No budgets for your organization', () => {
+ render( );
+ expect(screen.getByText('No budgets for your organization'));
+ expect(screen.getByText('Contact support'));
+ });
+});
diff --git a/src/utils.js b/src/utils.js
index 44fe64bc94..9582955d3c 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -406,6 +406,12 @@ const getCourseProductLineText = (courseProductLine) => {
return courseProductLineText;
};
+const getCourseProductLineAbbreviation = (courseProductLine) => {
+ let courseProductLineText = '';
+ courseProductLineText = courseProductLine === 'Open Courses Marketplace' ? 'OCM' : 'Executive Education';
+ return courseProductLineText;
+};
+
export {
camelCaseDict,
camelCaseDictArray,
@@ -440,4 +446,5 @@ export {
pollAsync,
isNotValidNumberString,
getCourseProductLineText,
+ getCourseProductLineAbbreviation,
};
From cd82047106d1076944a0ddb898ca41b8a8c90cfd Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Wed, 9 Aug 2023 16:16:30 -0700
Subject: [PATCH 014/124] chore: replace font-awesome with paragon icons
(#1017)
* chore: replace font-awesome with paragon icons
---
package-lock.json | 33 +-
package.json | 5 -
src/components/Admin/AdminCards.jsx | 13 +-
src/components/Admin/AdminSearchForm.jsx | 4 +-
.../Admin/__snapshots__/Admin.test.jsx.snap | 2448 +++++++++++++----
src/components/Admin/index.jsx | 6 +-
.../CodeAssignmentModal/constants.jsx | 4 +-
src/components/CodeAssignmentModal/index.jsx | 4 +-
.../CodeManagement/ManageCodesTab.jsx | 8 +-
.../tests/ManageCodesTab.test.jsx | 2 +-
.../ManageCodesTab.test.jsx.snap | 248 +-
src/components/CodeReminderModal/index.jsx | 4 +-
src/components/CodeRevokeModal/index.jsx | 8 +-
.../CodeSearchResultsHeading.jsx | 3 +-
.../CodeSearchResultsTable.jsx | 3 +-
.../CodeSearchResults.test.jsx.snap | 166 +-
src/components/Coupon/_Coupon.scss | 1 +
.../Coupon/__snapshots__/Coupon.test.jsx.snap | 112 +-
src/components/Coupon/index.jsx | 9 +-
.../CouponDetails/_CouponDetails.scss | 17 -
src/components/CouponDetails/index.jsx | 45 +-
src/components/DownloadCsvButton/index.jsx | 6 +-
src/components/FileInput/index.jsx | 3 +-
.../IconWithTooltip/IconWithTooltip.test.jsx | 8 +-
src/components/IconWithTooltip/index.jsx | 9 +-
src/components/InviteLearnersModal/index.jsx | 4 +-
.../MultipleFileInputField.jsx | 4 +-
src/components/NumberCard/NumberCard.test.jsx | 6 +-
.../__snapshots__/NumberCard.test.jsx.snap | 72 +-
src/components/NumberCard/index.jsx | 23 +-
.../ReduxFormCheckbox/CheckboxWithTooltip.jsx | 2 +-
.../ReportingConfig/ReportingConfigForm.jsx | 13 +-
src/components/ReportingConfig/index.jsx | 15 +-
.../RequestCodesPage/RequestCodesForm.jsx | 4 +-
src/components/SaveTemplateButton/index.jsx | 7 +-
src/components/Sidebar/IconLink.test.jsx | 8 +-
src/components/Sidebar/index.jsx | 23 +-
.../subscriptions/SubscriptionDetails.jsx | 7 +-
.../CouponDetails/CouponDetails.test.jsx | 6 +-
.../__snapshots__/Sidebar.test.jsx.snap | 490 ++--
src/index.scss | 3 -
41 files changed, 2750 insertions(+), 1106 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 2df6b36851..ea06117091 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,11 +17,6 @@
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
- "@fortawesome/fontawesome-svg-core": "1.2.35",
- "@fortawesome/free-brands-svg-icons": "5.15.3",
- "@fortawesome/free-regular-svg-icons": "5.15.3",
- "@fortawesome/free-solid-svg-icons": "5.15.3",
- "@fortawesome/react-fontawesome": "0.1.14",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
@@ -3994,6 +3989,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
"hasInstallScript": true,
+ "peer": true,
"engines": {
"node": ">=6"
}
@@ -4003,30 +3999,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz",
"integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==",
"hasInstallScript": true,
- "dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@fortawesome/free-brands-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.3.tgz",
- "integrity": "sha512-1hirPcbjj72ZJtFvdnXGPbAbpn3Ox6mH3g5STbANFp3vGSiE5u5ingAKV06mK6ZVqNYxUPlh4DlTnaIvLtF2kw==",
- "hasInstallScript": true,
- "dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.35"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@fortawesome/free-regular-svg-icons": {
- "version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz",
- "integrity": "sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ==",
- "hasInstallScript": true,
+ "peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.35"
},
@@ -4039,6 +4012,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz",
"integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==",
"hasInstallScript": true,
+ "peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.35"
},
@@ -4050,6 +4024,7 @@
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
"integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
+ "peer": true,
"dependencies": {
"prop-types": "^15.7.2"
},
diff --git a/package.json b/package.json
index 87ddae89d0..c358cbf26d 100644
--- a/package.json
+++ b/package.json
@@ -33,11 +33,6 @@
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
- "@fortawesome/fontawesome-svg-core": "1.2.35",
- "@fortawesome/free-brands-svg-icons": "5.15.3",
- "@fortawesome/free-regular-svg-icons": "5.15.3",
- "@fortawesome/free-solid-svg-icons": "5.15.3",
- "@fortawesome/react-fontawesome": "0.1.14",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
diff --git a/src/components/Admin/AdminCards.jsx b/src/components/Admin/AdminCards.jsx
index d1afe4ac95..b367dff596 100644
--- a/src/components/Admin/AdminCards.jsx
+++ b/src/components/Admin/AdminCards.jsx
@@ -2,6 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import {
+ Award, Check, Groups, RemoveRedEye,
+} from '@edx/paragon/icons';
import NumberCard from '../NumberCard';
@@ -17,7 +20,7 @@ class AdminCards extends React.Component {
id: 'adminPortal.cards.registeredLearners',
defaultMessage: 'total number of learners registered',
}),
- iconClassName: 'fa fa-users',
+ icon: Groups,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.registeredUnenrolledLearners',
@@ -32,7 +35,7 @@ class AdminCards extends React.Component {
id: 'adminPortal.cards.enrolledOneCourse',
defaultMessage: 'learners enrolled in at least one course',
}),
- iconClassName: 'fa fa-check',
+ icon: Check,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.enrolledLearners',
@@ -53,7 +56,7 @@ class AdminCards extends React.Component {
id: 'adminPortal.cards.activeLearnersPastWeek',
defaultMessage: 'active learners in the past week',
}),
- iconClassName: 'fa fa-eye',
+ icon: RemoveRedEye,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.learnersActiveWeek',
@@ -77,7 +80,7 @@ class AdminCards extends React.Component {
courseCompletions: {
ref: React.createRef(),
description: 'course completions',
- iconClassName: 'fa fa-trophy',
+ icon: Award,
actions: [{
label: intl.formatMessage({
id: 'adminPortal.cards.completedLearners',
@@ -107,7 +110,7 @@ class AdminCards extends React.Component {
id={cardKey}
title={title}
description={card.description}
- iconClassName={card.iconClassName}
+ icon={card.icon}
detailActions={card.actions}
/>
diff --git a/src/components/Admin/AdminSearchForm.jsx b/src/components/Admin/AdminSearchForm.jsx
index 953929321e..3fcec0a454 100644
--- a/src/components/Admin/AdminSearchForm.jsx
+++ b/src/components/Admin/AdminSearchForm.jsx
@@ -2,9 +2,9 @@
import React from 'react';
import dayjs from 'dayjs';
import PropTypes from 'prop-types';
-import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { Form } from '@edx/paragon';
+import { Info } from '@edx/paragon/icons';
import SearchBar from '../SearchBar';
import { updateUrl } from '../../utils';
@@ -80,7 +80,7 @@ class AdminSearchForm extends React.Component {
Filter by start date
diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
index 13fb8a3fa2..fd763719cf 100644
--- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
@@ -219,10 +219,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+
+
+ Show details
+
@@ -312,10 +340,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+
+
+ Show details
+
@@ -421,10 +477,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+
+
+ Show details
+
@@ -546,10 +630,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # cou
-
- Show details
+
+
+
+
+ Show details
+
@@ -682,10 +794,24 @@ exports[` renders correctly with dashboard analytics data renders # cou
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -724,11 +850,19 @@ exports[` renders correctly with dashboard analytics data renders # cou
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -798,10 +932,24 @@ exports[` renders correctly with dashboard analytics data renders # of
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -891,10 +1053,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1000,10 +1190,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1125,10 +1343,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1261,10 +1507,24 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -1303,11 +1563,19 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -1377,10 +1645,24 @@ exports[` renders correctly with dashboard analytics data renders # of
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1470,10 +1766,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1579,10 +1903,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1704,10 +2056,24 @@ exports[` renders correctly with dashboard analytics data renders # of
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders # of
-
- Show details
+
+
+
+
+ Show details
+
@@ -1840,10 +2220,24 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -1886,11 +2280,19 @@ exports[` renders correctly with dashboard analytics data renders # of
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -1960,10 +2362,24 @@ exports[` renders correctly with dashboard analytics data renders colla
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+
+
+ Show details
+
@@ -2053,10 +2483,24 @@ exports[` renders correctly with dashboard analytics data renders colla
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+
+
+ Show details
+
@@ -2162,10 +2620,24 @@ exports[` renders correctly with dashboard analytics data renders colla
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+
+
+ Show details
+
@@ -2287,10 +2773,24 @@ exports[` renders correctly with dashboard analytics data renders colla
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders colla
-
- Show details
+
+
+
+
+ Show details
+
@@ -2451,11 +2965,19 @@ exports[` renders correctly with dashboard analytics data renders colla
onClick={[Function]}
type="button"
>
-
+
+
+
Download full report (CSV)
@@ -2477,7 +2999,7 @@ exports[` renders correctly with dashboard analytics data renders colla
>
Filter by course
@@ -2486,7 +3008,7 @@ exports[` renders correctly with dashboard analytics data renders colla
>
renders correctly with dashboard analytics data renders colla
>
Filter by start date
-
-
-
+
+
+
+
renders correctly with dashboard analytics data renders colla
renders correctly with dashboard analytics data renders colla
>
renders correctly with dashboard analytics data renders colla
className="form-control"
data-hj-suppress={true}
disabled={false}
- id="pgn-searchfield-input-106"
+ id="pgn-searchfield-input-8"
name="searchfield-input"
onBlur={[Function]}
onChange={[Function]}
@@ -2698,10 +3222,24 @@ exports[` renders correctly with dashboard analytics data renders full
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders full
-
- Show details
+
+
+
+
+ Show details
+
@@ -2791,10 +3343,24 @@ exports[` renders correctly with dashboard analytics data renders full
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders full
-
- Show details
+
+
+
+
+ Show details
+
@@ -2900,10 +3480,24 @@ exports[` renders correctly with dashboard analytics data renders full
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders full
-
- Show details
+
+
+
+
+ Show details
+
@@ -3025,10 +3633,24 @@ exports[` renders correctly with dashboard analytics data renders full
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders full
-
- Show details
+
+
+
+
+ Show details
+
@@ -3189,11 +3825,19 @@ exports[` renders correctly with dashboard analytics data renders full
onClick={[Function]}
type="button"
>
-
+
+
+
Download full report (CSV)
@@ -3215,7 +3859,7 @@ exports[` renders correctly with dashboard analytics data renders full
>
Filter by course
@@ -3224,7 +3868,7 @@ exports[` renders correctly with dashboard analytics data renders full
>
renders correctly with dashboard analytics data renders full
>
Filter by start date
-
-
-
+
+
+
+
renders correctly with dashboard analytics data renders full
renders correctly with dashboard analytics data renders full
>
renders correctly with dashboard analytics data renders full
className="form-control"
data-hj-suppress={true}
disabled={false}
- id="pgn-searchfield-input-12"
+ id="pgn-searchfield-input-3"
name="searchfield-input"
onBlur={[Function]}
onChange={[Function]}
@@ -3436,10 +4082,24 @@ exports[` renders correctly with dashboard analytics data renders inact
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -3529,10 +4203,24 @@ exports[` renders correctly with dashboard analytics data renders inact
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -3638,10 +4340,24 @@ exports[` renders correctly with dashboard analytics data renders inact
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -3763,10 +4493,24 @@ exports[` renders correctly with dashboard analytics data renders inact
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -3899,10 +4657,24 @@ exports[` renders correctly with dashboard analytics data renders inact
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -3945,11 +4717,19 @@ exports[` renders correctly with dashboard analytics data renders inact
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -4019,10 +4799,24 @@ exports[` renders correctly with dashboard analytics data renders inact
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -4112,10 +4920,24 @@ exports[` renders correctly with dashboard analytics data renders inact
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -4221,10 +5057,24 @@ exports[` renders correctly with dashboard analytics data renders inact
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -4346,10 +5210,24 @@ exports[` renders correctly with dashboard analytics data renders inact
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders inact
-
- Show details
+
+
+
+
+ Show details
+
@@ -4482,10 +5374,24 @@ exports[` renders correctly with dashboard analytics data renders inact
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -4528,11 +5434,19 @@ exports[` renders correctly with dashboard analytics data renders inact
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -4602,10 +5516,24 @@ exports[` renders correctly with dashboard analytics data renders learn
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders learn
-
- Show details
+
+
+
+
+ Show details
+
@@ -4695,10 +5637,24 @@ exports[` renders correctly with dashboard analytics data renders learn
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders learn
-
- Show details
+
+
+
+
+ Show details
+
@@ -4804,10 +5774,24 @@ exports[` renders correctly with dashboard analytics data renders learn
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders learn
-
- Show details
+
+
+
+
+ Show details
+
@@ -4929,10 +5927,24 @@ exports[` renders correctly with dashboard analytics data renders learn
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders learn
-
- Show details
+
+
+
+
+ Show details
+
@@ -5065,10 +6091,24 @@ exports[` renders correctly with dashboard analytics data renders learn
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -5111,11 +6151,19 @@ exports[` renders correctly with dashboard analytics data renders learn
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -5185,10 +6233,24 @@ exports[` renders correctly with dashboard analytics data renders regis
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders regis
-
- Show details
+
+
+
+
+ Show details
+
@@ -5278,10 +6354,24 @@ exports[` renders correctly with dashboard analytics data renders regis
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders regis
-
- Show details
+
+
+
+
+ Show details
+
@@ -5387,10 +6491,24 @@ exports[` renders correctly with dashboard analytics data renders regis
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders regis
-
- Show details
+
+
+
+
+ Show details
+
@@ -5512,10 +6644,24 @@ exports[` renders correctly with dashboard analytics data renders regis
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders regis
-
- Show details
+
+
+
+
+ Show details
+
@@ -5648,10 +6808,24 @@ exports[` renders correctly with dashboard analytics data renders regis
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -5690,11 +6864,19 @@ exports[` renders correctly with dashboard analytics data renders regis
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
@@ -5764,10 +6946,24 @@ exports[` renders correctly with dashboard analytics data renders top a
3
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders top a
-
- Show details
+
+
+
+
+ Show details
+
@@ -5857,10 +7067,24 @@ exports[` renders correctly with dashboard analytics data renders top a
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders top a
-
- Show details
+
+
+
+
+ Show details
+
@@ -5966,10 +7204,24 @@ exports[` renders correctly with dashboard analytics data renders top a
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders top a
-
- Show details
+
+
+
+
+ Show details
+
@@ -6091,10 +7357,24 @@ exports[` renders correctly with dashboard analytics data renders top a
1
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with dashboard analytics data renders top a
-
- Show details
+
+
+
+
+ Show details
+
@@ -6227,10 +7521,24 @@ exports[` renders correctly with dashboard analytics data renders top a
onClick={[Function]}
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Reset to
Full Report
@@ -6273,11 +7581,19 @@ exports[` renders correctly with dashboard analytics data renders top a
onClick={[Function]}
type="button"
>
-
+
+
+
Download current report (CSV)
diff --git a/src/components/Admin/index.jsx b/src/components/Admin/index.jsx
index 4f95e3e94a..45db4b5059 100644
--- a/src/components/Admin/index.jsx
+++ b/src/components/Admin/index.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { Alert, Icon } from '@edx/paragon';
-import { Error } from '@edx/paragon/icons';
+import { Error, Undo } from '@edx/paragon/icons';
import { Link } from 'react-router-dom';
import Hero from '../Hero';
@@ -225,7 +225,7 @@ class Admin extends React.Component {
return (
-
+
Reset to {this.getMetadataForAction().title}
);
@@ -242,7 +242,7 @@ class Admin extends React.Component {
const resetLink = resetQuery ? `${pathname}?${resetQuery}` : pathname;
return (
-
+
Reset Filters
);
diff --git a/src/components/CodeAssignmentModal/constants.jsx b/src/components/CodeAssignmentModal/constants.jsx
index 995adefa65..a1f427ebd1 100644
--- a/src/components/CodeAssignmentModal/constants.jsx
+++ b/src/components/CodeAssignmentModal/constants.jsx
@@ -1,4 +1,4 @@
-import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
+import { Info } from '@edx/paragon/icons';
import { MODAL_TYPES } from '../EmailTemplateForm/constants';
import { getTemplateEmailFields } from '../EmailTemplateForm';
import CheckboxWithTooltip from '../ReduxFormCheckbox/CheckboxWithTooltip';
@@ -20,7 +20,7 @@ export const getAssignmentModalFields = formatMessage => {
id: EMAIL_TEMPLATE_NUDGE_EMAIL_ID,
component: CheckboxWithTooltip,
className: 'auto-reminder-wrapper',
- icon: faInfoCircle,
+ icon: Info,
altText: formatMessage(messages.modalAltText),
tooltipText: formatMessage(messages.modalTooltipText),
label: formatMessage(messages.modalFieldLabel),
diff --git a/src/components/CodeAssignmentModal/index.jsx b/src/components/CodeAssignmentModal/index.jsx
index e492fef0b2..270411d242 100644
--- a/src/components/CodeAssignmentModal/index.jsx
+++ b/src/components/CodeAssignmentModal/index.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { reduxForm, SubmissionError } from 'redux-form';
import {
- Button, Icon, Modal, Form,
+ Button, Modal, Form, Spinner,
} from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -418,7 +418,7 @@ export class BaseCodeAssignmentModal extends React.Component {
data-testid={SUBMIT_BUTTON_TEST_ID}
>
<>
- {mode === MODAL_TYPES.assign && submitting && }
+ {mode === MODAL_TYPES.assign && submitting && }
{`Assign ${isBulkAssign ? 'Codes' : 'Code'}`}
>
,
diff --git a/src/components/CodeManagement/ManageCodesTab.jsx b/src/components/CodeManagement/ManageCodesTab.jsx
index 23f4dd7195..1e7e3cb61e 100644
--- a/src/components/CodeManagement/ManageCodesTab.jsx
+++ b/src/components/CodeManagement/ManageCodesTab.jsx
@@ -9,7 +9,9 @@ import {
Icon,
Pagination,
} from '@edx/paragon';
-import { CheckCircle, Info, WarningFilled } from '@edx/paragon/icons';
+import {
+ CheckCircle, Info, Plus, SpinnerIcon, WarningFilled,
+} from '@edx/paragon/icons';
import SearchBar from '../SearchBar';
import CodeSearchResults from '../CodeSearchResults';
@@ -277,7 +279,7 @@ class ManageCodesTab extends React.Component {
disabled={loading}
>
<>
-
+
Refresh data
>
@@ -286,7 +288,7 @@ class ManageCodesTab extends React.Component {
to={`/${enterpriseSlug}/admin/${ROUTE_NAMES.codeManagement}/request-codes`}
>
<>
-
+
Request more codes
>
diff --git a/src/components/CodeManagement/tests/ManageCodesTab.test.jsx b/src/components/CodeManagement/tests/ManageCodesTab.test.jsx
index ea08c4cf96..2e8451de7a 100644
--- a/src/components/CodeManagement/tests/ManageCodesTab.test.jsx
+++ b/src/components/CodeManagement/tests/ManageCodesTab.test.jsx
@@ -268,7 +268,7 @@ describe('ManageCodesTabWrapper', () => {
const store = mockStore({ ...initialState });
const wrapper = mount( );
store.clearActions();
- wrapper.find('.fa-refresh').hostNodes().simulate('click');
+ wrapper.find('[data-testid="refresh-data"]').hostNodes().simulate('click');
expect(store.getActions().filter(action => action.type === COUPONS_REQUEST)).toHaveLength(1);
});
diff --git a/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap b/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap
index 7e568a0fdc..13de3d3b8a 100644
--- a/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap
+++ b/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap
@@ -91,10 +91,25 @@ Array [
type="button"
>
+ className="pgn__icon mr-2"
+ data-testid="refresh-data"
+ >
+
+
+
+
Refresh data
+ className="pgn__icon"
+ >
+
+
+
+
Request more codes
@@ -201,7 +230,7 @@ Array [
>
+ className="pgn__icon mr-2"
+ data-testid="refresh-data"
+ >
+
+
+
+
Refresh data
+ className="pgn__icon"
+ >
+
+
+
+
Request more codes
@@ -381,7 +439,7 @@ Array [
>
+ className="pgn__icon mr-2"
+ data-testid="refresh-data"
+ >
+
+
+
+
Refresh data
+ className="pgn__icon"
+ >
+
+
+
+
Request more codes
@@ -529,7 +616,7 @@ Array [
>
+ className="pgn__icon mr-2"
+ data-testid="refresh-data"
+ >
+
+
+
+
Refresh data
+ className="pgn__icon"
+ >
+
+
+
+
Request more codes
@@ -733,14 +849,28 @@ Array [
className="icons col-lg-1 order-first order-lg-last text-right pr-2 mt-1 m-lg-0"
>
-
- Open coupon details
+
+
+
+
+ Open coupon details
+
@@ -850,14 +980,28 @@ Array [
className="icons col-lg-1 order-first order-lg-last text-right pr-2 mt-1 m-lg-0"
>
-
- Open coupon details
+
+
+
+
+ Open coupon details
+
diff --git a/src/components/CodeReminderModal/index.jsx b/src/components/CodeReminderModal/index.jsx
index fdd22cb562..4e4a1d41bd 100644
--- a/src/components/CodeReminderModal/index.jsx
+++ b/src/components/CodeReminderModal/index.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { reduxForm, SubmissionError } from 'redux-form';
-import { Button, Icon, Modal } from '@edx/paragon';
+import { Button, Modal, Spinner } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import SaveTemplateButton from '../../containers/SaveTemplateButton';
@@ -229,7 +229,7 @@ export class BaseCodeReminderModal extends React.Component {
className="code-remind-save-btn"
onClick={handleSubmit(this.handleModalSubmit)}
>
- {mode === REMIND_MODE && submitting && }
+ {mode === REMIND_MODE && submitting && }
Remind
,
{
@@ -240,7 +240,7 @@ class CodeRevokeModal extends React.Component {
className="code-revoke-save-btn"
onClick={handleSubmit(this.handleModalSubmit)}
>
- {mode === MODAL_TYPES.revoke && submitting && }
+ {mode === MODAL_TYPES.revoke && submitting && }
Revoke
,
(
@@ -15,7 +16,7 @@ const CodeSearchResultsHeading = ({ searchQuery, onClose }) => (
className="close-search-results-btn"
onClick={onClose}
>
-
+
Close search results
diff --git a/src/components/CodeSearchResults/CodeSearchResultsTable.jsx b/src/components/CodeSearchResults/CodeSearchResultsTable.jsx
index e43469218d..0ec6e533fc 100644
--- a/src/components/CodeSearchResults/CodeSearchResultsTable.jsx
+++ b/src/components/CodeSearchResults/CodeSearchResultsTable.jsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import { withRouter } from 'react-router-dom';
import { Icon } from '@edx/paragon';
+import { Check } from '@edx/paragon/icons';
import { isValidEmail } from '../../utils';
import TableContainer from '../../containers/TableContainer';
@@ -112,7 +113,7 @@ const CodeSearchResultsTable = ({
couponName,
code,
isRedeemed: isRedeemed ? (
-
+
) : defaultEmptyValue,
courseTitle: courseTitle || defaultEmptyValue,
assignedTo: assignedTo || defaultEmptyValue,
diff --git a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
index 8395dc41f8..eb08be87a9 100644
--- a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
+++ b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
@@ -46,10 +46,24 @@ exports[` basic rendering should render empty table data 1`
type="button"
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Close search results
@@ -138,10 +152,24 @@ exports[` basic rendering should render error 1`] = `
type="button"
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Close search results
@@ -238,10 +266,24 @@ exports[` basic rendering should render loading 1`] = `
type="button"
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Close search results
@@ -595,10 +637,24 @@ exports[` basic rendering should render table data 1`] = `
type="button"
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Close search results
@@ -685,14 +741,28 @@ exports[` basic rendering should render table data 1`] = `
scope={null}
>
-
- has been redeemed
+
+
+
+
+ has been redeemed
+
basic rendering should render table data 1`] = `
scope={null}
>
-
- has been redeemed
+
+
+
+
+ has been redeemed
+
basic rendering should render table data when sea
type="button"
>
+ className="pgn__icon mr-2"
+ >
+
+
+
+
Close search results
diff --git a/src/components/Coupon/_Coupon.scss b/src/components/Coupon/_Coupon.scss
index aba1b3bac7..8ddb98f2bb 100644
--- a/src/components/Coupon/_Coupon.scss
+++ b/src/components/Coupon/_Coupon.scss
@@ -63,3 +63,4 @@
}
}
}
+
diff --git a/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap b/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap
index 5d160b5109..434813392e 100644
--- a/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap
+++ b/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap
@@ -98,24 +98,52 @@ exports[` renders correctly with error state 1`] = `
className="icons col-lg-1 order-first order-lg-last text-right pr-2 mt-1 m-lg-0"
>
-
- Coupon has an error.
+
+
+
+
+ Coupon has an error.
+
-
- Open coupon details
+
+
+
+
+ Open coupon details
+
@@ -228,14 +256,28 @@ exports[` renders correctly with max uses 1`] = `
className="icons col-lg-1 order-first order-lg-last text-right pr-2 mt-1 m-lg-0"
>
-
- Open coupon details
+
+
+
+
+ Open coupon details
+
@@ -343,14 +385,28 @@ exports[` renders correctly without max uses 1`] = `
className="icons col-lg-1 order-first order-lg-last text-right pr-2 mt-1 m-lg-0"
>
-
- Open coupon details
+
+
+
+
+ Open coupon details
+
diff --git a/src/components/Coupon/index.jsx b/src/components/Coupon/index.jsx
index 45777efbf2..eee7eb0048 100644
--- a/src/components/Coupon/index.jsx
+++ b/src/components/Coupon/index.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Icon } from '@edx/paragon';
+import { Error, ExpandLess, ExpandMore } from '@edx/paragon/icons';
import CouponDetails from '../../containers/CouponDetails';
@@ -83,12 +84,13 @@ class Coupon extends React.Component {
renderExpandCollapseIcon() {
const { isExpanded } = this.state;
- const iconClass = isExpanded ? 'fa-chevron-up' : 'fa-chevron-down';
+ const icon = isExpanded ? ExpandLess : ExpandMore;
const iconColor = isExpanded ? 'text-white' : 'text-primary';
const screenReaderText = isExpanded ? 'Close' : 'Open';
return (
);
@@ -97,7 +99,8 @@ class Coupon extends React.Component {
renderErrorIcon() {
return (
);
diff --git a/src/components/CouponDetails/_CouponDetails.scss b/src/components/CouponDetails/_CouponDetails.scss
index e0d34ef342..78dd08ffba 100644
--- a/src/components/CouponDetails/_CouponDetails.scss
+++ b/src/components/CouponDetails/_CouponDetails.scss
@@ -58,27 +58,10 @@
& + label {
width: 15px;
- &:before {
- font-family: FontAwesome;
- content: '\f096'; /* fa-square-o */
- font-weight: normal;
- font-size: 1rem;
- line-height: 1rem;
- }
- }
- &:checked + label:before {
- content: '\f046'; /* fa-check-square-o */
}
- &.mixed + label:before {
- content: '\f146'; /* fa-minus-square */
- }
- &:focus + label:before {
- border-radius: 2px;
- box-shadow: $input-btn-focus-box-shadow;
- }
}
}
}
diff --git a/src/components/CouponDetails/index.jsx b/src/components/CouponDetails/index.jsx
index fff0b0c661..5be165eea1 100644
--- a/src/components/CouponDetails/index.jsx
+++ b/src/components/CouponDetails/index.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
- Alert, Button, CheckBox, Icon,
+ Alert, Button, Icon, Form,
} from '@edx/paragon';
import { CheckCircle, Error } from '@edx/paragon/icons';
@@ -242,16 +242,14 @@ class CouponDetails extends React.Component {
getNewColumns(selectedToggle) {
const selectColumn = {
label: (
- {this.getSelectAllCheckBoxLabel()}
- }
- onChange={(checked) => {
- this.hasAllTableRowsSelected = checked;
- this.handleSelectAllCodes(checked);
+ onChange={(event) => {
+ this.hasAllTableRowsSelected = event.target.checked;
+ this.handleSelectAllCodes(event.target.checked);
}}
+ checked={this.hasAllTableRowsSelected}
ref={this.selectAllCheckBoxRef}
/>
),
@@ -298,20 +296,6 @@ class CouponDetails extends React.Component {
}));
}
- getSelectAllCheckBoxLabel = () => {
- if (this.hasAllTableRowsSelected) {
- return 'unselect all codes';
- }
- return 'select all codes';
- };
-
- getLabelForCodeCheckBox = (code) => {
- if (this.selectedTableRows[code]) {
- return `unselect code ${code}`;
- }
- return `select code ${code}`;
- };
-
reset() {
this.resetModals();
this.resetCodeActionStatus();
@@ -386,7 +370,7 @@ class CouponDetails extends React.Component {
// TODO: Paragon now has an IndeterminateCheckbox that can be used here.
const selectAllCheckBoxRef = selectColumn.label.ref && selectColumn.label.ref.current;
const selectAllCheckBoxDOM = (
- selectAllCheckBoxRef && document.getElementById(selectAllCheckBoxRef.props.id)
+ selectAllCheckBoxRef && document.getElementById(selectAllCheckBoxRef.props?.id)
);
if (selectAllCheckBoxDOM && hasPartialSelection) {
@@ -414,7 +398,7 @@ class CouponDetails extends React.Component {
code: {code.code} ,
assigned_to: code.error ? (
-
+
{code.error}
) : code.assigned_to,
@@ -430,16 +414,13 @@ class CouponDetails extends React.Component {
setModalState={this.setModalState}
/>,
select: (
- {this.getLabelForCodeCheckBox(code.code)}
- }
- onChange={(checked) => {
- this.handleCodeSelection({ checked, code });
- if (checked && !this.selectedTableRows[code.code]) {
+ onChange={(event) => {
+ this.handleCodeSelection({ checked: event.target.checked, code });
+ if (event.target.checked && !this.selectedTableRows[code.code]) {
this.selectedTableRows[code.code] = true;
- } else if (!checked && this.selectedTableRows[code.code]) {
+ } else if (!event.target.checked && this.selectedTableRows[code.code]) {
delete this.selectedTableRows[code.code];
}
}}
diff --git a/src/components/DownloadCsvButton/index.jsx b/src/components/DownloadCsvButton/index.jsx
index 702c37e7e8..caf3a856fd 100644
--- a/src/components/DownloadCsvButton/index.jsx
+++ b/src/components/DownloadCsvButton/index.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Button, Icon } from '@edx/paragon';
+import { Button, Spinner } from '@edx/paragon';
+import { FileDownload } from '@edx/paragon/icons';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
@@ -22,7 +23,6 @@ class DownloadCsvButton extends React.Component {
id,
} = this.props;
- const downloadButtonIconClasses = csvLoading ? ['fa-spinner', 'fa-spin'] : ['fa-download'];
return (
<>
-
+ {csvLoading ? : }
{buttonLabel || }
>
diff --git a/src/components/FileInput/index.jsx b/src/components/FileInput/index.jsx
index 641fa16c9c..cc6b78dfe7 100644
--- a/src/components/FileInput/index.jsx
+++ b/src/components/FileInput/index.jsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { clearFields } from 'redux-form';
import { Button, Icon, ValidationFormGroup } from '@edx/paragon';
+import { Close } from '@edx/paragon/icons';
class FileInput extends React.Component {
constructor(props) {
@@ -136,7 +137,7 @@ class FileInput extends React.Component {
this.setState({ fileName: null });
}}
>
-
+
Remove
)}
diff --git a/src/components/IconWithTooltip/IconWithTooltip.test.jsx b/src/components/IconWithTooltip/IconWithTooltip.test.jsx
index c682c796fd..d262934538 100644
--- a/src/components/IconWithTooltip/IconWithTooltip.test.jsx
+++ b/src/components/IconWithTooltip/IconWithTooltip.test.jsx
@@ -1,13 +1,13 @@
import React from 'react';
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
+import { Info } from '@edx/paragon/icons';
import IconWithTooltip from './index';
const defaultAltText = 'infoooo';
const defaultTooltipText = 'Tooool';
const DEFAULT_PROPS = {
- icon: faInfoCircle,
+ icon: Info,
altText: defaultAltText,
tooltipText: defaultTooltipText,
};
@@ -17,7 +17,7 @@ describe(' ', () => {
global.innerWidth = 800;
global.dispatchEvent(new Event('resize'));
const { container } = render( );
- expect(container.querySelector(`[data-icon=${faInfoCircle.iconName}]`)).toBeTruthy();
+ expect(container.querySelector('svg')).toBeTruthy();
});
[
{ windowSize: 800, expectedLocation: 'right' },
@@ -27,7 +27,7 @@ describe(' ', () => {
global.innerWidth = data.windowSize;
global.dispatchEvent(new Event('resize'));
const { container } = render( );
- const icon = container.querySelector(`[data-icon=${faInfoCircle.iconName}]`);
+ const icon = container.querySelector('svg');
expect(icon).toBeTruthy();
act(async () => {
diff --git a/src/components/IconWithTooltip/index.jsx b/src/components/IconWithTooltip/index.jsx
index 67c99fe4fb..4aa85455a8 100644
--- a/src/components/IconWithTooltip/index.jsx
+++ b/src/components/IconWithTooltip/index.jsx
@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { OverlayTrigger, Tooltip, useWindowSize } from '@edx/paragon';
+import {
+ Icon, OverlayTrigger, Tooltip, useWindowSize,
+} from '@edx/paragon';
const IconWithTooltip = ({
icon, altText, tooltipText, placementSm = 'right', placementLg = 'top', trigger = ['hover', 'focus'], breakpoint = 768, iconClassNames = 'ml-1',
@@ -19,13 +20,13 @@ const IconWithTooltip = ({
)}
>
-
+
);
};
IconWithTooltip.propTypes = {
- icon: PropTypes.shape({}).isRequired,
+ icon: PropTypes.func.isRequired,
altText: PropTypes.string.isRequired,
tooltipText: PropTypes.string.isRequired,
// These props have defaults above
diff --git a/src/components/InviteLearnersModal/index.jsx b/src/components/InviteLearnersModal/index.jsx
index 1def540adb..89ee1e62d6 100644
--- a/src/components/InviteLearnersModal/index.jsx
+++ b/src/components/InviteLearnersModal/index.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm, SubmissionError } from 'redux-form';
import {
- Button, Icon, Modal, Alert,
+ Button, Modal, Alert, Spinner,
} from '@edx/paragon';
import { Cancel as ErrorIcon } from '@edx/paragon/icons';
@@ -192,7 +192,7 @@ class InviteLearnersModal extends React.Component {
onClick={handleSubmit(this.handleModalSubmit)}
>
<>
- {submitting && }
+ {submitting && }
Invite learners
>
,
diff --git a/src/components/MultipleFileInputField/MultipleFileInputField.jsx b/src/components/MultipleFileInputField/MultipleFileInputField.jsx
index 86363bbaca..5e3419bfce 100644
--- a/src/components/MultipleFileInputField/MultipleFileInputField.jsx
+++ b/src/components/MultipleFileInputField/MultipleFileInputField.jsx
@@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import {
Form, FormControl, IconButton,
} from '@edx/paragon';
+import { Close } from '@edx/paragon/icons';
import classNames from 'classnames';
-import * as FontAwesome from '@fortawesome/free-solid-svg-icons/faTimes';
import { getSizeInBytes, formatBytes } from './utils';
import { MAX_FILES_SIZE, FILE_SIZE_EXCEEDS_ERROR } from './constants';
@@ -91,7 +91,7 @@ const MultipleFileInputField = ({
inputValues?.map((e, i) => (
{`${e.name} - ${formatBytes(e.size)}`}
- handleFileRemoveClick(i)} variant="danger" />
+ handleFileRemoveClick(i)} variant="danger" />
))
}
diff --git a/src/components/NumberCard/NumberCard.test.jsx b/src/components/NumberCard/NumberCard.test.jsx
index 042694244d..fff59a6746 100644
--- a/src/components/NumberCard/NumberCard.test.jsx
+++ b/src/components/NumberCard/NumberCard.test.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import renderer from 'react-test-renderer';
import { mount } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
+import { Groups } from '@edx/paragon/icons';
import NumberCard from './index';
@@ -14,7 +15,7 @@ const NumberCardWrapper = props => (
className="test-class"
title={10}
description="This describes the data!"
- iconClassName="fa fa-users"
+ icon={Groups}
{...props}
/>
@@ -134,10 +135,9 @@ describe(' ', () => {
}]}
/>
));
-
wrapper.setProps({ detailsExpanded: true });
const action = getNumberCard(wrapper).find('.footer-body .btn-link').hostNodes().first();
- expect(action.find('.fa-spinner').exists()).toBeTruthy();
+ expect(action.find('.ml-2').exists()).toBeTruthy();
});
});
});
diff --git a/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap b/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap
index 59b61ff6c0..415a2c1b3e 100644
--- a/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap
+++ b/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap
@@ -17,10 +17,24 @@ exports[` renders correctly with detail actions 1`] = `
10
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
renders correctly with detail actions 1`] = `
-
- Show details
+
+
+
+
+ Show details
+
@@ -125,10 +153,24 @@ exports[` renders correctly without detail actions 1`] = `
10
+ className="pgn__icon d-flex align-items-center justify-content-center"
+ >
+
+
+
+
{action.loading
- && }
+ && }
));
@@ -161,7 +162,7 @@ class NumberCard extends React.Component {
const {
className,
title,
- iconClassName,
+ icon,
description,
detailActions,
id,
@@ -180,14 +181,14 @@ class NumberCard extends React.Component {
{this.formatTitle(title)}
- {iconClassName && (
+ {icon && (
)}
@@ -212,13 +213,7 @@ class NumberCard extends React.Component {
@@ -246,7 +241,7 @@ class NumberCard extends React.Component {
NumberCard.defaultProps = {
className: null,
- iconClassName: null,
+ icon: null,
detailActions: null,
detailsExpanded: false,
};
@@ -256,7 +251,7 @@ NumberCard.propTypes = {
description: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
className: PropTypes.string,
- iconClassName: PropTypes.string,
+ icon: PropTypes.func,
detailActions: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
loading: PropTypes.bool,
diff --git a/src/components/ReduxFormCheckbox/CheckboxWithTooltip.jsx b/src/components/ReduxFormCheckbox/CheckboxWithTooltip.jsx
index be577a2cfa..1473d8a47c 100644
--- a/src/components/ReduxFormCheckbox/CheckboxWithTooltip.jsx
+++ b/src/components/ReduxFormCheckbox/CheckboxWithTooltip.jsx
@@ -26,7 +26,7 @@ CheckboxWithTooltip.defaultProps = {
CheckboxWithTooltip.propTypes = {
className: PropTypes.string,
// Icon should be a paragon icon
- icon: PropTypes.shape().isRequired,
+ icon: PropTypes.func.isRequired,
altText: PropTypes.string.isRequired,
tooltipText: PropTypes.string.isRequired,
};
diff --git a/src/components/ReportingConfig/ReportingConfigForm.jsx b/src/components/ReportingConfig/ReportingConfigForm.jsx
index 4e662c6a11..09dadbe4e5 100644
--- a/src/components/ReportingConfig/ReportingConfigForm.jsx
+++ b/src/components/ReportingConfig/ReportingConfigForm.jsx
@@ -3,8 +3,9 @@ import PropTypes from 'prop-types';
import isEmpty from 'lodash/isEmpty';
import omit from 'lodash/omit';
import {
- ValidationFormGroup, Input, StatefulButton, Icon, Button,
+ ValidationFormGroup, Input, StatefulButton, Icon, Button, Spinner,
} from '@edx/paragon';
+import { Check, Close, Download } from '@edx/paragon/icons';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import SFTPDeliveryMethodForm from './SFTPDeliveryMethodForm';
import EmailDeliveryMethodForm from './EmailDeliveryMethodForm';
@@ -419,10 +420,10 @@ class ReportingConfigForm extends React.Component {
error: 'Error',
}}
icons={{
- default: ,
- pending: ,
- complete: ,
- error: ,
+ default: ,
+ pending: ,
+ complete: ,
+ error: ,
}}
disabledStates={[SUBMIT_STATES.PENDING]}
className="ml-3 col"
@@ -434,7 +435,7 @@ class ReportingConfigForm extends React.Component {
className="btn-outline-danger mr-3"
onClick={() => this.props.deleteConfig(config.uuid)}
>
- Delete
+ Delete
)}
diff --git a/src/components/ReportingConfig/index.jsx b/src/components/ReportingConfig/index.jsx
index e34c1f17e3..f77748e903 100644
--- a/src/components/ReportingConfig/index.jsx
+++ b/src/components/ReportingConfig/index.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Collapsible, Icon } from '@edx/paragon';
+import { Check, Close } from '@edx/paragon/icons';
import { camelCaseObject } from '@edx/frontend-platform';
import EnterpriseCatalogApiService from '../../data/services/EnterpriseCatalogApiService';
import LMSApiService from '../../data/services/LmsApiService';
@@ -142,9 +143,17 @@ class ReportingConfig extends React.Component {
className="shadow"
title={(
-
+ {config.active ? (
+
+ ) : (
+
+ )}
Report Type:
{config.data_type}
diff --git a/src/components/RequestCodesPage/RequestCodesForm.jsx b/src/components/RequestCodesPage/RequestCodesForm.jsx
index 50fdb226ce..fd46d99187 100644
--- a/src/components/RequestCodesPage/RequestCodesForm.jsx
+++ b/src/components/RequestCodesPage/RequestCodesForm.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { Link, Redirect } from 'react-router-dom';
-import { Alert, Button, Icon } from '@edx/paragon';
+import { Alert, Button, Spinner } from '@edx/paragon';
import RenderField from '../RenderField';
@@ -120,7 +120,7 @@ class RequestCodesForm extends React.Component {
className="btn-primary"
>
<>
- {submitting &&
}
+ {submitting &&
}
Request Codes
>
diff --git a/src/components/SaveTemplateButton/index.jsx b/src/components/SaveTemplateButton/index.jsx
index afe95f763c..efcf854b81 100644
--- a/src/components/SaveTemplateButton/index.jsx
+++ b/src/components/SaveTemplateButton/index.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { StatefulButton, Icon } from '@edx/paragon';
+import { StatefulButton, Icon, Spinner } from '@edx/paragon';
+import { CheckCircle } from '@edx/paragon/icons';
import { SubmissionError } from 'redux-form';
import { validateEmailTemplateFields } from '../../data/validation/email';
@@ -149,8 +150,8 @@ class SaveTemplateButton extends React.Component {
complete: 'Template Saved',
}}
icons={{
- pending:
,
- complete:
,
+ pending:
,
+ complete:
,
}}
disabledStates={[SUBMIT_STATES.PENDING]}
disabled={disabled}
diff --git a/src/components/Sidebar/IconLink.test.jsx b/src/components/Sidebar/IconLink.test.jsx
index 5108123617..e34f9aaf2a 100644
--- a/src/components/Sidebar/IconLink.test.jsx
+++ b/src/components/Sidebar/IconLink.test.jsx
@@ -3,8 +3,8 @@ import {
} from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faUniversity } from '@fortawesome/free-solid-svg-icons';
+import { Icon } from '@edx/paragon';
+import { School } from '@edx/paragon/icons';
import { MemoryRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
@@ -28,7 +28,7 @@ describe('
', () => {
const defaultProps = {
title: 'Internal Route',
to: 'admin/test',
- icon:
,
+ icon:
,
};
render(
);
expect(screen.getByText('Internal Route').closest('a')).toHaveAttribute('href', '/admin/test');
@@ -38,7 +38,7 @@ describe('
', () => {
const defaultProps = {
title: 'External Route',
to: 'http://helpcenter.edx.org/us',
- icon:
,
+ icon:
,
external: true,
};
diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx
index f0515898c5..5a78bf11e8 100644
--- a/src/components/Sidebar/index.jsx
+++ b/src/components/Sidebar/index.jsx
@@ -3,13 +3,10 @@ import React, {
} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import { faFile, faLifeRing } from '@fortawesome/free-regular-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import {
- faCreditCard, faTags, faChartLine, faChartBar, faCog,
-} from '@fortawesome/free-solid-svg-icons';
import { Icon } from '@edx/paragon';
-import { MoneyOutline, BookOpen } from '@edx/paragon/icons';
+import {
+ BookOpen, CreditCard, Description, InsertChartOutlined, MoneyOutline, Settings, Support, Tag, TrendingUp,
+} from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import IconLink from './IconLink';
@@ -73,25 +70,25 @@ const Sidebar = ({
{
title: 'Learner Progress Report',
to: `${baseUrl}/admin/${ROUTE_NAMES.learners}`,
- icon:
,
+ icon:
,
},
{
title: 'Analytics',
to: `${baseUrl}/admin/${ROUTE_NAMES.analytics}`,
- icon:
,
+ icon:
,
hidden: !features.ANALYTICS || !enableAnalyticsScreen,
},
{
title: 'Code Management',
to: `${baseUrl}/admin/${ROUTE_NAMES.codeManagement}`,
- icon:
,
+ icon:
,
hidden: !features.CODE_MANAGEMENT || !enableCodeManagementScreen,
notification: !!subsidyRequestsCounts.couponCodes,
},
{
title: 'Subscription Management',
to: `${baseUrl}/admin/${ROUTE_NAMES.subscriptionManagement}`,
- icon:
,
+ icon:
,
hidden: !enableSubscriptionManagementScreen,
notification: !!subsidyRequestsCounts.subscriptionLicenses,
},
@@ -112,20 +109,20 @@ const Sidebar = ({
{
title: 'Reporting Configurations',
to: `${baseUrl}/admin/${ROUTE_NAMES.reporting}`,
- icon:
,
+ icon:
,
hidden: !features.REPORTING_CONFIGURATIONS || !enableReportingConfigScreen,
},
{
title: 'Settings',
id: TOUR_TARGETS.SETTINGS_SIDEBAR,
to: `${baseUrl}/admin/${ROUTE_NAMES.settings}`,
- icon:
,
+ icon:
,
},
// NOTE: keep "Support" link the last nav item
{
title: 'Support',
to: configuration.ENTERPRISE_SUPPORT_URL,
- icon:
,
+ icon:
,
hidden: !features.SUPPORT,
external: true,
},
diff --git a/src/components/subscriptions/SubscriptionDetails.jsx b/src/components/subscriptions/SubscriptionDetails.jsx
index 3ef662d0c2..a41fd63e2d 100644
--- a/src/components/subscriptions/SubscriptionDetails.jsx
+++ b/src/components/subscriptions/SubscriptionDetails.jsx
@@ -3,11 +3,10 @@ import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import dayjs from 'dayjs';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faAngleLeft } from '@fortawesome/free-solid-svg-icons';
import {
- Button, Row, Col, Toast,
+ Button, Row, Col, Toast, Icon,
} from '@edx/paragon';
+import { ArrowBackIos } from '@edx/paragon/icons';
import { SubscriptionDetailContext } from './SubscriptionDetailContextProvider';
import InviteLearnersButton from './buttons/InviteLearnersButton';
@@ -38,7 +37,7 @@ const SubscriptionDetails = ({ enterpriseSlug }) => {
-
+
Back to subscriptions
diff --git a/src/containers/CouponDetails/CouponDetails.test.jsx b/src/containers/CouponDetails/CouponDetails.test.jsx
index 0499565dbb..0c6038c94a 100644
--- a/src/containers/CouponDetails/CouponDetails.test.jsx
+++ b/src/containers/CouponDetails/CouponDetails.test.jsx
@@ -163,7 +163,7 @@ describe('CouponDetails container', () => {
const selectAllCodesOnPage = ({ isSelected, expectedSelectionLength }) => {
const selectAllCheckbox = wrapper.find('table th').find('input[type=\'checkbox\']');
- selectAllCheckbox.simulate('change', { target: { value: isSelected } });
+ selectAllCheckbox.simulate('change', { target: { checked: isSelected } });
expect(wrapper.find('CouponDetails').instance().state.selectedCodes).toHaveLength(expectedSelectionLength);
};
@@ -594,8 +594,8 @@ describe('CouponDetails container', () => {
it('handles individual code selection within table', () => {
const checkboxes = wrapper.find('table').find('input[type=\'checkbox\']').slice(1);
- checkboxes.first().simulate('change', { target: { value: true } });
- checkboxes.last().simulate('change', { target: { value: true } });
+ checkboxes.first().simulate('change', { target: { checked: true } });
+ checkboxes.last().simulate('change', { target: { checked: true } });
expect(wrapper.find('CouponDetails').instance().state.selectedCodes).toHaveLength(2);
diff --git a/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap b/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap
index 33e4cd3f06..49c9024f3b 100644
--- a/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap
+++ b/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap
@@ -31,23 +31,26 @@ exports[` renders correctly 1`] = `
-
-
-
+
+
+
+
renders correctly 1`] = `
-
-
-
+
+
+
+
renders correctly 1`] = `
-
-
-
+
+
+
+
renders correctly 1`] = `
-
-
-
+
+
+
+
renders correctly when code management is hidden 1`] = `
-
-
-
+
+
+
+
renders correctly when code management is hidden 1`] = `
-
-
-
+
+
+
+
renders correctly when expanded 1`] = `
-
-
-
+
+
+
+
Learner Progress Report
@@ -448,23 +469,26 @@ exports[` renders correctly when expanded 1`] = `
-
-
-
+
+
+
+
Code Management
@@ -487,23 +511,26 @@ exports[` renders correctly when expanded 1`] = `
-
-
-
+
+
+
+
Subscription Management
@@ -569,23 +596,26 @@ exports[` renders correctly when expanded 1`] = `
-
-
-
+
+
+
+
Settings
@@ -629,23 +659,26 @@ exports[` renders correctly when expanded by toggle 1`] = `
-
-
-
+
+
+
+
Learner Progress Report
@@ -668,23 +701,26 @@ exports[` renders correctly when expanded by toggle 1`] = `
-
-
-
+
+
+
+
Code Management
@@ -707,23 +743,26 @@ exports[` renders correctly when expanded by toggle 1`] = `
-
-
-
+
+
+
+
Subscription Management
@@ -789,23 +828,26 @@ exports[` renders correctly when expanded by toggle 1`] = `
-
-
-
+
+
+
+
Settings
diff --git a/src/index.scss b/src/index.scss
index e7f2a1fa57..34c7e99ba0 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -7,8 +7,6 @@ $modal-max-width: 650px;
@import "~@edx/frontend-enterprise-catalog-search";
@import "~@edx/brand/paragon/overrides";
-$fa-font-path: "~font-awesome/fonts";
-@import "~font-awesome/scss/font-awesome";
@import "./components/EnterpriseApp/EnterpriseApp";
@import "./components/CodeSearchResults/CodeSearchResults";
@@ -27,7 +25,6 @@ $fa-font-path: "~font-awesome/fonts";
@import "./components/Admin/Admin";
@import "./components/settings/settings";
-
body {
overflow-x: hidden;
}
From fc3a70fc07eca5dfd6166fd37181ac32b95c4daa Mon Sep 17 00:00:00 2001
From: irfanuddinahmad <34648393+irfanuddinahmad@users.noreply.github.com>
Date: Tue, 22 Aug 2023 13:56:28 +0500
Subject: [PATCH 015/124] feat: Added support for multiple active offers in
subsidies context (#1020)
Co-authored-by: IrfanUddinAhmad
---
.../EnterpriseSubsidiesContext/data/hooks.js | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index 123d022df1..d699098cd0 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -41,26 +41,26 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen,
let activeSubsidyFound = false;
if (results.length !== 0) {
let subsidy = results[0];
+ const offerData = [];
+ let activeSubsidyData = {};
for (let i = 0; i < results.length; i++) {
subsidy = results[i];
activeSubsidyFound = source === 'ecommerceApi'
? subsidy.isCurrent
: subsidy.isActive;
if (activeSubsidyFound === true) {
- break;
+ activeSubsidyData = {
+ id: subsidy.uuid || subsidy.id,
+ name: subsidy.title || subsidy.displayName,
+ start: subsidy.activeDatetime || subsidy.startDatetime,
+ end: subsidy.expirationDatetime || subsidy.endDatetime,
+ isCurrent: activeSubsidyFound,
+ };
+ offerData.push(activeSubsidyData);
+ setCanManageLearnerCredit(true);
}
}
- if (activeSubsidyFound === true) {
- const offerData = {
- id: subsidy.uuid || subsidy.id,
- name: subsidy.title || subsidy.displayName,
- start: subsidy.activeDatetime || subsidy.startDatetime,
- end: subsidy.expirationDatetime || subsidy.endDatetime,
- isCurrent: activeSubsidyFound,
- };
- setOffers([offerData]);
- setCanManageLearnerCredit(true);
- }
+ setOffers(offerData);
}
} catch (error) {
logError(error);
From 4266584cb92dfe79d42c84cfa433aa7d912300dc Mon Sep 17 00:00:00 2001
From: Zaman Afzal
Date: Wed, 23 Aug 2023 00:35:37 +0500
Subject: [PATCH 016/124] feat: Show exec-ed learner credit only for exec ed
offers (#1021)
---
.../learner-credit-management/BudgetCard.jsx | 24 ++++++----
.../data/constants.js | 2 +
.../data/tests/utils.test.js | 5 ++
.../learner-credit-management/data/utils.js | 2 +
.../tests/BudgetCard.test.jsx | 48 ++++++++++++++++++-
5 files changed, 70 insertions(+), 11 deletions(-)
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index 138113f964..247769c715 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -15,6 +15,7 @@ import { useOfferRedemptions, useOfferSummary } from './data/hooks';
import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import { EXEC_ED_OFFER_TYPE } from './data/constants';
const BudgetCard = ({
offer,
@@ -136,16 +137,19 @@ const BudgetCard = ({
-
-
-
- {renderCardHeader('Executive Education')}
- {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFundsExecEd)}
-
-
-
+ {offerSummary?.offerType === EXEC_ED_OFFER_TYPE
+ && (
+
+
+
+ {renderCardHeader('Executive Education')}
+ {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFundsExecEd)}
+
+
+
+ )}
>
)
: (
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 6c11820c1c..2e27404dee 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -14,3 +14,5 @@ export const LOW_REMAINING_BALANCE_PERCENT_THRESHOLD = 0.75;
export const NO_BALANCE_REMAINING_DOLLAR_THRESHOLD = 100;
export const DATE_FORMAT = 'MMMM DD, YYYY';
+
+export const EXEC_ED_OFFER_TYPE = 'learner_credit';
diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js
index 88773efbcc..33902d40fe 100644
--- a/src/components/learner-credit-management/data/tests/utils.test.js
+++ b/src/components/learner-credit-management/data/tests/utils.test.js
@@ -1,4 +1,5 @@
import { transformOfferSummary } from '../utils';
+import { EXEC_ED_OFFER_TYPE } from '../constants';
describe('transformOfferSummary', () => {
it('should return null if there is no offerSummary', () => {
@@ -11,6 +12,7 @@ describe('transformOfferSummary', () => {
amountOfOfferSpent: 1.34,
remainingBalance: -0.34,
percentOfOfferSpent: 1.34,
+ offerType: EXEC_ED_OFFER_TYPE,
};
expect(transformOfferSummary(offerSummary)).toEqual({
@@ -20,6 +22,7 @@ describe('transformOfferSummary', () => {
redeemedFundsOcm: NaN,
remainingFunds: 0.0,
percentUtilized: 1.0,
+ offerType: EXEC_ED_OFFER_TYPE,
});
});
@@ -29,6 +32,7 @@ describe('transformOfferSummary', () => {
amountOfOfferSpent: 100,
remainingBalance: null,
percentOfOfferSpent: null,
+ offerType: 'Site',
};
expect(transformOfferSummary(offerSummary)).toEqual({
@@ -36,6 +40,7 @@ describe('transformOfferSummary', () => {
redeemedFunds: 100,
remainingFunds: null,
percentUtilized: null,
+ offerType: 'Site',
});
});
});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 22cff6cd3a..30b0efba01 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -37,6 +37,7 @@ export const transformOfferSummary = (offerSummary) => {
if (percentUtilized) {
percentUtilized = Math.min(percentUtilized, 1.0);
}
+ const { offerType } = offerSummary;
return {
totalFunds,
@@ -45,6 +46,7 @@ export const transformOfferSummary = (offerSummary) => {
redeemedFundsExecEd,
remainingFunds,
percentUtilized,
+ offerType,
};
};
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index afa6c60620..49350821d1 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -15,6 +15,7 @@ import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import BudgetCard from '../BudgetCard';
import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
+import { EXEC_ED_OFFER_TYPE } from '../data/constants';
jest.mock('../data/hooks');
useOfferSummary.mockReturnValue({
@@ -51,6 +52,7 @@ const mockOfferSummary = {
redeemedFunds: 200,
remainingFunds: 4800,
percentUtilized: 0.04,
+ offerType: EXEC_ED_OFFER_TYPE,
};
const BudgetCardWrapper = ({ ...rest }) => (
@@ -67,7 +69,7 @@ describe(' ', () => {
jest.clearAllMocks();
});
- it('displays correctly', () => {
+ it('displays correctly for all offers', () => {
const mockOffer = {
id: mockEnterpriseOfferId,
name: mockOfferDisplayName,
@@ -105,6 +107,50 @@ describe(' ', () => {
expect(firstElementWithTestId).toHaveTextContent(formattedString);
});
+ it('displays correctly for Offer type Site', () => {
+ const mockOffer = {
+ id: mockEnterpriseOfferId,
+ name: mockOfferDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ };
+ const mockOfferRedemption = {
+ created: '2022-02-01',
+ enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
+ };
+ useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerType: 'Site',
+ },
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ results: [mockOfferRedemption],
+ itemCount: 1,
+ pageCount: 1,
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+ render( );
+ expect(screen.getByText('Open Courses Marketplace'));
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
+ const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('offer-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+ });
+
it('displays table on clicking view budget', async () => {
const mockOffer = {
id: mockEnterpriseOfferId,
From 9af4648d73782d7e1eef0f674bce2b1ce7aaa266 Mon Sep 17 00:00:00 2001
From: Zaman Afzal
Date: Thu, 24 Aug 2023 17:29:56 +0500
Subject: [PATCH 017/124] feat: Show one card on learner credit screen (#1022)
---
.../BudgetCard-V2.jsx | 163 ++++++++++++++++++
.../LearnerCreditAllocationTable.jsx | 7 +-
.../MultipleBudgetsPicker.jsx | 2 +-
.../tests/BudgetCard.test.jsx | 44 +----
4 files changed, 168 insertions(+), 48 deletions(-)
create mode 100644 src/components/learner-credit-management/BudgetCard-V2.jsx
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
new file mode 100644
index 0000000000..16132bf012
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetCard-V2.jsx
@@ -0,0 +1,163 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import dayjs from 'dayjs';
+import {
+ Card,
+ Button,
+ Stack,
+ Row,
+ Col,
+ Breadcrumb,
+} from '@edx/paragon';
+
+import { useOfferRedemptions, useOfferSummary } from './data/hooks';
+import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
+import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
+import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+
+const BudgetCard = ({
+ offer,
+ enterpriseUUID,
+ enterpriseSlug,
+}) => {
+ const {
+ start,
+ end,
+ } = offer;
+
+ const {
+ isLoading: isLoadingOfferSummary,
+ offerSummary,
+ } = useOfferSummary(enterpriseUUID, offer);
+
+ const {
+ isLoading: isLoadingOfferRedemptions,
+ offerRedemptions,
+ fetchOfferRedemptions,
+ } = useOfferRedemptions(enterpriseUUID, offer?.id);
+ const [detailPage, setDetailPage] = useState(false);
+ const [activeLabel, setActiveLabel] = useState('');
+ const links = [
+ { label: 'Budgets', url: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
+ ];
+ const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
+ const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
+ const navigateToBudgetRedemptions = (budgetType) => {
+ setDetailPage(true);
+ links.push({ label: budgetType, url: `/${enterpriseSlug}/admin/learner-credit` });
+ setActiveLabel(budgetType);
+ };
+
+ const renderActions = (budgetType) => (
+ navigateToBudgetRedemptions(budgetType)}
+ >
+ View Budget
+
+ );
+
+ const renderCardHeader = (budgetType) => {
+ const subtitle = (
+
+
+ {formattedStartDate} - {formattedExpirationDate}
+
+
+ );
+
+ return (
+
+ {renderActions(budgetType)}
+
+ )}
+ />
+ );
+ };
+
+ const renderCardSection = (available, spent) => (
+
+
+
+ Available
+ {available}
+
+
+ Spent
+ {spent}
+
+
+
+ );
+
+ const renderCardAggregate = () => (
+
+
+
+ );
+
+ return (
+
+
+
+
+
+
+ {!detailPage
+ ? (
+ <>
+ {renderCardAggregate()}
+ Budgets
+
+
+
+ {renderCardHeader('Overview')}
+ {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFunds)}
+
+
+
+ >
+ )
+ : (
+
+ )}
+
+ );
+};
+
+BudgetCard.propTypes = {
+ offer: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ start: PropTypes.string.isRequired,
+ end: PropTypes.string.isRequired,
+ }).isRequired,
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+};
+
+export default BudgetCard;
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index e463487937..e5eceb7766 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -17,10 +17,9 @@ const LearnerCreditAllocationTable = ({
tableData,
fetchTableData,
enterpriseUUID,
- budgetType,
}) => {
const isDesktopTable = useMediaQuery({ minWidth: breakpoints.extraLarge.minWidth });
- const defaultFilter = budgetType ? [{ id: 'courseProductLine', value: budgetType }] : [];
+ const defaultFilter = [];
return (
);
};
-LearnerCreditAllocationTable.defaultProps = {
- budgetType: null,
-};
LearnerCreditAllocationTable.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
@@ -109,7 +105,6 @@ LearnerCreditAllocationTable.propTypes = {
pageCount: PropTypes.number.isRequired,
}).isRequired,
fetchTableData: PropTypes.func.isRequired,
- budgetType: PropTypes.string,
};
export default LearnerCreditAllocationTable;
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index a7b4bc814e..8535bd1d21 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -6,7 +6,7 @@ import {
Col,
} from '@edx/paragon';
-import BudgetCard from './BudgetCard';
+import BudgetCard from './BudgetCard-V2';
const MultipleBudgetsPicker = ({
offers,
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index 49350821d1..cfc91ded7c 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -13,7 +13,7 @@ import {
import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import BudgetCard from '../BudgetCard';
+import BudgetCard from '../BudgetCard-V2';
import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
import { EXEC_ED_OFFER_TYPE } from '../data/constants';
@@ -69,45 +69,7 @@ describe('
', () => {
jest.clearAllMocks();
});
- it('displays correctly for all offers', () => {
- const mockOffer = {
- id: mockEnterpriseOfferId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- };
- const mockOfferRedemption = {
- created: '2022-02-01',
- enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
- };
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: mockOfferSummary,
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- results: [mockOfferRedemption],
- itemCount: 1,
- pageCount: 1,
- },
- fetchOfferRedemptions: jest.fn(),
- });
- render(
);
- expect(screen.getByText('Open Courses Marketplace'));
- expect(screen.getByText('Executive Education'));
- expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
- const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
- const elementsWithTestId = screen.getAllByTestId('offer-date');
- const firstElementWithTestId = elementsWithTestId[0];
- expect(firstElementWithTestId).toHaveTextContent(formattedString);
- });
-
- it('displays correctly for Offer type Site', () => {
+ it('displays correctly for Offers', () => {
const mockOffer = {
id: mockEnterpriseOfferId,
name: mockOfferDisplayName,
@@ -142,7 +104,7 @@ describe('
', () => {
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseId}
/>);
- expect(screen.getByText('Open Courses Marketplace'));
+ expect(screen.getByText('Overview'));
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
From ef1692d0541cd160fbd700a9cde4f3cf7d493f6c Mon Sep 17 00:00:00 2001
From: Emily Rosario-Aquin <129111440+emrosarioa@users.noreply.github.com>
Date: Thu, 31 Aug 2023 13:43:12 -0500
Subject: [PATCH 018/124] Refactor LearnerCreditAllocationTable (#1019)
* chore: refactor LCM data table
---
.../BudgetCard-V2.jsx | 1 +
.../LearnerCreditAllocationTable.jsx | 54 ++++++++++++-------
.../learner-credit-management/data/hooks.js | 13 ++---
.../data/tests/hooks.test.js | 6 +--
.../learner-credit-management/data/utils.js | 1 +
.../tests/BudgetCard.test.jsx | 1 -
.../LearnerCreditAllocationTable.test.jsx | 38 ++++++++++++-
7 files changed, 79 insertions(+), 35 deletions(-)
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
index 16132bf012..d9a1db40c9 100644
--- a/src/components/learner-credit-management/BudgetCard-V2.jsx
+++ b/src/components/learner-credit-management/BudgetCard-V2.jsx
@@ -143,6 +143,7 @@ const BudgetCard = ({
tableData={offerRedemptions}
fetchTableData={fetchOfferRedemptions}
enterpriseUUID={enterpriseUUID}
+ enterpriseSlug={enterpriseSlug}
/>
)}
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index e5eceb7766..7422b41594 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -1,10 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
-import {
- DataTable, useMediaQuery, breakpoints,
-} from '@edx/paragon';
-
+import { DataTable, Hyperlink } from '@edx/paragon';
+import { getConfig } from '@edx/frontend-platform/config';
import TableTextFilter from './TableTextFilter';
import EmailAddressTableCell from './EmailAddressTableCell';
import { getCourseProductLineText } from '../../utils';
@@ -12,13 +10,19 @@ import { getCourseProductLineText } from '../../utils';
export const PAGE_SIZE = 20;
export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
+const getEnrollmentDetailsAccessor = row => ({
+ courseTitle: row.courseTitle,
+ userEmail: row.userEmail,
+ courseKey: row.courseKey,
+});
+
const LearnerCreditAllocationTable = ({
isLoading,
tableData,
fetchTableData,
enterpriseUUID,
+ enterpriseSlug,
}) => {
- const isDesktopTable = useMediaQuery({ minWidth: breakpoints.extraLarge.minWidth });
const defaultFilter = [];
return (
@@ -29,32 +33,41 @@ const LearnerCreditAllocationTable = ({
manualPagination
isFilterable
manualFilters
- showFiltersInSidebar={isDesktopTable}
isLoading={isLoading}
defaultColumnValues={{ Filter: TableTextFilter }}
+ /* eslint-disable */
columns={[
{
- Header: 'Email Address',
- accessor: 'userEmail',
- // eslint-disable-next-line react/prop-types, react/no-unstable-nested-components
- Cell: ({ row }) =>
,
+ Header: 'Date',
+ accessor: 'enrollmentDate',
+ Cell: ({ row }) => dayjs(row.values.enrollmentDate).format('MMM D, YYYY'),
+ disableFilters: true,
},
{
- Header: 'Course Name',
- accessor: 'courseTitle',
+ Header: 'Enrollment details',
+ accessor: getEnrollmentDetailsAccessor,
+ Cell: ({ row }) => (
+ <>
+
+
+
+ {row.original.courseTitle}
+
+
+ >
+ ),
+ disableFilters: false,
+ disableSortBy: true,
},
{
- Header: 'Course Price',
+ Header: 'Amount',
accessor: 'courseListPrice',
Cell: ({ row }) => `$${row.values.courseListPrice}`,
disableFilters: true,
},
- {
- Header: 'Date Spent',
- accessor: 'enrollmentDate',
- Cell: ({ row }) => dayjs(row.values.enrollmentDate).format('MMMM DD, YYYY'),
- disableFilters: true,
- },
{
Header: 'Product',
accessor: 'courseProductLine',
@@ -78,7 +91,6 @@ const LearnerCreditAllocationTable = ({
itemCount={tableData.itemCount}
pageCount={tableData.pageCount}
EmptyTableComponent={
- // eslint-disable-next-line react/no-unstable-nested-components
() => {
if (isLoading) {
return null;
@@ -89,9 +101,11 @@ const LearnerCreditAllocationTable = ({
/>
);
};
+/* eslint-enable */
LearnerCreditAllocationTable.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
isLoading: PropTypes.bool.isRequired,
tableData: PropTypes.shape({
results: PropTypes.arrayOf(PropTypes.shape({
diff --git a/src/components/learner-credit-management/data/hooks.js b/src/components/learner-credit-management/data/hooks.js
index 7ff74aaa69..585970c35e 100644
--- a/src/components/learner-credit-management/data/hooks.js
+++ b/src/components/learner-credit-management/data/hooks.js
@@ -63,18 +63,15 @@ const applySortByToOptions = (sortBy, options) => {
};
const applyFiltersToOptions = (filters, options) => {
- const userSearchQuery = filters?.find(filter => filter.id === 'userEmail')?.value;
- const courseTitleSearchQuery = filters?.find(filter => filter.id === 'courseTitle')?.value;
const courseProductLineSearchQuery = filters?.find(filter => filter.id === 'courseProductLine')?.value;
- if (userSearchQuery) {
- Object.assign(options, { search: userSearchQuery });
- }
- if (courseTitleSearchQuery) {
- Object.assign(options, { searchCourse: courseTitleSearchQuery });
- }
+ const searchQuery = filters?.find(filter => filter.id.toLowerCase() === 'enrollment details')?.value;
+
if (courseProductLineSearchQuery) {
Object.assign(options, { courseProductLine: courseProductLineSearchQuery });
}
+ if (searchQuery) {
+ Object.assign(options, { searchAll: searchQuery });
+ }
};
export const useOfferRedemptions = (enterpriseUUID, offerId) => {
diff --git a/src/components/learner-credit-management/data/tests/hooks.test.js b/src/components/learner-credit-management/data/tests/hooks.test.js
index fa03c60f9e..8ab61bce2f 100644
--- a/src/components/learner-credit-management/data/tests/hooks.test.js
+++ b/src/components/learner-credit-management/data/tests/hooks.test.js
@@ -105,8 +105,7 @@ describe('useOfferRedemptions', () => {
{ id: 'enrollmentDate', desc: true },
],
filters: [
- { id: 'userEmail', value: mockOfferEnrollments[0].user_email },
- { id: 'courseTitle', value: mockOfferEnrollments[0].course_title },
+ { id: 'Enrollment Details', value: mockOfferEnrollments[0].user_email },
],
});
});
@@ -118,8 +117,7 @@ describe('useOfferRedemptions', () => {
pageSize: 20,
offerId: mockEnterpriseOffer.id,
ordering: '-enrollment_date', // default sort order
- search: mockOfferEnrollments[0].user_email,
- searchCourse: mockOfferEnrollments[0].course_title,
+ searchAll: mockOfferEnrollments[0].user_email,
ignoreNullCourseListPrice: true,
};
expect(EnterpriseDataApiService.fetchCourseEnrollments).toHaveBeenCalledWith(
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 30b0efba01..65524c1346 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -68,6 +68,7 @@ export const transformUtilizationTableResults = results => results.map(result =>
enrollmentDate: result.enrollmentDate,
courseProductLine: result.courseProductLine,
uuid: uuidv4(),
+ courseKey: result.courseKey,
}));
/**
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index cfc91ded7c..7d8f349bda 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -141,7 +141,6 @@ describe('
', () => {
const elementsWithTestId = screen.getAllByTestId('view-budget');
const firstElementWithTestId = elementsWithTestId[0];
await waitFor(() => userEvent.click(firstElementWithTestId));
- expect(screen.getByText('Filters'));
expect(screen.getByText('No results found'));
});
});
diff --git a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
index fb79748c70..f67aa0e8bb 100644
--- a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
+++ b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
@@ -7,6 +7,10 @@ import { IntlProvider } from '@edx/frontend-platform/i18n';
import LearnerCreditAllocationTable from '../LearnerCreditAllocationTable';
+jest.mock('@edx/frontend-platform/config', () => ({
+ getConfig: () => ({ ENTERPRISE_LEARNER_PORTAL_URL: 'https://enterprise.edx.org' }),
+}));
+
const LearnerCreditAllocationTableWrapper = (props) => (
@@ -19,6 +23,7 @@ describe(' ', () => {
enterpriseUUID: 'test-enterprise-id',
isLoading: false,
budgetType: 'OCM',
+ enterpriseSlug: 'test-enterprise-slug',
tableData: {
results: [{
userEmail: 'test@example.com',
@@ -36,7 +41,6 @@ describe(' ', () => {
render( );
- expect(screen.getByText('Open', { exact: false }));
expect(screen.getByText(props.tableData.results[0].userEmail.toString(), {
exact: false,
}));
@@ -46,7 +50,7 @@ describe(' ', () => {
expect(screen.getByText(props.tableData.results[0].courseListPrice.toString(), {
exact: false,
}));
- expect(screen.getByText('February', { exact: false }));
+ expect(screen.getByText('Feb', { exact: false }));
});
it('renders with empty table data', () => {
const props = {
@@ -66,4 +70,34 @@ describe(' ', () => {
expect(screen.getByText('No results found', { exact: false }));
});
+
+ it('constructs the correct URL for the course', () => {
+ const props = {
+ enterpriseUUID: 'test-enterprise-id',
+ isLoading: false,
+ budgetType: 'OCM',
+ enterpriseSlug: 'test-enterprise-slug',
+ tableData: {
+ results: [{
+ userEmail: 'test@example.com',
+ courseTitle: 'course-title',
+ courseKey: 'course-v1:edX=CTL.SC101x.3T2019',
+ courseListPrice: 100,
+ enrollmentDate: '2-2-23',
+ courseProductLine: 'OCM',
+ }],
+ itemCount: 1,
+ pageCount: 1,
+ },
+ fetchTableData: jest.fn(),
+ };
+ props.fetchTableData.mockReturnValue(props.tableData);
+
+ render( );
+
+ const expectedLink = 'https://enterprise.edx.org/test-enterprise-slug/course/course-v1:edX=CTL.SC101x.3T2019';
+ const courseLinkElement = screen.getByText('course-title');
+
+ expect(courseLinkElement.getAttribute('href')).toBe(expectedLink);
+ });
});
From 0979d48ff37cfedc48c560c913b47e3911272b55 Mon Sep 17 00:00:00 2001
From: Marlon Keating
Date: Wed, 30 Aug 2023 19:59:50 +0000
Subject: [PATCH 019/124] feat: Add feature flag AUTH0_SELF_SERVICE_INTEGRATION
chore: clean up comments
---
.../SettingsSSOTab/NewSSOConfigForm.jsx | 5 ++++-
.../settings/SettingsSSOTab/NewSSOStepper.jsx | 3 +++
.../tests/NewSSOConfigForm.test.jsx | 21 +++++++++++++++++++
src/config/index.js | 1 +
4 files changed, 29 insertions(+), 1 deletion(-)
create mode 100644 src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx
index cac4e0ee98..6cbcab4c14 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx
@@ -4,16 +4,19 @@ import { WarningFilled } from '@edx/paragon/icons';
import { SSOConfigContext } from './SSOConfigContext';
import SSOStepper from './SSOStepper';
import { HELP_CENTER_SAML_LINK } from '../data/constants';
+import { features } from '../../../config';
+import NewSSOStepper from './NewSSOStepper';
const NewSSOConfigForm = () => {
const { ssoState: { currentError } } = useContext(SSOConfigContext);
+ const { AUTH0_SELF_SERVICE_INTEGRATION } = features;
return (
Connect to a SAML identity provider for single sign-on
to allow quick access to your organization's learning catalog.
-
+ {AUTH0_SELF_SERVICE_INTEGRATION ?
:
}
{currentError && (
null;
+
+export default NewSSOStepper;
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
index 891c102f0d..03372c4fd5 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
@@ -12,6 +12,7 @@ import handleErrors from '../../utils';
import {
INVALID_ODATA_API_TIMEOUT_INTERVAL, INVALID_SAPSF_OAUTH_ROOT_URL, INVALID_API_ROOT_URL,
} from '../../data/constants';
+import { features } from '../../../../config';
jest.mock('../data/actions');
jest.mock('../../utils');
@@ -70,6 +71,7 @@ const contextValue = {
describe('SAML Config Tab', () => {
afterEach(() => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = false;
jest.clearAllMocks();
});
test('canceling connect step', async () => {
@@ -307,6 +309,25 @@ describe('SAML Config Tab', () => {
expect(screen.getByText('Next')).not.toBeDisabled();
}, []);
});
+ test('show new SSO stepper placeholder when feature flag enabled', async () => {
+ // Setup
+ features.AUTH0_SELF_SERVICE_INTEGRATION = true;
+ contextValue.ssoState.currentStep = 'idp';
+ render(
+
+
+ ,
+ );
+ await waitFor(() => {
+ expect(
+ screen.queryByText(
+ 'Connect to a SAML identity provider for single sign-on'
+ + ' to allow quick access to your organization\'s learning catalog.',
+ ),
+ ).toBeInTheDocument();
+ expect(screen.queryByText('Next')).not.toBeInTheDocument();
+ }, []);
+ });
test('idp step fetches and displays existing idp data fields', async () => {
// Setup
const mockGetProviderData = jest.spyOn(LmsApiService, 'getProviderData');
diff --git a/src/config/index.js b/src/config/index.js
index 4fe281804e..663f190723 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -53,6 +53,7 @@ const features = {
FEATURE_SSO_SETTINGS_TAB: process.env.FEATURE_SSO_SETTINGS_TAB || hasFeatureFlagEnabled('SSO_SETTINGS_TAB'),
FEATURE_INTEGRATION_REPORTING: process.env.FEATURE_INTEGRATION_REPORTING || hasFeatureFlagEnabled('FEATURE_INTEGRATION_REPORTING'),
SUBSCRIPTION_LPR: process.env.SUBSCRIPTION_LPR || hasFeatureFlagEnabled('SUBSCRIPTION_LPR'),
+ AUTH0_SELF_SERVICE_INTEGRATION: process.env.AUTH0_SELF_SERVICE_INTEGRATION || hasFeatureFlagEnabled('AUTH0_SELF_SERVICE_INTEGRATION'),
};
export { configuration, features };
From 5a629a57896cc96f1118f4b960096febf0f49a90 Mon Sep 17 00:00:00 2001
From: Emily Rosario-Aquin <129111440+emrosarioa@users.noreply.github.com>
Date: Fri, 1 Sep 2023 08:44:32 -0500
Subject: [PATCH 020/124] ENT-7512: LearnerCreditAllocationTable course title
(#1025)
* chore: courseTitle link in LCM only if learner portal enabled
---
.../BudgetCard-V2.jsx | 3 ++
.../LearnerCreditAllocationTable.jsx | 9 ++++++
.../MultipleBudgetsPage.jsx | 4 +++
.../MultipleBudgetsPicker.jsx | 3 ++
.../LearnerCreditAllocationTable.test.jsx | 30 +++++++++++++++++++
5 files changed, 49 insertions(+)
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
index d9a1db40c9..b39b9297d9 100644
--- a/src/components/learner-credit-management/BudgetCard-V2.jsx
+++ b/src/components/learner-credit-management/BudgetCard-V2.jsx
@@ -19,6 +19,7 @@ const BudgetCard = ({
offer,
enterpriseUUID,
enterpriseSlug,
+ enableLearnerPortal,
}) => {
const {
start,
@@ -144,6 +145,7 @@ const BudgetCard = ({
fetchTableData={fetchOfferRedemptions}
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseSlug}
+ enableLearnerPortal={enableLearnerPortal}
/>
)}
@@ -159,6 +161,7 @@ BudgetCard.propTypes = {
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
};
export default BudgetCard;
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index 7422b41594..555df1ca95 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -16,12 +16,15 @@ const getEnrollmentDetailsAccessor = row => ({
courseKey: row.courseKey,
});
+const FilterStatus = (rest) => ;
+
const LearnerCreditAllocationTable = ({
isLoading,
tableData,
fetchTableData,
enterpriseUUID,
enterpriseSlug,
+ enableLearnerPortal,
}) => {
const defaultFilter = [];
@@ -35,6 +38,7 @@ const LearnerCreditAllocationTable = ({
manualFilters
isLoading={isLoading}
defaultColumnValues={{ Filter: TableTextFilter }}
+ FilterStatusComponent={FilterStatus}
/* eslint-disable */
columns={[
{
@@ -50,12 +54,16 @@ const LearnerCreditAllocationTable = ({
<>
+ {enableLearnerPortal ? (
{row.original.courseTitle}
+ ) : (
+ row.original.courseTitle
+ )}
>
),
@@ -106,6 +114,7 @@ const LearnerCreditAllocationTable = ({
LearnerCreditAllocationTable.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
tableData: PropTypes.shape({
results: PropTypes.arrayOf(PropTypes.shape({
diff --git a/src/components/learner-credit-management/MultipleBudgetsPage.jsx b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
index cec507a231..3df18c465a 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPage.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
@@ -22,6 +22,7 @@ const PAGE_TITLE = 'Learner Credit';
const MultipleBudgetsPage = ({
enterpriseUUID,
enterpriseSlug,
+ enableLearnerPortal,
}) => {
const { offers, isLoading } = useContext(EnterpriseSubsidiesContext);
@@ -66,6 +67,7 @@ const MultipleBudgetsPage = ({
offers={offers}
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseSlug}
+ enableLearnerPortal={enableLearnerPortal}
/>
>
);
@@ -74,11 +76,13 @@ const MultipleBudgetsPage = ({
const mapStateToProps = state => ({
enterpriseUUID: state.portalConfiguration.enterpriseId,
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
});
MultipleBudgetsPage.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
};
export default connect(mapStateToProps)(MultipleBudgetsPage);
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index 8535bd1d21..4c3da2d0ce 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -12,6 +12,7 @@ const MultipleBudgetsPicker = ({
offers,
enterpriseUUID,
enterpriseSlug,
+ enableLearnerPortal,
}) => (
@@ -22,6 +23,7 @@ const MultipleBudgetsPicker = ({
offer={offer}
enterpriseUUID={enterpriseUUID}
enterpriseSlug={enterpriseSlug}
+ enableLearnerPortal={enableLearnerPortal}
/>
))}
@@ -33,6 +35,7 @@ MultipleBudgetsPicker.propTypes = {
offers: PropTypes.arrayOf(PropTypes.shape()).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
};
export default MultipleBudgetsPicker;
diff --git a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
index f67aa0e8bb..9099404e4f 100644
--- a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
+++ b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
@@ -24,6 +24,7 @@ describe(' ', () => {
isLoading: false,
budgetType: 'OCM',
enterpriseSlug: 'test-enterprise-slug',
+ enableLearnerPortal: true,
tableData: {
results: [{
userEmail: 'test@example.com',
@@ -77,6 +78,7 @@ describe(' ', () => {
isLoading: false,
budgetType: 'OCM',
enterpriseSlug: 'test-enterprise-slug',
+ enableLearnerPortal: true,
tableData: {
results: [{
userEmail: 'test@example.com',
@@ -100,4 +102,32 @@ describe(' ', () => {
expect(courseLinkElement.getAttribute('href')).toBe(expectedLink);
});
+
+ it('does not render the course link if the learner portal is disabled', () => {
+ const props = {
+ enterpriseUUID: 'test-enterprise-id',
+ isLoading: false,
+ budgetType: 'OCM',
+ enterpriseSlug: 'test-enterprise-slug',
+ enableLearnerPortal: false,
+ tableData: {
+ results: [{
+ userEmail: 'test@example.com',
+ courseTitle: 'course-title',
+ courseKey: 'course-v1:edX=CTL.SC101x.3T2019',
+ courseListPrice: 100,
+ enrollmentDate: '2-2-23',
+ courseProductLine: 'OCM',
+ }],
+ itemCount: 1,
+ pageCount: 1,
+ },
+ fetchTableData: jest.fn(),
+ };
+ props.fetchTableData.mockReturnValue(props.tableData);
+
+ render( );
+ const courseTitleElement = screen.queryByText('course-title');
+ expect(courseTitleElement.closest('a')).toBeNull();
+ });
});
From 4da552cbdfcc86d3e48b5342ff8714bf504e77c2 Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Fri, 8 Sep 2023 09:53:15 -0600
Subject: [PATCH 021/124] feat: generate API Credentials in Admin Portal
(#1027)
* feat: adding api credential tab
* feat: add zero state card under api credentails tab
* feat: add a new tab
* fix: make lms-service run as expected
* fix: fix coupon.test.jsx lint error
* feat: generate API Credentials Tab in Admin Portal
* fix: modify modal
* fix: modify lmsservice url
* fix: remove dependency in useffect
* fix: add api-document url
* fix: lots of little fixes
* fix: test fixes
* fix: more changes
* fix: more fixes
* fix: PR review requests
---
.env.development | 1 +
.../ContactCustomerSupportButton/index.jsx | 2 +-
src/components/forms/FormWorkflow.tsx | 11 +-
src/components/settings/HelpCenterButton.jsx | 33 ++
.../APICredentialsPage.jsx | 98 ++++++
.../SettingsApiCredentialsTab/Context.jsx | 5 +
.../SettingsApiCredentialsTab/CopiedToast.jsx | 13 +
.../SettingsApiCredentialsTab/CopyButton.jsx | 48 +++
.../SettingsApiCredentialsTab/FailedAlert.jsx | 16 +
.../RegenerateCredentialWarningModal.jsx | 98 ++++++
.../ZeroStateCard.jsx | 91 ++++++
.../SettingsApiCredentialsTab/constants.jsx | 14 +
.../SettingsApiCredentialsTab/index.jsx | 70 +++++
.../tests/SettingsAPICredentialsPage.test.jsx | 281 ++++++++++++++++++
.../settings/SettingsLMSTab/index.jsx | 11 +-
src/components/settings/SettingsTabs.jsx | 57 +++-
src/components/settings/data/constants.js | 9 +
src/components/settings/settings.scss | 15 +-
.../settings/tests/SettingsTabs.test.jsx | 14 +
src/config/index.js | 1 +
src/data/images/ZeroState.svg | 45 +++
src/data/reducers/portalConfiguration.js | 4 +
src/data/reducers/portalConfiguration.test.js | 3 +
src/data/services/LmsApiService.js | 17 ++
24 files changed, 924 insertions(+), 33 deletions(-)
create mode 100644 src/components/settings/HelpCenterButton.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/Context.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/CopiedToast.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/FailedAlert.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/RegenerateCredentialWarningModal.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/constants.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/index.jsx
create mode 100644 src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx
create mode 100644 src/data/images/ZeroState.svg
diff --git a/.env.development b/.env.development
index d74a56083c..03858d1ff4 100644
--- a/.env.development
+++ b/.env.development
@@ -40,6 +40,7 @@ FEATURE_SETTINGS_PAGE_LMS_TAB='true'
FEATURE_SETTINGS_PAGE_APPEARANCE_TAB='true'
FEATURE_LEARNER_CREDIT_MANAGEMENT='true'
FEATURE_CONTENT_HIGHLIGHTS='true'
+FEATURE_API_CREDENTIALS_TAB='true'
HOTJAR_APP_ID=''
HOTJAR_VERSION='6'
HOTJAR_DEBUG=''
diff --git a/src/components/ContactCustomerSupportButton/index.jsx b/src/components/ContactCustomerSupportButton/index.jsx
index 8c2e1408ca..b0bdee42cf 100644
--- a/src/components/ContactCustomerSupportButton/index.jsx
+++ b/src/components/ContactCustomerSupportButton/index.jsx
@@ -31,7 +31,7 @@ ContactCustomerSupportButton.propTypes = {
ContactCustomerSupportButton.defaultProps = {
children: 'Contact support',
- variant: 'btn-outline-primary',
+ variant: 'outline-primary',
};
export default ContactCustomerSupportButton;
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index e87c12a931..f6c2a9d6da 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import type { Dispatch } from 'react';
import {
- ActionRow, Button, FullscreenModal, Hyperlink, Stepper, useToggle,
+ ActionRow, Button, FullscreenModal, Stepper, useToggle,
} from '@edx/paragon';
import { Launch } from '@edx/paragon/icons';
@@ -14,6 +14,7 @@ import { HELP_CENTER_LINK, SUBMIT_TOAST_MESSAGE } from '../settings/data/constan
import UnsavedChangesModal from '../settings/SettingsLMSTab/UnsavedChangesModal';
import ConfigErrorModal from '../settings/ConfigErrorModal';
import { channelMapping, pollAsync } from '../../utils';
+import HelpCenterButton from '../settings/HelpCenterButton';
export const WAITING_FOR_ASYNC_OPERATION = 'WAITING FOR ASYNC OPERATION';
@@ -201,13 +202,9 @@ const FormWorkflow = ({
className="stepper-modal"
footerNode={(
-
+
Help Center: Integrations
-
+
Cancel
{nextButtonConfig && (
diff --git a/src/components/settings/HelpCenterButton.jsx b/src/components/settings/HelpCenterButton.jsx
new file mode 100644
index 0000000000..50b9749abb
--- /dev/null
+++ b/src/components/settings/HelpCenterButton.jsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Hyperlink } from '@edx/paragon';
+import PropTypes from 'prop-types';
+
+const HelpCenterButton = ({
+ url,
+ children,
+ ...rest
+}) => {
+ const destinationUrl = url;
+
+ return (
+
+ {children}
+
+ );
+};
+
+HelpCenterButton.defaultProps = {
+ children: 'Help Center',
+};
+
+HelpCenterButton.propTypes = {
+ children: PropTypes.node,
+ url: PropTypes.string,
+};
+
+export default HelpCenterButton;
diff --git a/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx b/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
new file mode 100644
index 0000000000..f86dc4aaaf
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
@@ -0,0 +1,98 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+
+import { Form, Hyperlink } from '@edx/paragon';
+import { dataPropType } from './constants';
+import RegenerateCredentialWarningModal from './RegenerateCredentialWarningModal';
+import CopyButton from './CopyButton';
+import { API_CLIENT_DOCUMENTATION, HELP_CENTER_LINK } from '../data/constants';
+
+const APICredentialsPage = ({ data, setData }) => {
+ const [formValue, setFormValue] = useState('');
+ const handleFormChange = (e) => {
+ setFormValue(e.target.value);
+ };
+ return (
+
+
+
Your API credentials
+
+ Copy and paste the following credential information and send it to your API developer(s).
+
+
+
+
+ Application name:
+ {data?.name}
+
+
+ Allowed URIs:
+ {data?.redirect_uris}
+
+
+ API client ID:
+ {data?.client_id}
+
+
+ API client secret:
+ {data?.client_secret}
+
+
API client documentation:
+ {API_CLIENT_DOCUMENTATION}
+
+
+ Last generated on:
+ {data?.updated}
+
+
+
+
+
+
+
Redirect URIs (optional)
+
+ If you need additional redirect URIs, add them below and regenerate your API credentials.
+ You will need to communicate the new credentials to your API developers.
+
+
+
+ Allowed URIs list, space separated
+
+
+
+
+
Questions or modifications?
+
+ To troubleshoot your API credentialing, or to request additional API endpoints to your
+ credentials,
+
+ contact Enterprise Customer Support.
+
+
+
+
+ );
+};
+
+APICredentialsPage.defaultProps = {
+ data: null,
+};
+
+APICredentialsPage.propTypes = {
+ data: PropTypes.shape(dataPropType),
+ setData: PropTypes.func.isRequired,
+};
+
+export default APICredentialsPage;
diff --git a/src/components/settings/SettingsApiCredentialsTab/Context.jsx b/src/components/settings/SettingsApiCredentialsTab/Context.jsx
new file mode 100644
index 0000000000..4682f99ef7
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/Context.jsx
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+
+export const ErrorContext = createContext(null);
+export const ShowSuccessToast = createContext(null);
+export const EnterpriseId = createContext(null);
diff --git a/src/components/settings/SettingsApiCredentialsTab/CopiedToast.jsx b/src/components/settings/SettingsApiCredentialsTab/CopiedToast.jsx
new file mode 100644
index 0000000000..5506b7e14f
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/CopiedToast.jsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Toast } from '@edx/paragon';
+
+const CopiedToast = ({ content, ...rest }) => (
+
+ {content}
+
+);
+CopiedToast.propTypes = {
+ content: PropTypes.string.isRequired,
+};
+export default CopiedToast;
diff --git a/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx b/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
new file mode 100644
index 0000000000..eb3477e047
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
@@ -0,0 +1,48 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+
+import { Button } from '@edx/paragon';
+import { ContentCopy } from '@edx/paragon/icons';
+import CopiedToast from './CopiedToast';
+import { dataPropType } from './constants';
+
+const CopyButton = ({ data }) => {
+ const [isCopyLinkToastOpen, setIsCopyLinkToastOpen] = useState(false);
+ const [copiedError, setCopiedError] = useState(false);
+
+ const handleCopyLink = async () => {
+ try {
+ const jsonString = JSON.stringify(data);
+ await navigator.clipboard.writeText(jsonString);
+ } catch (error) {
+ setCopiedError(true);
+ } finally {
+ setIsCopyLinkToastOpen(true);
+ }
+ };
+ const handleCloseLinkCopyToast = () => {
+ setIsCopyLinkToastOpen(false);
+ };
+ return (
+ <>
+
+ Copy credentials to clipboard
+
+
+ >
+ );
+};
+
+CopyButton.propTypes = {
+ data: PropTypes.shape(dataPropType),
+};
+
+export default CopyButton;
diff --git a/src/components/settings/SettingsApiCredentialsTab/FailedAlert.jsx b/src/components/settings/SettingsApiCredentialsTab/FailedAlert.jsx
new file mode 100644
index 0000000000..f37c285dc1
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/FailedAlert.jsx
@@ -0,0 +1,16 @@
+import { Alert } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+import { credentialErrorMessage } from './constants';
+
+const FailedAlert = () => (
+
+
+ Credential generation failed
+
+
+ {credentialErrorMessage}
+
+
+);
+
+export default FailedAlert;
diff --git a/src/components/settings/SettingsApiCredentialsTab/RegenerateCredentialWarningModal.jsx b/src/components/settings/SettingsApiCredentialsTab/RegenerateCredentialWarningModal.jsx
new file mode 100644
index 0000000000..dd829012d5
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/RegenerateCredentialWarningModal.jsx
@@ -0,0 +1,98 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import {
+ ActionRow, Button, Icon, ModalDialog, useToggle,
+} from '@edx/paragon';
+import { Warning } from '@edx/paragon/icons';
+
+import {
+ ErrorContext,
+ ShowSuccessToast, EnterpriseId,
+} from './Context';
+import LmsApiService from '../../../data/services/LmsApiService';
+import { dataPropType } from './constants';
+
+const RegenerateCredentialWarningModal = ({
+ redirectURIs,
+ data,
+ setData,
+}) => {
+ const [isOn, setOn, setOff] = useToggle(false);
+ const [, setHasError] = useContext(ErrorContext);
+ const [, setShowSuccessToast] = useContext(ShowSuccessToast);
+ const enterpriseId = useContext(EnterpriseId);
+ const handleOnClickRegeneration = async () => {
+ try {
+ const response = await LmsApiService.regenerateAPICredentials(redirectURIs, enterpriseId);
+ const newURIs = response.data.redirect_uris;
+ setShowSuccessToast(true);
+ const updatedData = data;
+ updatedData.redirect_uris = newURIs;
+ setData(updatedData);
+ } catch (error) {
+ setHasError(true);
+ } finally {
+ setOff(true);
+ }
+ };
+
+ return (
+ <>
+
+ Regenerate API Credentials
+
+
+
+
+
+
+ Regenerate API credentials?
+
+
+
+
+
+ Any system, job, or script using the previous credentials will no
+ longer be able to authenticate with the edX API.
+
+
+ If you do regenerate, you will need to send the new credentials to your developers.
+
+
+
+
+
+ Cancel
+
+
+ Regenerate
+
+
+
+
+ >
+ );
+};
+
+RegenerateCredentialWarningModal.propTypes = {
+ redirectURIs: PropTypes.string.isRequired,
+ data: PropTypes.shape(dataPropType),
+ setData: PropTypes.func.isRequired,
+};
+
+export default RegenerateCredentialWarningModal;
diff --git a/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx b/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx
new file mode 100644
index 0000000000..a9e37a0b8f
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx
@@ -0,0 +1,91 @@
+import React, { useState, useContext } from 'react';
+import PropTypes from 'prop-types';
+
+import {
+ Button, Card, Hyperlink, Icon, Spinner,
+} from '@edx/paragon';
+import { Add, Error } from '@edx/paragon/icons';
+
+import { credentialErrorMessage } from './constants';
+import cardImage from '../../../data/images/ZeroState.svg';
+import { EnterpriseId } from './Context';
+import LmsApiService from '../../../data/services/LmsApiService';
+import {
+ API_CLIENT_DOCUMENTATION, API_TERMS_OF_SERVICE, HELP_CENTER_LINK,
+} from '../data/constants';
+
+const ZeroStateCard = ({ setShowToast, setData }) => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [displayFailureAlert, setFailureAlert] = useState(false);
+ const enterpriseId = useContext(EnterpriseId);
+ const handleClick = async () => {
+ setIsLoading(true);
+ try {
+ const response = await LmsApiService.createNewAPICredentials(enterpriseId);
+ const data = { ...response.data, api_client_documentation: API_CLIENT_DOCUMENTATION };
+ setData(data);
+ setShowToast(true);
+ } catch (err) {
+ setFailureAlert(true);
+ }
+ };
+
+ return (
+
+
+
+ You don't have API credentials yet.
+ { !displayFailureAlert && (
+
+ This page allows you to generate API credentials to send to
+ your developers so they can work on integration projects.
+ If you believe you are seeing this page in error,
+
+ contact Enterprise Customer Support.
+
+
+ )}
+
+ edX for Business API credentials credentials will provide access
+ to the following edX API endpoints: reporting dashboard, dashboard, and catalog administration.
+
+
+ By clicking the button below, you and your organization accept the {'\n'}
+ edX API terms of service .
+
+
+
+ { displayFailureAlert && (
+
+
+ {credentialErrorMessage}
+
+ )}
+
+ {isLoading && }
+ {isLoading ? 'Generating...' : 'Generate API Credentials'}
+
+
+
+ );
+};
+
+ZeroStateCard.propTypes = {
+ setShowToast: PropTypes.func.isRequired,
+ setData: PropTypes.func.isRequired,
+};
+
+export default ZeroStateCard;
diff --git a/src/components/settings/SettingsApiCredentialsTab/constants.jsx b/src/components/settings/SettingsApiCredentialsTab/constants.jsx
new file mode 100644
index 0000000000..2132f801df
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/constants.jsx
@@ -0,0 +1,14 @@
+import PropTypes from 'prop-types';
+
+export const dataPropType = PropTypes.shape({
+ name: PropTypes.string,
+ redirect_uris: PropTypes.string,
+ client_id: PropTypes.string,
+ client_secret: PropTypes.string,
+ api_client_documentation: PropTypes.string,
+ updated: PropTypes.bool,
+});
+
+export const credentialErrorMessage = 'Something went wrong while '
++ 'generating your credentials. Please try again. '
++ 'If the issue continues, contact Enterprise Customer Support.';
diff --git a/src/components/settings/SettingsApiCredentialsTab/index.jsx b/src/components/settings/SettingsApiCredentialsTab/index.jsx
new file mode 100644
index 0000000000..2cdef4d762
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/index.jsx
@@ -0,0 +1,70 @@
+/* eslint-disable react/jsx-no-constructed-context-values */
+import React, { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+
+import { logError } from '@edx/frontend-platform/logging';
+import { ActionRow, Toast } from '@edx/paragon';
+import ZeroStateCard from './ZeroStateCard';
+import APICredentialsPage from './APICredentialsPage';
+import FailedAlert from './FailedAlert';
+import { HELP_CENTER_API_GUIDE } from '../data/constants';
+import HelpCenterButton from '../HelpCenterButton';
+import {
+ EnterpriseId, ErrorContext, ShowSuccessToast,
+} from './Context';
+import LmsApiService from '../../../data/services/LmsApiService';
+
+const SettingsApiCredentialsTab = ({
+ enterpriseId,
+}) => {
+ const [data, setData] = useState();
+ const [hasRegenerationError, setHasRegenerationError] = useState(false);
+ const [showToast, setShowToast] = useState(false);
+
+ useEffect(() => {
+ const fetchExistingAPICredentials = async () => {
+ try {
+ const response = await LmsApiService.fetchAPICredentials(enterpriseId);
+ setData(response.data);
+ } catch (error) {
+ logError(error);
+ }
+ };
+ fetchExistingAPICredentials();
+ }, [enterpriseId]);
+
+ return (
+
+
+
+ { hasRegenerationError && }
+
+ API credentials
+
+
+ Help Center: EdX Enterprise API Guide
+
+
+
+ {!data ? (
+
+ ) : (
)}
+
+
+ { showToast && (
+ setShowToast(false)}
+ show={showToast}
+ >
+ API credentials successfully generated
+
+ )}
+
+
+
+ );
+};
+SettingsApiCredentialsTab.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+};
+export default SettingsApiCredentialsTab;
diff --git a/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx b/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx
new file mode 100644
index 0000000000..425d02b7ca
--- /dev/null
+++ b/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx
@@ -0,0 +1,281 @@
+import {
+ render, screen, waitFor,
+} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import '@testing-library/jest-dom/extend-expect';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import LmsApiService from '../../../../data/services/LmsApiService';
+import SettingsApiCredentialsTab from '../index';
+import {
+ API_CLIENT_DOCUMENTATION, HELP_CENTER_API_GUIDE, API_TERMS_OF_SERVICE, HELP_CENTER_LINK,
+} from '../../data/constants';
+
+jest.mock('../../../../data/services/LmsApiService', () => ({
+ fetchAPICredentials: jest.fn(),
+ createNewAPICredentials: jest.fn(),
+ regenerateAPICredentials: jest.fn(),
+}));
+
+const name = "edx's Enterprise Credentials";
+const clientId = 'y0TCvOEvvIs6ll95irirzCJ5EaF0RnSbBIIXuNJE';
+const clientSecret = '1G896sVeT67jtjHO6FNd5qFqayZPIV7BtnW01P8zaAd4mDfmBVVVsUP33u';
+const updated = '2023-07-28T04:28:20.909550Z';
+const redirectUris = 'www.customercourses.edx.com, www.customercourses.edx.stage.com';
+
+const data = {
+ name,
+ client_id: clientId,
+ client_secret: clientSecret,
+ redirect_uris: redirectUris,
+ updated,
+};
+const regenerationData = {
+ ...data,
+ redirect_uris: redirectUris,
+};
+const copiedData = {
+ ...data,
+ api_client_documentation: API_CLIENT_DOCUMENTATION,
+};
+
+describe('API Credentials Tab', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const basicProps = {
+ enterpriseId: 'test-enterprise-uuid',
+ };
+
+ const enterpriseId = 'test-enterprise-uuid';
+
+ test('renders zero state page when having no api credentials', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ const mockCreateFn = jest.spyOn(LmsApiService, 'createNewAPICredentials');
+ mockFetchFn.mockRejectedValue();
+ mockCreateFn.mockResolvedValue();
+
+ render(
+
+
+ ,
+ );
+ expect(screen.getByText('API credentials')).toBeInTheDocument();
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+ expect(screen.getByText("You don't have API credentials yet.")).toBeInTheDocument();
+ expect(screen.queryByText('Help Center: EdX Enterprise API Guide')).toBeInTheDocument();
+ const helpLink = screen.getByText('Help Center: EdX Enterprise API Guide');
+ expect(helpLink.getAttribute('href')).toBe(HELP_CENTER_API_GUIDE);
+ const serviceLink = screen.getByText('edX API terms of service');
+ expect(serviceLink.getAttribute('href')).toBe(API_TERMS_OF_SERVICE);
+
+ expect(screen.getByText('Generate API Credentials').toBeInTheDocument);
+ userEvent.click(screen.getByText('Generate API Credentials'));
+ await waitFor(() => expect(mockCreateFn).toHaveBeenCalled());
+ });
+ test('renders api credentials page when having existing api credentials', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockResolvedValue({ data });
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+ await waitFor(() => expect(screen.getByText(name)).toBeInTheDocument);
+
+ expect(screen.getByText(name).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client ID: ${clientId}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client secret: ${clientSecret}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client documentation: ${API_CLIENT_DOCUMENTATION}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Last generated on: ${updated}` }).toBeInTheDocument);
+ const link = screen.getByText('contact Enterprise Customer Support.');
+ expect(link.getAttribute('href')).toBe(HELP_CENTER_LINK);
+ });
+ test('renders error stage while creating new api credentials through clicking generation button', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockRejectedValue();
+ const mockCreatFn = jest.spyOn(LmsApiService, 'createNewAPICredentials');
+ mockCreatFn.mockRejectedValue();
+
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+ userEvent.click(screen.getByText('Generate API Credentials'));
+ await waitFor(() => { expect(mockCreatFn).toHaveBeenCalled(); });
+ expect(
+ screen.getByText(
+ 'Something went wrong while generating your credentials. Please try again. If the issue continues, contact Enterprise Customer Support.',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('renders api credentials page after successfully creating api credentials through clicking generation button', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockRejectedValue();
+ const mockCreatFn = jest.spyOn(LmsApiService, 'createNewAPICredentials');
+ mockCreatFn.mockResolvedValue({ data });
+ const writeText = jest.fn();
+ Object.assign(navigator, {
+ clipboard: {
+ writeText,
+ },
+ });
+ const jsonString = JSON.stringify(copiedData);
+ navigator.clipboard.writeText.mockResolvedValue(jsonString);
+
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+
+ userEvent.click(screen.getByText('Generate API Credentials'));
+ await waitFor(() => expect(mockCreatFn).toHaveBeenCalled());
+ expect(screen.getByText('API credentials successfully generated')).toBeInTheDocument();
+ const closeButton = screen.getByLabelText('Close');
+ userEvent.click(closeButton);
+ await waitFor(() => {
+ expect(screen.queryByText('API credentials successfully generated')).not.toBeInTheDocument();
+ });
+
+ expect(screen.getByRole('heading', { name: `Application name: ${name}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client ID: ${clientId}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client secret: ${clientSecret}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client documentation: ${API_CLIENT_DOCUMENTATION}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Last generated on: ${updated}` }).toBeInTheDocument);
+
+ const copyBtn = screen.getByText('Copy credentials to clipboard');
+ userEvent.click(copyBtn);
+ await waitFor(() => expect(navigator.clipboard.writeText).toHaveBeenCalledWith(jsonString));
+ await waitFor(() => expect(screen.getByText('Copied Successfully')).toBeInTheDocument());
+ });
+ test('renders error message when failing to copying api credentials to clipboard', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockRejectedValue();
+ const mockCreatFn = jest.spyOn(LmsApiService, 'createNewAPICredentials');
+ mockCreatFn.mockResolvedValue({ data });
+ const writeText = jest.fn();
+ Object.assign(navigator, {
+ clipboard: {
+ writeText,
+ },
+ });
+ const jsonString = JSON.stringify(copiedData);
+ navigator.clipboard.writeText.mockRejectedValue();
+
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+
+ userEvent.click(screen.getByText('Generate API Credentials'));
+ await waitFor(() => expect(mockCreatFn).toHaveBeenCalled());
+ const copyBtn = screen.getByText('Copy credentials to clipboard');
+ userEvent.click(copyBtn);
+ await waitFor(() => expect(navigator.clipboard.writeText).toHaveBeenCalledWith(jsonString));
+ await waitFor(() => expect(screen.getByText('Cannot copied to the clipboard')).toBeInTheDocument());
+ });
+ test('renders api credentials page after successfully regenerating api credentials', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockResolvedValue({ data });
+ const mockPatchFn = jest.spyOn(LmsApiService, 'regenerateAPICredentials');
+ mockPatchFn.mockResolvedValue({ data: regenerationData });
+
+ render(
+
+
+ ,
+ );
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+ const input = screen.getByTestId('form-control');
+ expect(input).toHaveValue('');
+ userEvent.type(input, redirectUris);
+ await waitFor(() => expect(input).toHaveValue(redirectUris));
+ const button = screen.getByText('Regenerate API Credentials');
+ userEvent.click(button);
+
+ await waitFor(() => expect(screen.getByText('Regenerate API credentials?')).toBeInTheDocument());
+ const confirmedButton = screen.getByText('Regenerate');
+ userEvent.click(confirmedButton);
+ await waitFor(() => {
+ expect(mockPatchFn).toHaveBeenCalledWith(redirectUris, enterpriseId);
+ });
+ expect(screen.getByRole('heading', { name: `Application name: ${name}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client ID: ${clientId}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client secret: ${clientSecret}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `API client documentation: ${API_CLIENT_DOCUMENTATION}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Last generated on: ${updated}` }).toBeInTheDocument);
+ expect(screen.queryByText('Something went wrong while generating your credentials. Please try again. If the issue continues, contact Enterprise Customer Support.'))
+ .not.toBeInTheDocument();
+ });
+ test('renders error state when failing to regenerating api credentials', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockResolvedValue({ data });
+ const mockPatchFn = jest.spyOn(LmsApiService, 'regenerateAPICredentials');
+ mockPatchFn.mockRejectedValue();
+
+ render(
+
+
+ ,
+ );
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+ const input = screen.getByTestId('form-control');
+ expect(input).toHaveValue('');
+ userEvent.type(input, redirectUris);
+ await waitFor(() => expect(input).toHaveValue(redirectUris));
+ const button = screen.getByText('Regenerate API Credentials');
+ userEvent.click(button);
+
+ await waitFor(() => expect(screen.getByText('Regenerate API credentials?')).toBeInTheDocument());
+ const confirmedButton = screen.getByText('Regenerate');
+ userEvent.click(confirmedButton);
+ await waitFor(() => {
+ expect(mockPatchFn).toHaveBeenCalledWith(redirectUris, enterpriseId);
+ });
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByText('Something went wrong while generating your credentials. Please try again. If the issue continues, contact Enterprise Customer Support.'))
+ .toBeInTheDocument();
+ });
+ test('renders api credentials when canceling regenerating api credentials', async () => {
+ const mockFetchFn = jest.spyOn(LmsApiService, 'fetchAPICredentials');
+ mockFetchFn.mockResolvedValue({ data });
+ const mockPatchFn = jest.spyOn(LmsApiService, 'regenerateAPICredentials');
+ mockPatchFn.mockResolvedValue({ data: regenerationData });
+
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
+ const input = screen.getByTestId('form-control');
+ expect(input).toHaveValue('');
+ userEvent.type(input, redirectUris);
+ await waitFor(() => expect(input).toHaveValue(redirectUris));
+ const button = screen.getByText('Regenerate API Credentials');
+ userEvent.click(button);
+
+ await waitFor(() => expect(screen.getByText('Regenerate API credentials?')).toBeInTheDocument());
+ const cancelButton = screen.getByText('Cancel');
+ userEvent.click(cancelButton);
+ await waitFor(() => {
+ expect(mockPatchFn).not.toHaveBeenCalledWith(redirectUris, enterpriseId);
+ });
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ });
+});
diff --git a/src/components/settings/SettingsLMSTab/index.jsx b/src/components/settings/SettingsLMSTab/index.jsx
index 54c3c3fa1d..1da30d6a09 100644
--- a/src/components/settings/SettingsLMSTab/index.jsx
+++ b/src/components/settings/SettingsLMSTab/index.jsx
@@ -7,11 +7,12 @@ import PropTypes from 'prop-types';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import {
- Alert, Button, Hyperlink, Toast, Skeleton, useToggle,
+ Alert, Button, Toast, Skeleton, useToggle,
} from '@edx/paragon';
import { Add, Info } from '@edx/paragon/icons';
import { logError } from '@edx/frontend-platform/logging';
+import HelpCenterButton from '../HelpCenterButton';
import { camelCaseDictArray, getChannelMap } from '../../../utils';
import LMSConfigPage from './LMSConfigPage';
import ExistingLMSCardDeck from './ExistingLMSCardDeck';
@@ -149,13 +150,9 @@ const SettingsLMSTab = ({
return (
Learning Platform Integrations
-
+
Help Center: Integrations
-
+
{!configsLoading && !config && (
{
const [hasSSOConfig, setHasSSOConfig] = useState(false);
- const { FEATURE_SSO_SETTINGS_TAB, SETTINGS_PAGE_LMS_TAB, SETTINGS_PAGE_APPEARANCE_TAB } = features;
+ const {
+ FEATURE_SSO_SETTINGS_TAB, SETTINGS_PAGE_LMS_TAB,
+ SETTINGS_PAGE_APPEARANCE_TAB,
+ FEATURE_API_CREDENTIALS_TAB,
+ } = features;
const tab = useCurrentSettingsTab();
@@ -48,21 +54,6 @@ const SettingsTabs = ({
const tabArray = useMemo(() => {
const initialTabs = [];
- if (SETTINGS_PAGE_APPEARANCE_TAB) {
- initialTabs.push(
-
-
- ,
- );
- }
if (enableLearnerPortal) {
initialTabs.push(
,
);
}
+ if (SETTINGS_PAGE_APPEARANCE_TAB) {
+ initialTabs.push(
+
+
+ ,
+ );
+ }
+ if (FEATURE_API_CREDENTIALS_TAB && enableApiCredentialGeneration) {
+ initialTabs.push(
+
+
+ ,
+ );
+ }
return initialTabs;
}, [
FEATURE_SSO_SETTINGS_TAB,
+ FEATURE_API_CREDENTIALS_TAB,
SETTINGS_PAGE_APPEARANCE_TAB,
SETTINGS_PAGE_LMS_TAB,
enableIntegratedCustomerLearnerPortalSearch,
enableLearnerPortal,
enableLmsConfigurationsScreen,
enableSamlConfigurationScreen,
+ enableApiCredentialGeneration,
enableUniversalLink,
enterpriseId,
enterpriseSlug,
@@ -168,6 +190,7 @@ const mapStateToProps = state => {
enableLearnerPortal,
enableLmsConfigurationsScreen,
enableSamlConfigurationScreen,
+ enableApiCredentialGeneration,
enableUniversalLink,
identityProvider,
enterpriseBranding,
@@ -180,6 +203,7 @@ const mapStateToProps = state => {
enableLearnerPortal,
enableLmsConfigurationsScreen,
enableSamlConfigurationScreen,
+ enableApiCredentialGeneration,
enableUniversalLink,
identityProvider,
enterpriseBranding,
@@ -202,6 +226,7 @@ SettingsTabs.propTypes = {
enableLearnerPortal: PropTypes.bool.isRequired,
enableLmsConfigurationsScreen: PropTypes.bool.isRequired,
enableSamlConfigurationScreen: PropTypes.bool.isRequired,
+ enableApiCredentialGeneration: PropTypes.bool.isRequired,
enableUniversalLink: PropTypes.bool.isRequired,
identityProvider: PropTypes.string,
updatePortalConfiguration: PropTypes.func.isRequired,
diff --git a/src/components/settings/data/constants.js b/src/components/settings/data/constants.js
index 02edae89e9..117636d3df 100644
--- a/src/components/settings/data/constants.js
+++ b/src/components/settings/data/constants.js
@@ -5,11 +5,13 @@ const ACCESS_TAB = 'access';
const LMS_TAB = 'lms';
const SSO_TAB = 'sso';
const APPEARANCE_TAB = 'appearance';
+const API_CREDENTIALS_TAB = 'api_credentials';
const ACCESS_TAB_LABEL = 'Configure Access';
const LMS_TAB_LABEL = 'Learning Platform';
const SSO_TAB_LABEL = 'Single Sign On (SSO)';
const APPEARANCE_TAB_LABEL = 'Portal Appearance';
+const API_CREDENTIALS_TAB_LABEL = 'API Credentials';
export const HELP_CENTER_LINK = 'https://business-support.edx.org/hc/en-us/categories/360000368453-Integrations';
export const HELP_CENTER_BLACKBOARD = 'https://business-support.edx.org/hc/en-us/sections/4405096719895-Blackboard';
@@ -18,11 +20,16 @@ export const HELP_CENTER_CORNERSTONE = 'https://business-support.edx.org/hc/en-u
export const HELP_CENTER_DEGREED = 'https://business-support.edx.org/hc/en-us/sections/360000868494-Degreed';
export const HELP_CENTER_MOODLE = 'https://business-support.edx.org/hc/en-us/sections/1500002758722-Moodle';
export const HELP_CENTER_SAP = 'https://business-support.edx.org/hc/en-us/sections/360000868534-SuccessFactors';
+export const HELP_CENTER_API_GUIDE = 'https://edx-enterprise-api.readthedocs.io/en/latest/index.html';
export const HELP_CENTER_SAML_LINK = 'https://business-support.edx.org/hc/en-us/articles/360005421073-5-Implementing-Single-Sign-on-SSO-with-edX';
export const HELP_CENTER_SAP_IDP_LINK = 'https://business-support.edx.org/hc/en-us/articles/360005205314';
export const HELP_CENTER_BRANDING_LINK = 'https://business-support.edx.org/hc/en-us/sections/8739219372183';
+export const API_CLIENT_DOCUMENTATION = 'https://edx-enterprise-api.readthedocs.io/en/latest/index.html';
+export const API_TERMS_OF_SERVICE = 'https://courses.edx.org/api-admin/terms-of-service/';
+export const ENTERPRISE_CUSTOMER_SUPPORT_EMAIL = 'enterprise-support@edx.org';
+
export const ACTIVATE_TOAST_MESSAGE = 'Learning platform integration successfully activated.';
export const DELETE_TOAST_MESSAGE = 'Learning platform integration successfully removed.';
export const INACTIVATE_TOAST_MESSAGE = 'Learning platform integration successfully disabled.';
@@ -58,6 +65,7 @@ export const SETTINGS_TABS_VALUES = {
[LMS_TAB]: LMS_TAB,
[SSO_TAB]: SSO_TAB,
[APPEARANCE_TAB]: APPEARANCE_TAB,
+ [API_CREDENTIALS_TAB]: API_CREDENTIALS_TAB,
};
/**
@@ -68,6 +76,7 @@ export const SETTINGS_TAB_LABELS = {
[LMS_TAB]: LMS_TAB_LABEL,
[SSO_TAB]: SSO_TAB_LABEL,
[APPEARANCE_TAB]: APPEARANCE_TAB_LABEL,
+ [API_CREDENTIALS_TAB]: API_CREDENTIALS_TAB_LABEL,
};
/** Default tab when no parameter is given */
diff --git a/src/components/settings/settings.scss b/src/components/settings/settings.scss
index 0dca7c30bc..d91246002c 100644
--- a/src/components/settings/settings.scss
+++ b/src/components/settings/settings.scss
@@ -69,7 +69,7 @@
.existing-lms-card {
width: 70rem !important;
- .pgn__card-header .pgn__card-header-content{
+ .pgn__card-header .pgn__card-header-content {
margin-top: 0 !important;
justify-content: center;
}
@@ -160,4 +160,15 @@
.stepper-modal .pgn__modal-header {
border-bottom: solid 7px;
-}
\ No newline at end of file
+}
+
+.warning-icon {
+ color: #F0CC00;
+}
+
+.api-cred-fields {
+ span {
+ font-weight: normal;
+ margin-left: 0.25rem;
+ }
+}
diff --git a/src/components/settings/tests/SettingsTabs.test.jsx b/src/components/settings/tests/SettingsTabs.test.jsx
index 4c91a6d47b..7dff9d0a67 100644
--- a/src/components/settings/tests/SettingsTabs.test.jsx
+++ b/src/components/settings/tests/SettingsTabs.test.jsx
@@ -20,6 +20,7 @@ import '@testing-library/jest-dom/extend-expect';
const ACCESS_MOCK_CONTENT = 'access';
const LMS_MOCK_CONTENT = 'lms';
const SSO_MOCK_CONTENT = 'sso';
+const API_CREDENTIALS_CONTENT = 'credentials';
jest.mock('../../../data/services/LmsApiService', () => ({
updateEnterpriseCustomerBranding: jest.fn(),
@@ -46,6 +47,13 @@ jest.mock(
},
);
+jest.mock(
+ '../SettingsApiCredentialsTab/',
+ () => function SettingsAccessTab() {
+ return {API_CREDENTIALS_CONTENT}
;
+ },
+);
+
const enterpriseId = 'test-enterprise';
const initialStore = {
portalConfiguration: {
@@ -116,4 +124,10 @@ describe(' ', () => {
await act(async () => { userEvent.click(accessTab); });
expect(screen.queryByText(ACCESS_MOCK_CONTENT)).toBeTruthy();
});
+
+ test('Api credentials tab is not rendered if FEATURE_API_CREDENTIALS_TAB = false', () => {
+ features.FEATURE_API_CREDENTIALS_TAB = false;
+ render( );
+ expect(screen.queryByText(API_CREDENTIALS_CONTENT)).not.toBeInTheDocument();
+ });
});
diff --git a/src/config/index.js b/src/config/index.js
index 663f190723..e83f03cd54 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -52,6 +52,7 @@ const features = {
SETTINGS_PAGE_APPEARANCE_TAB: process.env.FEATURE_SETTINGS_PAGE_APPEARANCE_TAB || hasFeatureFlagEnabled('SETTINGS_PAGE_APPEARANCE_TAB'),
FEATURE_SSO_SETTINGS_TAB: process.env.FEATURE_SSO_SETTINGS_TAB || hasFeatureFlagEnabled('SSO_SETTINGS_TAB'),
FEATURE_INTEGRATION_REPORTING: process.env.FEATURE_INTEGRATION_REPORTING || hasFeatureFlagEnabled('FEATURE_INTEGRATION_REPORTING'),
+ FEATURE_API_CREDENTIALS_TAB: process.env.FEATURE_API_CREDENTIALS_TAB || hasFeatureFlagEnabled('FEATURE_API_CREDENTIALS_TAB'),
SUBSCRIPTION_LPR: process.env.SUBSCRIPTION_LPR || hasFeatureFlagEnabled('SUBSCRIPTION_LPR'),
AUTH0_SELF_SERVICE_INTEGRATION: process.env.AUTH0_SELF_SERVICE_INTEGRATION || hasFeatureFlagEnabled('AUTH0_SELF_SERVICE_INTEGRATION'),
};
diff --git a/src/data/images/ZeroState.svg b/src/data/images/ZeroState.svg
new file mode 100644
index 0000000000..ffbdc2813d
--- /dev/null
+++ b/src/data/images/ZeroState.svg
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/data/reducers/portalConfiguration.js b/src/data/reducers/portalConfiguration.js
index 581e3dfcac..e2f5fbc961 100644
--- a/src/data/reducers/portalConfiguration.js
+++ b/src/data/reducers/portalConfiguration.js
@@ -25,6 +25,7 @@ const initialState = {
enableLearnerPortal: false,
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
+ enableApiCredentialGeneration: false,
};
const portalConfiguration = (state = initialState, action) => {
@@ -56,6 +57,7 @@ const portalConfiguration = (state = initialState, action) => {
enableLmsConfigurationsScreen: action.payload.data.enable_portal_lms_configurations_screen,
enableUniversalLink: action.payload.data.enable_universal_link,
enablePortalLearnerCreditManagementScreen: action.payload.data.enable_portal_learner_credit_management_screen,
+ enableApiCredentialGeneration: action.payload.data.enable_generation_of_api_credentials,
};
case FETCH_PORTAL_CONFIGURATION_FAILURE:
return {
@@ -78,6 +80,7 @@ const portalConfiguration = (state = initialState, action) => {
enableLmsConfigurationsScreen: false,
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
+ enableApiCredentialGeneration: false,
};
case CLEAR_PORTAL_CONFIGURATION:
return {
@@ -98,6 +101,7 @@ const portalConfiguration = (state = initialState, action) => {
enableLmsConfigurationsScreen: false,
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
+ enableApiCredentialGeneration: false,
};
case UPDATE_PORTAL_CONFIGURATION:
return {
diff --git a/src/data/reducers/portalConfiguration.test.js b/src/data/reducers/portalConfiguration.test.js
index dc91850d5e..f97e186346 100644
--- a/src/data/reducers/portalConfiguration.test.js
+++ b/src/data/reducers/portalConfiguration.test.js
@@ -18,6 +18,7 @@ const initialState = {
enableReportingConfigScreen: false,
enableSubscriptionManagementScreen: false,
enableIntegratedCustomerLearnerPortalSearch: false,
+ enableApiCredentialGeneration: false,
enableLearnerPortal: false,
enableSamlConfigurationScreen: false,
enableAnalyticsScreen: false,
@@ -50,6 +51,7 @@ const enterpriseData = {
enable_portal_lms_configurations_screen: true,
enable_universal_link: true,
enable_browse_and_request: true,
+ enable_generation_of_api_credentials: true,
};
describe('portalConfiguration reducer', () => {
@@ -78,6 +80,7 @@ describe('portalConfiguration reducer', () => {
enableLmsConfigurationsScreen: enterpriseData.enable_portal_lms_configurations_screen,
enableUniversalLink: enterpriseData.enable_universal_link,
enablePortalLearnerCreditManagementScreen: enterpriseData.enable_portal_learner_credit_management_screen,
+ enableApiCredentialGeneration: enterpriseData.enable_generation_of_api_credentials,
};
expect(portalConfiguration(undefined, {
type: FETCH_PORTAL_CONFIGURATION_SUCCESS,
diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js
index 8a2fb6c96e..eb4b3b67f2 100644
--- a/src/data/services/LmsApiService.js
+++ b/src/data/services/LmsApiService.js
@@ -35,6 +35,8 @@ class LmsApiService {
static enterpriseCustomerInviteKeyUrl = `${LmsApiService.baseUrl}/enterprise/api/v1/enterprise-customer-invite-key/`;
+ static apiCredentialsUrl = `${LmsApiService.baseUrl}/enterprise/api/v1/enterprise-customer-api-credentials/`;
+
static fetchEnterpriseList(options) {
const queryParams = new URLSearchParams({
page: 1,
@@ -329,6 +331,21 @@ class LmsApiService {
const url = `${LmsApiService.enterpriseCustomerUrl}${enterpriseUUID}/toggle_universal_link/`;
return LmsApiService.apiClient().patch(url, formData);
}
+
+ static fetchAPICredentials(enterpriseUUID) {
+ return LmsApiService.apiClient().get(`${LmsApiService.apiCredentialsUrl}${enterpriseUUID}/`);
+ }
+
+ static createNewAPICredentials(enterpriseUUID) {
+ return LmsApiService.apiClient().post(`${LmsApiService.apiCredentialsUrl}${enterpriseUUID}/`);
+ }
+
+ static regenerateAPICredentials(redirectURLs, enterpriseUUID) {
+ const requestData = {
+ redirect_uris: redirectURLs,
+ };
+ return LmsApiService.apiClient().put(`${LmsApiService.apiCredentialsUrl}${enterpriseUUID}/regenerate_credentials`, requestData);
+ }
}
export default LmsApiService;
From 22f7c2cd705dd08596c35827a891ce8d3b5a0f27 Mon Sep 17 00:00:00 2001
From: Alex Dusenbery
Date: Thu, 14 Sep 2023 14:50:59 -0400
Subject: [PATCH 022/124] fix: remove broken usage of useCache from
getSubsidyRequestConfiguration
---
.gitignore | 1 +
src/components/subsidy-requests/data/hooks.js | 29 +++++++++----------
.../subsidy-requests/data/tests/hooks.test.js | 4 +--
.../services/EnterpriseAccessApiService.js | 6 ++--
.../tests/EnterpriseAccessApiService.test.js | 1 -
5 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/.gitignore b/.gitignore
index 2f81fc6ff0..32335845d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ dist/
# emacs
*~
+.projectile
# edx
.env.private
diff --git a/src/components/subsidy-requests/data/hooks.js b/src/components/subsidy-requests/data/hooks.js
index d0391eb389..7eeab49392 100644
--- a/src/components/subsidy-requests/data/hooks.js
+++ b/src/components/subsidy-requests/data/hooks.js
@@ -57,21 +57,18 @@ export const useSubsidyRequestConfiguration = ({
}
}, [enterpriseId]);
- const loadSubsidyRequestConfiguration = useCallback(
- async ({ clearCacheEntry = false } = { clearCacheEntry: false }) => {
- try {
- const response = await EnterpriseAccessApiService.getSubsidyRequestConfiguration(
- { enterpriseId, clearCacheEntry },
- );
- const customerConfiguration = camelCaseObject(response.data);
- setSubsidyRequestConfiguration(customerConfiguration);
- } catch (error) {
- logError(error);
- throw error;
- }
- },
- [enterpriseId],
- );
+ const loadSubsidyRequestConfiguration = useCallback(async () => {
+ try {
+ const response = await EnterpriseAccessApiService.getSubsidyRequestConfiguration(
+ { enterpriseId },
+ );
+ const customerConfiguration = camelCaseObject(response.data);
+ setSubsidyRequestConfiguration(customerConfiguration);
+ } catch (error) {
+ logError(error);
+ throw error;
+ }
+ }, [enterpriseId]);
useEffect(() => {
if (!enterpriseId) {
@@ -111,7 +108,7 @@ export const useSubsidyRequestConfiguration = ({
enterpriseId,
options,
);
- loadSubsidyRequestConfiguration({ clearCacheEntry: true });
+ loadSubsidyRequestConfiguration();
} catch (err) {
logError(err);
throw err;
diff --git a/src/components/subsidy-requests/data/tests/hooks.test.js b/src/components/subsidy-requests/data/tests/hooks.test.js
index 435bb1e26b..448025e8e0 100644
--- a/src/components/subsidy-requests/data/tests/hooks.test.js
+++ b/src/components/subsidy-requests/data/tests/hooks.test.js
@@ -64,7 +64,7 @@ describe('useSubsidyRequestConfiguration', () => {
await waitForNextUpdate();
expect(EnterpriseAccessApiService.getSubsidyRequestConfiguration).toHaveBeenCalledWith(
- { clearCacheEntry: false, enterpriseId: TEST_ENTERPRISE_UUID },
+ { enterpriseId: TEST_ENTERPRISE_UUID },
);
expect(result.current.subsidyRequestConfiguration).toEqual(
camelCaseObject(TEST_CONFIGURATION_RESPONSE.data),
@@ -230,7 +230,7 @@ describe('useSubsidyRequestConfiguration', () => {
});
expect(EnterpriseAccessApiService.getSubsidyRequestConfiguration).toHaveBeenCalledWith(
- { clearCacheEntry: true, enterpriseId: TEST_ENTERPRISE_UUID },
+ { enterpriseId: TEST_ENTERPRISE_UUID },
);
});
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index 98edbbbefd..380428686e 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -7,11 +7,9 @@ class EnterpriseAccessApiService {
static apiClient = getAuthenticatedHttpClient;
- static getSubsidyRequestConfiguration({ enterpriseId, clearCacheEntry = false }) {
+ static getSubsidyRequestConfiguration({ enterpriseId }) {
const url = `${EnterpriseAccessApiService.baseUrl}/customer-configurations/${enterpriseId}/`;
- return EnterpriseAccessApiService.apiClient({
- useCache: configuration.USE_API_CACHE,
- }).get(url, { clearCacheEntry });
+ return EnterpriseAccessApiService.apiClient().get(url);
}
static createSubsidyRequestConfiguration({
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index e6e21b833c..485bf7db90 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -103,7 +103,6 @@ describe('EnterpriseAccessApiService', () => {
EnterpriseAccessApiService.getSubsidyRequestConfiguration({ enterpriseId: mockEnterpriseUUID });
expect(axios.get).toBeCalledWith(
`${enterpriseAccessBaseUrl}/api/v1/customer-configurations/${mockEnterpriseUUID}/`,
- { clearCacheEntry: false },
);
});
From eb38ec1dfa4a2cd75754896e7c9593fff4b45041 Mon Sep 17 00:00:00 2001
From: Brian Beggs
Date: Mon, 18 Sep 2023 16:15:52 -0400
Subject: [PATCH 023/124] chore: update max allowed highlights per set and max
number of highlight sets
---
src/components/ContentHighlights/data/constants.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/ContentHighlights/data/constants.js b/src/components/ContentHighlights/data/constants.js
index a0a4ebab64..9bc344f578 100644
--- a/src/components/ContentHighlights/data/constants.js
+++ b/src/components/ContentHighlights/data/constants.js
@@ -43,10 +43,10 @@ export const TAB_TITLES = {
export const MAX_HIGHLIGHT_TITLE_LENGTH = 60;
// Max highlight sets per enteprise curation
-export const MAX_HIGHLIGHT_SETS_PER_ENTERPRISE_CURATION = 8;
+export const MAX_HIGHLIGHT_SETS_PER_ENTERPRISE_CURATION = 12;
// Max number of content items per highlight set
-export const MAX_CONTENT_ITEMS_PER_HIGHLIGHT_SET = 12;
+export const MAX_CONTENT_ITEMS_PER_HIGHLIGHT_SET = 24;
// Max number of content items displayed from search results
export const MAX_PAGE_SIZE = 24;
From f4328dc45d45013a3006776cc52ced7e046efbdc Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 21 Sep 2023 09:05:10 -0400
Subject: [PATCH 024/124] feat: expose `enterpriseFeatures` from API in
`portalConfiguration` (#1039)
---
src/data/actions/portalConfiguration.js | 9 ++--
src/data/actions/portalConfiguration.test.js | 42 +++++++++++++------
src/data/reducers/portalConfiguration.js | 4 ++
src/data/reducers/portalConfiguration.test.js | 5 ++-
src/data/services/LmsApiService.js | 6 ++-
5 files changed, 49 insertions(+), 17 deletions(-)
diff --git a/src/data/actions/portalConfiguration.js b/src/data/actions/portalConfiguration.js
index a578072bb1..ec342db0c4 100644
--- a/src/data/actions/portalConfiguration.js
+++ b/src/data/actions/portalConfiguration.js
@@ -12,9 +12,12 @@ const fetchPortalConfigurationRequest = () => ({
type: FETCH_PORTAL_CONFIGURATION_REQUEST,
});
-const fetchPortalConfigurationSuccess = data => ({
+const fetchPortalConfigurationSuccess = response => ({
type: FETCH_PORTAL_CONFIGURATION_SUCCESS,
- payload: { data },
+ payload: {
+ data: response.data,
+ enterpriseFeatures: response.enterpriseFeatures,
+ },
});
const fetchPortalConfigurationFailure = error => ({
@@ -34,7 +37,7 @@ const fetchPortalConfiguration = slug => (
dispatch(fetchPortalConfigurationRequest());
return LmsApiService.fetchEnterpriseBySlug(slug)
.then((response) => {
- dispatch(fetchPortalConfigurationSuccess(response.data));
+ dispatch(fetchPortalConfigurationSuccess(response));
})
.catch((error) => {
logError(error);
diff --git a/src/data/actions/portalConfiguration.test.js b/src/data/actions/portalConfiguration.test.js
index 2cceed3020..06cf580c25 100644
--- a/src/data/actions/portalConfiguration.test.js
+++ b/src/data/actions/portalConfiguration.test.js
@@ -1,5 +1,6 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
import { clearPortalConfiguration, fetchPortalConfiguration } from './portalConfiguration';
import {
@@ -14,6 +15,18 @@ jest.mock('@edx/frontend-platform/logging');
const mockStore = configureMockStore([thunk]);
+const mockEnterpriseCustomer = {
+ uuid: 'd749b244-dceb-47bb-951c-5184a6e6d36a',
+ name: 'Test Enterprise',
+ slug: 'test-enterprise',
+ branding_configuration: {
+ enterprise_customer: 'd749b244-dceb-47bb-951c-5184a6e6d36a',
+ enterprise_slug: 'test-enterprise',
+ logo: 'https://s3...',
+ },
+};
+const mockEnterpriseFeatures = { feature_a: true };
+
describe('actions', () => {
afterEach(() => {
axiosMock.reset();
@@ -21,20 +34,19 @@ describe('actions', () => {
it('dispatches success action after fetching portalConfiguration', () => {
const slug = 'test-enterprise';
- const responseData = {
- uuid: 'd749b244-dceb-47bb-951c-5184a6e6d36a',
- name: 'Test Enterprise',
- slug: 'test-enterprise',
- branding_configuration: {
- enterprise_customer: 'd749b244-dceb-47bb-951c-5184a6e6d36a',
- enterprise_slug: 'test-enterprise',
- logo: 'https://s3...',
- },
+ const mockResponseData = {
+ data: mockEnterpriseCustomer,
+ enterpriseFeatures: mockEnterpriseFeatures,
};
const expectedActions = [
- { type: FETCH_PORTAL_CONFIGURATION_REQUEST },
- { type: FETCH_PORTAL_CONFIGURATION_SUCCESS, payload: { data: responseData } },
+ {
+ type: FETCH_PORTAL_CONFIGURATION_REQUEST,
+ },
+ {
+ type: FETCH_PORTAL_CONFIGURATION_SUCCESS,
+ payload: { data: mockResponseData.data, enterpriseFeatures: camelCaseObject(mockEnterpriseFeatures) },
+ },
];
const store = mockStore();
const queryParams = new URLSearchParams({
@@ -43,7 +55,13 @@ describe('actions', () => {
enterprise_slug: slug,
});
axiosMock.onGet(`http://localhost:18000/enterprise/api/v1/enterprise-customer/dashboard_list/?${queryParams.toString()}`)
- .replyOnce(200, JSON.stringify({ results: [responseData] }));
+ .replyOnce(
+ 200,
+ JSON.stringify({
+ results: [mockResponseData.data],
+ enterprise_features: mockResponseData.enterpriseFeatures,
+ }),
+ );
return store.dispatch(fetchPortalConfiguration(slug)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
diff --git a/src/data/reducers/portalConfiguration.js b/src/data/reducers/portalConfiguration.js
index e2f5fbc961..9527e495ef 100644
--- a/src/data/reducers/portalConfiguration.js
+++ b/src/data/reducers/portalConfiguration.js
@@ -26,6 +26,7 @@ const initialState = {
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
enableApiCredentialGeneration: false,
+ enterpriseFeatures: {},
};
const portalConfiguration = (state = initialState, action) => {
@@ -58,6 +59,7 @@ const portalConfiguration = (state = initialState, action) => {
enableUniversalLink: action.payload.data.enable_universal_link,
enablePortalLearnerCreditManagementScreen: action.payload.data.enable_portal_learner_credit_management_screen,
enableApiCredentialGeneration: action.payload.data.enable_generation_of_api_credentials,
+ enterpriseFeatures: action.payload.enterpriseFeatures,
};
case FETCH_PORTAL_CONFIGURATION_FAILURE:
return {
@@ -81,6 +83,7 @@ const portalConfiguration = (state = initialState, action) => {
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
enableApiCredentialGeneration: false,
+ enterpriseFeatures: {},
};
case CLEAR_PORTAL_CONFIGURATION:
return {
@@ -102,6 +105,7 @@ const portalConfiguration = (state = initialState, action) => {
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
enableApiCredentialGeneration: false,
+ enterpriseFeatures: {},
};
case UPDATE_PORTAL_CONFIGURATION:
return {
diff --git a/src/data/reducers/portalConfiguration.test.js b/src/data/reducers/portalConfiguration.test.js
index f97e186346..f8d328f5ea 100644
--- a/src/data/reducers/portalConfiguration.test.js
+++ b/src/data/reducers/portalConfiguration.test.js
@@ -25,6 +25,7 @@ const initialState = {
enableLmsConfigurationsScreen: false,
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
+ enterpriseFeatures: {},
};
const enterpriseData = {
@@ -53,6 +54,7 @@ const enterpriseData = {
enable_browse_and_request: true,
enable_generation_of_api_credentials: true,
};
+const mockEnterpriseFeatures = { featureA: true };
describe('portalConfiguration reducer', () => {
it('has initial state', () => {
@@ -81,10 +83,11 @@ describe('portalConfiguration reducer', () => {
enableUniversalLink: enterpriseData.enable_universal_link,
enablePortalLearnerCreditManagementScreen: enterpriseData.enable_portal_learner_credit_management_screen,
enableApiCredentialGeneration: enterpriseData.enable_generation_of_api_credentials,
+ enterpriseFeatures: mockEnterpriseFeatures,
};
expect(portalConfiguration(undefined, {
type: FETCH_PORTAL_CONFIGURATION_SUCCESS,
- payload: { data: enterpriseData },
+ payload: { data: enterpriseData, enterpriseFeatures: mockEnterpriseFeatures },
})).toEqual(expected);
});
diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js
index eb4b3b67f2..4cff4834d1 100644
--- a/src/data/services/LmsApiService.js
+++ b/src/data/services/LmsApiService.js
@@ -1,4 +1,6 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+
import { configuration } from '../../config';
import generateFormattedStatusUrl from './apiServiceUtils';
@@ -53,8 +55,10 @@ class LmsApiService {
.then((response) => {
const { data } = response;
const results = data?.results;
+ const enterpriseFeatures = camelCaseObject(data?.enterprise_features);
return {
- data: results?.length && results[0],
+ data: results?.[0],
+ enterpriseFeatures,
};
});
}
From e5edf15ce7e0d73d845c5c1fb00f0df9028ced4e Mon Sep 17 00:00:00 2001
From: Marlon Keating <322346+marlonkeating@users.noreply.github.com>
Date: Thu, 21 Sep 2023 14:00:44 -0700
Subject: [PATCH 025/124] feat: New SSO workflow skeleton (#1032)
* feat: New SSO workflow skeleton
fix: Make service provider metadata button primary color
feat: Add non-SAP fields to SSO Configure page
feat: Show different SSO metadata inputs based on radio selection
test: Add test for SSO metadata input hiding
feat: Add back buttons to sso config workflow
chore: rename function
* test: New sso flow cancel button test
---
src/components/forms/FormContextWrapper.tsx | 3 +-
src/components/forms/FormWorkflow.tsx | 25 +++-
src/components/forms/ValidatedFormControl.tsx | 3 +
src/components/forms/ValidatedFormRadio.tsx | 3 +-
.../settings/SettingsLMSTab/LMSConfigPage.jsx | 1 +
.../LMSConfigs/SAP/SAPConfigEnablePage.tsx | 1 +
.../tests/BlackboardConfig.test.tsx | 1 +
.../tests/CanvasConfig.test.tsx | 1 +
.../tests/DegreedConfig.test.tsx | 1 +
.../settings/SettingsSSOTab/NewSSOStepper.jsx | 30 +++-
.../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 59 ++++++++
.../steps/NewSSOConfigAuthorizeStep.tsx | 38 +++++
.../steps/NewSSOConfigConfigureStep.tsx | 130 ++++++++++++++++++
.../steps/NewSSOConfigConfirmStep.tsx | 53 +++++++
.../steps/NewSSOConfigConnectStep.tsx | 88 ++++++++++++
.../tests/NewSSOConfigForm.test.jsx | 130 ++++++++++++++++--
src/components/test/testUtils.jsx | 4 +-
17 files changed, 554 insertions(+), 17 deletions(-)
create mode 100644 src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
create mode 100644 src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
create mode 100644 src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
create mode 100644 src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx
create mode 100644 src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
diff --git a/src/components/forms/FormContextWrapper.tsx b/src/components/forms/FormContextWrapper.tsx
index cec05e1483..a6d745f896 100644
--- a/src/components/forms/FormContextWrapper.tsx
+++ b/src/components/forms/FormContextWrapper.tsx
@@ -8,6 +8,7 @@ import {
type FormWrapperProps = FormWorkflowProps & { formData: FormConfigData };
const FormContextWrapper = ({
+ workflowTitle,
formWorkflowConfig,
onClickOut,
formData,
@@ -32,7 +33,7 @@ const FormContextWrapper = ({
>
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index f6c2a9d6da..d3bb072534 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -53,6 +53,8 @@ export type FormWorkflowStep = {
errHandler: FormWorkflowErrorHandler
) => Promise;
nextButtonConfig: (FormData: FormData) => FormWorkflowButtonConfig;
+ showBackButton?: boolean;
+ showCancelButton?: boolean;
};
export type FormWorkflowConfig = {
@@ -61,14 +63,16 @@ export type FormWorkflowConfig = {
};
export type FormWorkflowProps = {
+ workflowTitle: string;
formWorkflowConfig: FormWorkflowConfig;
- onClickOut: (edited: boolean, msg?: string) => null;
+ onClickOut: (() => void) | ((edited?: boolean, msg?: string) => null);
dispatch: Dispatch;
isStepperOpen: boolean;
};
// Modal container for multi-step forms
const FormWorkflow = ({
+ workflowTitle,
formWorkflowConfig,
onClickOut,
isStepperOpen,
@@ -150,6 +154,15 @@ const FormWorkflow = ({
}
};
+ const onBack = () => {
+ if (step?.index !== undefined) {
+ const previousStep: number = step.index - 1;
+ if (previousStep >= 0) {
+ dispatch(setStepAction({ step: formWorkflowConfig.steps[previousStep] }));
+ }
+ }
+ };
+
const stepBody = (currentStep: FormWorkflowStep) => {
if (currentStep) {
const FormComponent: DynamicComponent = currentStep?.formComponent;
@@ -175,6 +188,10 @@ const FormWorkflow = ({
}
}, [formFields]);
+ // Show back button only if showBackButton === true
+ const showBackButton = (step?.index !== undefined) && (step.index > 0) && step.showBackButton;
+ // Show cancel button by default
+ const showCancelButton = step?.showCancelButton === undefined || step?.showCancelButton;
return (
<>
({
await step?.saveChanges(formFields as FormConfigData, setFormError);
onClickOut(true, SUBMIT_TOAST_MESSAGE);
}
+ onClickOut(false, 'No changes saved');
}}
/>
{formWorkflowConfig.steps && (
({
Help Center: Integrations
- Cancel
+ {showCancelButton && Cancel }
+ {showBackButton && Back }
{nextButtonConfig && (
{nextButtonConfig.buttonText}
diff --git a/src/components/forms/ValidatedFormControl.tsx b/src/components/forms/ValidatedFormControl.tsx
index df996ca0f1..86e816d569 100644
--- a/src/components/forms/ValidatedFormControl.tsx
+++ b/src/components/forms/ValidatedFormControl.tsx
@@ -7,12 +7,15 @@ import { Form } from '@edx/paragon';
import { setFormFieldAction } from './data/actions';
import { useFormContext } from './FormContext';
+// TODO: Get from Paragon
type InheritedParagonControlProps = {
autoFocus?: boolean;
className?: string;
type: string;
+ as?: string;
maxLength?: number;
floatingLabel?: string;
+ rows?: number;
};
export type ValidatedFormControlProps = {
diff --git a/src/components/forms/ValidatedFormRadio.tsx b/src/components/forms/ValidatedFormRadio.tsx
index 5d8e48ad65..a37e6e0b8c 100644
--- a/src/components/forms/ValidatedFormRadio.tsx
+++ b/src/components/forms/ValidatedFormRadio.tsx
@@ -18,6 +18,7 @@ export type ValidatedFormRadioProps = {
fieldInstructions?: string;
label?: string;
options?: string[][];
+ isInline?: boolean;
} & InheritedParagonRadioProps;
const ValidatedFormRadio = (props: ValidatedFormRadioProps) => {
@@ -69,7 +70,7 @@ const ValidatedFormRadio = (props: ValidatedFormRadioProps) => {
{createOptions(formRadioProps.options)}
diff --git a/src/components/settings/SettingsLMSTab/LMSConfigPage.jsx b/src/components/settings/SettingsLMSTab/LMSConfigPage.jsx
index 38b6c75e9d..80cd1faac0 100644
--- a/src/components/settings/SettingsLMSTab/LMSConfigPage.jsx
+++ b/src/components/settings/SettingsLMSTab/LMSConfigPage.jsx
@@ -37,6 +37,7 @@ const LMSConfigPage = ({
return (
(
formId={formFieldNames.USER_TYPE}
label="SAP User Type"
options={[['User', 'user'], ['Admin', 'admin']]}
+ isInline
/>
diff --git a/src/components/settings/SettingsLMSTab/tests/BlackboardConfig.test.tsx b/src/components/settings/SettingsLMSTab/tests/BlackboardConfig.test.tsx
index ae98b38195..208aea0d34 100644
--- a/src/components/settings/SettingsLMSTab/tests/BlackboardConfig.test.tsx
+++ b/src/components/settings/SettingsLMSTab/tests/BlackboardConfig.test.tsx
@@ -76,6 +76,7 @@ function testBlackboardConfigSetup(formData) {
},
},
})}
+ workflowTitle="New learning platform integration"
onClickOut={mockOnClick}
formData={formData}
isStepperOpen
diff --git a/src/components/settings/SettingsLMSTab/tests/CanvasConfig.test.tsx b/src/components/settings/SettingsLMSTab/tests/CanvasConfig.test.tsx
index 2af5de1f0a..a9db65616f 100644
--- a/src/components/settings/SettingsLMSTab/tests/CanvasConfig.test.tsx
+++ b/src/components/settings/SettingsLMSTab/tests/CanvasConfig.test.tsx
@@ -73,6 +73,7 @@ function testCanvasConfigSetup(formData) {
},
},
})}
+ workflowTitle="New learning platform integration"
onClickOut={mockOnClick}
formData={formData}
isStepperOpen
diff --git a/src/components/settings/SettingsLMSTab/tests/DegreedConfig.test.tsx b/src/components/settings/SettingsLMSTab/tests/DegreedConfig.test.tsx
index 6e70a88f70..c804be2d14 100644
--- a/src/components/settings/SettingsLMSTab/tests/DegreedConfig.test.tsx
+++ b/src/components/settings/SettingsLMSTab/tests/DegreedConfig.test.tsx
@@ -78,6 +78,7 @@ function testDegreedConfigSetup(formData) {
},
},
})}
+ workflowTitle="New learning platform integration"
onClickOut={mockOnClick}
formData={formData}
isStepperOpen
diff --git a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
index 4db39d90e6..55ae8eb1f0 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
@@ -1,3 +1,31 @@
-const NewSSOStepper = () => null;
+import React, { useState, useContext } from 'react';
+import FormContextWrapper from '../../forms/FormContextWrapper';
+import { SSOConfigContext } from './SSOConfigContext';
+import SSOFormWorkflowConfig from './SSOFormWorkflowConfig';
+
+const NewSSOStepper = () => {
+ const {
+ setProviderConfig,
+ } = useContext(SSOConfigContext);
+ const [isStepperOpen, setIsStepperOpen] = useState(true);
+ const handleCloseWorkflow = () => {
+ setProviderConfig?.(null);
+ setIsStepperOpen(false);
+ };
+
+ return (isStepperOpen
+ && (
+
+
+
+ )
+ );
+};
export default NewSSOStepper;
diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
new file mode 100644
index 0000000000..dcaff6c46e
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
@@ -0,0 +1,59 @@
+import type { FormWorkflowStep } from '../../forms/FormWorkflow';
+import SSOConfigConnectStep from './steps/NewSSOConfigConnectStep';
+import SSOConfigConfigureStep from './steps/NewSSOConfigConfigureStep';
+import SSOConfigAuthorizeStep from './steps/NewSSOConfigAuthorizeStep';
+import SSOConfigConfirmStep from './steps/NewSSOConfigConfirmStep';
+
+type SSOConfigCamelCase = {};
+
+export const SSOFormWorkflowConfig = () => {
+ const placeHolderButton = (buttonName?: string) => () => ({
+ buttonText: buttonName || 'Next',
+ opensNewWindow: false,
+ onClick: () => {},
+ });
+
+ const steps: FormWorkflowStep[] = [
+ {
+ index: 0,
+ formComponent: SSOConfigConnectStep,
+ validations: [],
+ stepName: 'Connect',
+ nextButtonConfig: placeHolderButton(),
+ }, {
+ index: 1,
+ formComponent: SSOConfigConfigureStep,
+ validations: [],
+ stepName: 'Configure',
+ nextButtonConfig: placeHolderButton('Configure'),
+ showBackButton: true,
+ showCancelButton: false,
+ }, {
+ index: 2,
+ formComponent: SSOConfigAuthorizeStep,
+ validations: [],
+ stepName: 'Authorize',
+ nextButtonConfig: placeHolderButton(),
+ showBackButton: true,
+ showCancelButton: false,
+ }, {
+ index: 3,
+ formComponent: SSOConfigConfirmStep,
+ validations: [],
+ stepName: 'Confirm and Test',
+ nextButtonConfig: placeHolderButton('Finish'),
+ showBackButton: true,
+ showCancelButton: false,
+ },
+ ];
+
+ // Start at the first step
+ const getCurrentStep = () => steps[0];
+
+ return {
+ getCurrentStep,
+ steps,
+ };
+};
+
+export default SSOFormWorkflowConfig;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
new file mode 100644
index 0000000000..1d9fe823b2
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import {
+ Alert, Form, Hyperlink, Button, Row,
+} from '@edx/paragon';
+import { Info, Download } from '@edx/paragon/icons';
+
+const handleCheck = () => null;
+
+const SSOConfigAuthorizeStep = () => (
+ <>
+ Authorize edX as a Service Provider
+
+ Action required in a new window
+ Return to this window after completing the following steps in a new window to finish configuring your integration.
+
+
+
+ 1. Download the edX Service Provider metadata as an XML file:
+
+
+
+ edX Service Provider Metadata
+
+
+
+ 2. Launch a new window and upload the XML file to the list of
+ authorized SAML Service Providers on your Identity Provider's portal or website.
+
+
+ Return to this window and check the box once complete
+
+
+ I have authorized edX as a Service Provider
+
+ >
+);
+
+export default SSOConfigAuthorizeStep;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
new file mode 100644
index 0000000000..50cf5b5ba2
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
@@ -0,0 +1,130 @@
+import React from 'react';
+import {
+ Form, Container,
+} from '@edx/paragon';
+
+import ValidatedFormControl from '../../../forms/ValidatedFormControl';
+
+const SSOConfigConfigureStep = () => {
+ const renderBaseFields = () => (
+ <>
+ Enter user attributes
+
+ Please enter the SAML user attributes from your Identity Provider.
+ All attributes are space and case sensitive.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+ const renderSAPFields = () => (
+ <>
+ Enable learner account auto-registration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+
+ return (
+
+
+
+
+
+
+
+ {/* TODO: Render SAP fields selectively once logic is in place */}
+ {renderBaseFields()}
+ {renderSAPFields()}
+
+
+ );
+};
+
+export default SSOConfigConfigureStep;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx
new file mode 100644
index 0000000000..f43bdeeb71
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import {
+ Alert, Hyperlink, OverlayTrigger, Popover,
+} from '@edx/paragon';
+import { Info } from '@edx/paragon/icons';
+
+const IncognitoPopover = () => (
+
+
+ Steps to open a new window in incognito mode (also known as private mode)
+ may vary based on the browser you are using.
+ Review your browser's help documentation as needed.
+
+
+ )}
+ >
+ incognito window
+
+);
+
+const SSOConfigConfirmStep = () => (
+ <>
+ Wait for SSO configuration confirmation
+
+ Action required from email
+ Great news! You have completed the configuration steps, edX is actively configuring your SSO connection.
+ You will receive an email within about five minutes when the configuration is complete.
+ The email will include instructions for testing.
+
+
+ What to expect:
+
+ SSO configuration confirmation email.
+
+ Testing instructions involve copying and pasting a custom URL into an
+ A link back to the SSO Settings page
+
+
+
+
+ Select the "Finish" button below or close this form via the
+ "X" in the upper right corner while you wait for your
+ configuration email. Your SSO testing status will display on the following SSO settings screen.
+
+ >
+);
+
+export default SSOConfigConfirmStep;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
new file mode 100644
index 0000000000..4b66625d11
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import { Container, Dropzone, Form } from '@edx/paragon';
+
+import ValidatedFormRadio from '../../../forms/ValidatedFormRadio';
+import ValidatedFormControl from '../../../forms/ValidatedFormControl';
+import { FormContext, useFormContext } from '../../../forms/FormContext';
+
+const SSOConfigConnectStep = () => {
+ const fiveGbInBytes = 5368709120;
+ const ssoIdpOptions = [
+ ['Microsoft Azure Active Directory (Azure AD)', 'azure_ad'],
+ ['Google Workspace', 'google_workspace'],
+ ['Okta', 'okta'],
+ ['OneLogin', 'one_login'],
+ ['SAP SuccessFactors', 'sap_success_factors'],
+ ['Other', 'other'],
+ ];
+ const idpConnectOptions = [
+ ['Enter identity Provider Metadata URL', 'idp_metadata_url'],
+ ['Upload Identity Provider Metadata XML file', 'idp_metadata_xml'],
+ ];
+
+ const {
+ formFields,
+ }: FormContext = useFormContext();
+ const showUrlEntry = formFields?.idpConnectOption === 'idp_metadata_url';
+ const showXmlUpload = formFields?.idpConnectOption === 'idp_metadata_xml';
+
+ // TODO: Store uploaded XML data
+ const onUploadXml = () => null;
+
+ return (
+
+
+
+
+
+
+ Connect edX to your Identity Provider
+ Select a method to connect edX to your Identity Provider
+
+
+
+
+ {showUrlEntry && (
+
+
+
+ )}
+
+ {showXmlUpload
+ && (
+
+ )}
+
+
+ );
+};
+
+export default SSOConfigConnectStep;
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
index 03372c4fd5..4cc7cecc93 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
@@ -1,6 +1,7 @@
import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import userEvent from '@testing-library/user-event';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Provider } from 'react-redux';
import NewSSOConfigForm from '../NewSSOConfigForm';
@@ -13,6 +14,7 @@ import {
INVALID_ODATA_API_TIMEOUT_INTERVAL, INVALID_SAPSF_OAUTH_ROOT_URL, INVALID_API_ROOT_URL,
} from '../../data/constants';
import { features } from '../../../../config';
+import { getButtonElement } from '../../../test/testUtils';
jest.mock('../data/actions');
jest.mock('../../utils');
@@ -69,6 +71,17 @@ const contextValue = {
setRefreshBool: jest.fn(),
};
+const setupNewSSOStepper = () => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = true;
+ return render(
+
+
+
+
+ ,
+ );
+};
+
describe('SAML Config Tab', () => {
afterEach(() => {
features.AUTH0_SELF_SERVICE_INTEGRATION = false;
@@ -309,15 +322,113 @@ describe('SAML Config Tab', () => {
expect(screen.getByText('Next')).not.toBeDisabled();
}, []);
});
- test('show new SSO stepper placeholder when feature flag enabled', async () => {
- // Setup
- features.AUTH0_SELF_SERVICE_INTEGRATION = true;
- contextValue.ssoState.currentStep = 'idp';
- render(
-
-
- ,
- );
+ test('navigate through new sso workflow skeleton', async () => {
+ setupNewSSOStepper();
+ // Connect Step
+ await waitFor(() => {
+ expect(getButtonElement('Next')).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText('New SSO integration')).toBeInTheDocument();
+ expect(screen.queryByText('Connect')).toBeInTheDocument();
+ expect(screen.queryByText('Let\'s get started')).toBeInTheDocument();
+ userEvent.click(getButtonElement('Next'));
+
+ // Configure Step
+ await waitFor(() => {
+ expect(getButtonElement('Configure')).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText('Enter integration details')).toBeInTheDocument();
+ userEvent.click(getButtonElement('Configure'));
+
+ // Authorize Step
+ await waitFor(() => {
+ expect(getButtonElement('Next')).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText('Authorize edX as a Service Provider')).toBeInTheDocument();
+ userEvent.click(getButtonElement('Next'));
+
+ // Confirm and Test Step
+ await waitFor(() => {
+ expect(getButtonElement('Finish')).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText('Wait for SSO configuration confirmation')).toBeInTheDocument();
+ });
+ test('show correct metadata entry based on selection', async () => {
+ setupNewSSOStepper();
+ await waitFor(() => {
+ expect(getButtonElement('Next')).toBeInTheDocument();
+ }, []);
+
+ const enterUrlText = 'Find the URL in your Identity Provider portal or website.';
+ const uploadXmlText = 'Drag and drop your file here or click to upload.';
+
+ // Verify metadata selectors are hidden initially
+ expect(screen.queryByText(enterUrlText)).not.toBeInTheDocument();
+ expect(screen.queryByText(uploadXmlText)).not.toBeInTheDocument();
+
+ // Verify metadata selectors appear with their respective selections
+ userEvent.click(screen.getByText('Enter identity Provider Metadata URL'));
+ await waitFor(() => {
+ expect(screen.queryByText(enterUrlText)).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText(uploadXmlText)).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByText('Upload Identity Provider Metadata XML file'));
+ await waitFor(() => {
+ expect(screen.queryByText(uploadXmlText)).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText(enterUrlText)).not.toBeInTheDocument();
+ });
+ test('back button shown on pages after first page', async () => {
+ const getBackButton = () => getButtonElement('Back');
+ setupNewSSOStepper();
+ // Connect Step
+ await waitFor(() => {
+ expect(getButtonElement('Next')).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByRole('button', { name: 'Back' })).not.toBeInTheDocument();
+ userEvent.click(getButtonElement('Next'));
+
+ // Configure Step
+ await waitFor(() => {
+ expect(getButtonElement('Configure')).toBeInTheDocument();
+ }, []);
+ expect(getBackButton()).toBeInTheDocument();
+ userEvent.click(getButtonElement('Configure'));
+
+ // Authorize Step
+ await waitFor(() => {
+ expect(getButtonElement('Next')).toBeInTheDocument();
+ }, []);
+ expect(getBackButton()).toBeInTheDocument();
+ userEvent.click(getButtonElement('Next'));
+
+ // Back from Confirm and Test Step
+ await waitFor(() => {
+ expect(getButtonElement('Finish')).toBeInTheDocument();
+ }, []);
+ userEvent.click(getBackButton());
+ await waitFor(() => {
+ expect(screen.queryByText('Authorize edX as a Service Provider')).toBeInTheDocument();
+ }, []);
+ });
+ test('cancel out of new SSO workflow', async () => {
+ setupNewSSOStepper();
+ // Connect Step
+ await waitFor(() => {
+ expect(getButtonElement('Cancel')).toBeInTheDocument();
+ }, []);
+ userEvent.click(getButtonElement('Cancel'));
+ await waitFor(() => {
+ expect(getButtonElement('Cancel')).toBeInTheDocument();
+ }, []);
+
+ await waitFor(() => {
+ const exitButton = getButtonElement('Exit without saving');
+ expect(exitButton).toBeInTheDocument();
+ userEvent.click(exitButton);
+ }, []);
+
await waitFor(() => {
expect(
screen.queryByText(
@@ -325,7 +436,6 @@ describe('SAML Config Tab', () => {
+ ' to allow quick access to your organization\'s learning catalog.',
),
).toBeInTheDocument();
- expect(screen.queryByText('Next')).not.toBeInTheDocument();
}, []);
});
test('idp step fetches and displays existing idp data fields', async () => {
diff --git a/src/components/test/testUtils.jsx b/src/components/test/testUtils.jsx
index 539cd0a3ad..b5ac1bf34e 100644
--- a/src/components/test/testUtils.jsx
+++ b/src/components/test/testUtils.jsx
@@ -3,7 +3,7 @@
import React from 'react';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
-import { render } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
export function renderWithRouter(
ui,
@@ -30,3 +30,5 @@ export function findElementWithText(container, type, text) {
const elements = container.querySelectorAll(type);
return [...elements].find((elem) => elem.innerHTML.includes(text));
}
+
+export const getButtonElement = (buttonText) => screen.getByRole('button', { name: buttonText });
From ed98264e122e47f80edaf6186a1e80c81892a1b7 Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Fri, 22 Sep 2023 08:57:57 -0600
Subject: [PATCH 026/124] fix: Bug bash fixes (#1029)
* fix: Bug bash fixes
* fix: test fixes
---
.../APICredentialsPage.jsx | 5 +-
.../SettingsApiCredentialsTab/CopyButton.jsx | 2 +
.../ZeroStateCard.jsx | 21 ++++++---
.../SettingsApiCredentialsTab/index.jsx | 46 ++++++++++---------
.../tests/SettingsAPICredentialsPage.test.jsx | 46 ++++++++++---------
5 files changed, 69 insertions(+), 51 deletions(-)
diff --git a/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx b/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
index f86dc4aaaf..37e10d2222 100644
--- a/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
+++ b/src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
@@ -8,7 +8,7 @@ import CopyButton from './CopyButton';
import { API_CLIENT_DOCUMENTATION, HELP_CENTER_LINK } from '../data/constants';
const APICredentialsPage = ({ data, setData }) => {
- const [formValue, setFormValue] = useState('');
+ const [formValue, setFormValue] = useState(data?.redirect_uris);
const handleFormChange = (e) => {
setFormValue(e.target.value);
};
@@ -48,7 +48,7 @@ const APICredentialsPage = ({ data, setData }) => {
-
+
Redirect URIs (optional)
If you need additional redirect URIs, add them below and regenerate your API credentials.
@@ -77,6 +77,7 @@ const APICredentialsPage = ({ data, setData }) => {
contact Enterprise Customer Support.
diff --git a/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx b/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
index eb3477e047..42e73bbc13 100644
--- a/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
+++ b/src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
@@ -12,6 +12,8 @@ const CopyButton = ({ data }) => {
const handleCopyLink = async () => {
try {
+ const newData = data;
+ ['user', 'id'].forEach(prop => delete newData[prop]);
const jsonString = JSON.stringify(data);
await navigator.clipboard.writeText(jsonString);
} catch (error) {
diff --git a/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx b/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx
index a9e37a0b8f..32a6d60f35 100644
--- a/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx
+++ b/src/components/settings/SettingsApiCredentialsTab/ZeroStateCard.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useContext } from 'react';
+import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import {
@@ -41,24 +41,33 @@ const ZeroStateCard = ({ setShowToast, setData }) => {
You don't have API credentials yet.
{ !displayFailureAlert && (
- This page allows you to generate API credentials to send to
+ This page allows you to generate API credentials to send to
your developers so they can work on integration projects.
If you believe you are seeing this page in error,
contact Enterprise Customer Support.
)}
- edX for Business API credentials credentials will provide access
- to the following edX API endpoints: reporting dashboard, dashboard, and catalog administration.
+ edX for Business API credentials will provide access to the following
+ edX API endpoints: reporting dashboard, dashboard, and catalog administration.
- By clicking the button below, you and your organization accept the {'\n'}
- edX API terms of service .
+ By clicking the button below, you and your organization accept the
+
+ edX API terms of service.
+
diff --git a/src/components/settings/SettingsApiCredentialsTab/index.jsx b/src/components/settings/SettingsApiCredentialsTab/index.jsx
index 2cdef4d762..31e0da4c2d 100644
--- a/src/components/settings/SettingsApiCredentialsTab/index.jsx
+++ b/src/components/settings/SettingsApiCredentialsTab/index.jsx
@@ -34,34 +34,36 @@ const SettingsApiCredentialsTab = ({
}, [enterpriseId]);
return (
-
-
-
- { hasRegenerationError && }
-
- API credentials
-
-
- Help Center: EdX Enterprise API Guide
-
-
-
- {!data ? (
-
- ) : (
)}
-
-
- { showToast && (
+
+
+
+
+ { hasRegenerationError && }
+
+ API credentials
+
+
+ Help Center: EdX Enterprise API Guide
+
+
+
+ {!data ? (
+
+ ) : (
)}
+
+
+ { showToast && (
setShowToast(false)}
show={showToast}
>
API credentials successfully generated
- )}
-
-
-
+ )}
+
+
+
+
);
};
SettingsApiCredentialsTab.propTypes = {
diff --git a/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx b/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx
index 425d02b7ca..1c751835d1 100644
--- a/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx
+++ b/src/components/settings/SettingsApiCredentialsTab/tests/SettingsAPICredentialsPage.test.jsx
@@ -20,18 +20,19 @@ const name = "edx's Enterprise Credentials";
const clientId = 'y0TCvOEvvIs6ll95irirzCJ5EaF0RnSbBIIXuNJE';
const clientSecret = '1G896sVeT67jtjHO6FNd5qFqayZPIV7BtnW01P8zaAd4mDfmBVVVsUP33u';
const updated = '2023-07-28T04:28:20.909550Z';
-const redirectUris = 'www.customercourses.edx.com, www.customercourses.edx.stage.com';
+const redirectUri1 = 'www.customercourses.edx.com';
+const redirectUri2 = 'www.customercourses.edx.com';
const data = {
name,
client_id: clientId,
client_secret: clientSecret,
- redirect_uris: redirectUris,
+ redirect_uris: redirectUri1,
updated,
};
const regenerationData = {
...data,
- redirect_uris: redirectUris,
+ redirect_uris: redirectUri1,
};
const copiedData = {
...data,
@@ -66,7 +67,7 @@ describe('API Credentials Tab', () => {
expect(screen.queryByText('Help Center: EdX Enterprise API Guide')).toBeInTheDocument();
const helpLink = screen.getByText('Help Center: EdX Enterprise API Guide');
expect(helpLink.getAttribute('href')).toBe(HELP_CENTER_API_GUIDE);
- const serviceLink = screen.getByText('edX API terms of service');
+ const serviceLink = screen.getByText('edX API terms of service.');
expect(serviceLink.getAttribute('href')).toBe(API_TERMS_OF_SERVICE);
expect(screen.getByText('Generate API Credentials').toBeInTheDocument);
@@ -86,7 +87,7 @@ describe('API Credentials Tab', () => {
await waitFor(() => expect(screen.getByText(name)).toBeInTheDocument);
expect(screen.getByText(name).toBeInTheDocument);
- expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUri1}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client ID: ${clientId}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client secret: ${clientSecret}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client documentation: ${API_CLIENT_DOCUMENTATION}` }).toBeInTheDocument);
@@ -147,7 +148,7 @@ describe('API Credentials Tab', () => {
});
expect(screen.getByRole('heading', { name: `Application name: ${name}` }).toBeInTheDocument);
- expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUri1}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client ID: ${clientId}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client secret: ${clientSecret}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client documentation: ${API_CLIENT_DOCUMENTATION}` }).toBeInTheDocument);
@@ -200,9 +201,11 @@ describe('API Credentials Tab', () => {
);
await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
const input = screen.getByTestId('form-control');
- expect(input).toHaveValue('');
- userEvent.type(input, redirectUris);
- await waitFor(() => expect(input).toHaveValue(redirectUris));
+ expect(input).toHaveValue(redirectUri1);
+ userEvent.clear(input);
+ userEvent.type(input, redirectUri2);
+
+ await waitFor(() => expect(input).toHaveValue(redirectUri2));
const button = screen.getByText('Regenerate API Credentials');
userEvent.click(button);
@@ -210,10 +213,13 @@ describe('API Credentials Tab', () => {
const confirmedButton = screen.getByText('Regenerate');
userEvent.click(confirmedButton);
await waitFor(() => {
- expect(mockPatchFn).toHaveBeenCalledWith(redirectUris, enterpriseId);
+ expect(mockPatchFn).toHaveBeenCalledWith(redirectUri2, enterpriseId);
+ });
+ await waitFor(() => {
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUri2}` }).toBeInTheDocument);
});
expect(screen.getByRole('heading', { name: `Application name: ${name}` }).toBeInTheDocument);
- expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUri2}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client ID: ${clientId}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client secret: ${clientSecret}` }).toBeInTheDocument);
expect(screen.getByRole('heading', { name: `API client documentation: ${API_CLIENT_DOCUMENTATION}` }).toBeInTheDocument);
@@ -234,9 +240,9 @@ describe('API Credentials Tab', () => {
);
await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
const input = screen.getByTestId('form-control');
- expect(input).toHaveValue('');
- userEvent.type(input, redirectUris);
- await waitFor(() => expect(input).toHaveValue(redirectUris));
+ expect(input).toHaveValue(redirectUri1);
+ userEvent.type(input, ` ${redirectUri2}`);
+ await waitFor(() => expect(input).toHaveValue(`${redirectUri1} ${redirectUri2}`));
const button = screen.getByText('Regenerate API Credentials');
userEvent.click(button);
@@ -244,9 +250,9 @@ describe('API Credentials Tab', () => {
const confirmedButton = screen.getByText('Regenerate');
userEvent.click(confirmedButton);
await waitFor(() => {
- expect(mockPatchFn).toHaveBeenCalledWith(redirectUris, enterpriseId);
+ expect(mockPatchFn).toHaveBeenCalledWith(`${redirectUri1} ${redirectUri2}`, enterpriseId);
});
- expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUri1}` }).toBeInTheDocument);
expect(screen.getByText('Something went wrong while generating your credentials. Please try again. If the issue continues, contact Enterprise Customer Support.'))
.toBeInTheDocument();
});
@@ -264,9 +270,7 @@ describe('API Credentials Tab', () => {
await waitFor(() => expect(mockFetchFn).toHaveBeenCalled());
const input = screen.getByTestId('form-control');
- expect(input).toHaveValue('');
- userEvent.type(input, redirectUris);
- await waitFor(() => expect(input).toHaveValue(redirectUris));
+ expect(input).toHaveValue(redirectUri1);
const button = screen.getByText('Regenerate API Credentials');
userEvent.click(button);
@@ -274,8 +278,8 @@ describe('API Credentials Tab', () => {
const cancelButton = screen.getByText('Cancel');
userEvent.click(cancelButton);
await waitFor(() => {
- expect(mockPatchFn).not.toHaveBeenCalledWith(redirectUris, enterpriseId);
+ expect(mockPatchFn).not.toHaveBeenCalledWith(redirectUri1, enterpriseId);
});
- expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUris}` }).toBeInTheDocument);
+ expect(screen.getByRole('heading', { name: `Allowed URIs: ${redirectUri1}` }).toBeInTheDocument);
});
});
From ce8c6d776456279210a52abfe731662a0d309d68 Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Mon, 18 Sep 2023 20:37:33 +0000
Subject: [PATCH 027/124] feat: enterprise sso orchestrator record api client
---
.env.development | 1 +
src/data/services/LmsApiService.js | 29 +++++++++++++++++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/.env.development b/.env.development
index 03858d1ff4..9e9f84cf22 100644
--- a/.env.development
+++ b/.env.development
@@ -51,3 +51,4 @@ MAINTENANCE_ALERT_START_TIMESTAMP=''
USE_API_CACHE='true'
SUBSCRIPTION_LPR='true'
PLOTLY_SERVER_URL='http://localhost:8050'
+AUTH0_SELF_SERVICE_INTEGRATION='true'
diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js
index 4cff4834d1..2ed35e8274 100644
--- a/src/data/services/LmsApiService.js
+++ b/src/data/services/LmsApiService.js
@@ -39,6 +39,35 @@ class LmsApiService {
static apiCredentialsUrl = `${LmsApiService.baseUrl}/enterprise/api/v1/enterprise-customer-api-credentials/`;
+ static enterpriseSsoOrchestrationUrl = `${LmsApiService.baseUrl}/enterprise/api/v1/enterprise_customer_sso_configuration/`;
+
+ static fetchEnterpriseSsoOrchestrationRecord(configurationUuid) {
+ const enterpriseSsoOrchestrationFetchUrl = `${LmsApiService.enterpriseSsoOrchestrationUrl}${configurationUuid}`;
+ return LmsApiService.apiClient().get(enterpriseSsoOrchestrationFetchUrl);
+ }
+
+ static listEnterpriseSsoOrchestration(enterpriseCustomerUuid) {
+ const enterpriseSsoOrchestrationListUrl = `${LmsApiService.enterpriseSsoOrchestrationUrl}`;
+ if (enterpriseCustomerUuid) {
+ return LmsApiService.apiClient().get(`${enterpriseSsoOrchestrationListUrl}?enterprise_customer=${enterpriseCustomerUuid}`);
+ }
+ return LmsApiService.apiClient().get(enterpriseSsoOrchestrationListUrl);
+ }
+
+ static createEnterpriseSsoOrchestrationRecord(formData) {
+ return LmsApiService.apiClient().post(LmsApiService.enterpriseSsoOrchestrationUrl, formData);
+ }
+
+ static updateEnterpriseSsoOrchestrationRecord(formData, configurationUuid) {
+ const enterpriseSsoOrchestrationUpdateUrl = `${LmsApiService.enterpriseSsoOrchestrationUrl}${configurationUuid}`;
+ return LmsApiService.apiClient().put(enterpriseSsoOrchestrationUpdateUrl, formData);
+ }
+
+ static deleteEnterpriseSsoOrchestrationRecord(configurationUuid) {
+ const enterpriseSsoOrchestrationDeleteUrl = `${LmsApiService.enterpriseSsoOrchestrationUrl}${configurationUuid}`;
+ return LmsApiService.apiClient().delete(enterpriseSsoOrchestrationDeleteUrl);
+ }
+
static fetchEnterpriseList(options) {
const queryParams = new URLSearchParams({
page: 1,
From 9ed5ab37b5da85fa1c97189b681d71e8be3583b1 Mon Sep 17 00:00:00 2001
From: muhammad-ammar
Date: Tue, 19 Sep 2023 18:06:58 +0500
Subject: [PATCH 028/124] feat: add support for optional salesforce ids in csv
upload
---
src/components/InviteLearnersModal/index.jsx | 6 +-
src/data/validation/email.js | 75 ++++++++++++--
src/data/validation/email.test.js | 102 +++++++++++++++++++
3 files changed, 173 insertions(+), 10 deletions(-)
diff --git a/src/components/InviteLearnersModal/index.jsx b/src/components/InviteLearnersModal/index.jsx
index 89ee1e62d6..29c0f75e89 100644
--- a/src/components/InviteLearnersModal/index.jsx
+++ b/src/components/InviteLearnersModal/index.jsx
@@ -10,7 +10,7 @@ import { camelCaseObject } from '@edx/frontend-platform/utils';
import emailTemplate from './emailTemplate';
import TextAreaAutoSize from '../TextAreaAutoSize';
import FileInput from '../FileInput';
-import { returnValidatedEmails, validateEmailAddrTemplateForm } from '../../data/validation/email';
+import { extractSalesforceIds, returnValidatedEmails, validateEmailAddrTemplateForm } from '../../data/validation/email';
import { normalizeFileUpload } from '../../utils';
class InviteLearnersModal extends React.Component {
@@ -74,6 +74,10 @@ class InviteLearnersModal extends React.Component {
};
options.user_emails = returnValidatedEmails(formData);
+ const SFIDs = extractSalesforceIds(formData, options.user_emails);
+ if (SFIDs) {
+ options.user_sfids = SFIDs;
+ }
/* eslint-disable no-underscore-dangle */
return addLicensesForUsers(options, subscriptionUUID)
diff --git a/src/data/validation/email.js b/src/data/validation/email.js
index c39edc6585..a7f757d8a7 100644
--- a/src/data/validation/email.js
+++ b/src/data/validation/email.js
@@ -82,6 +82,33 @@ const validateEmailAddresses = (emails) => {
return result;
};
+// Each row in textarea or csv can contain email plus an optional salesforce id
+// Email and salesforce id will be separated by comma. This function will read
+// each row, split it by comma and then return an object with three properties:
+// textEmails: All emails extracted from textarea
+// csvEmails: All emails extracted from CSV
+// allEmails: Concatenation of `textEmails` and `csvEmails`
+const extractEmails = (formData) => {
+ let textEmails = [];
+ let csvEmails = [];
+ let allEmails = [];
+
+ if (formData[EMAIL_ADDRESS_TEXT_FORM_DATA] && formData[EMAIL_ADDRESS_TEXT_FORM_DATA].length) {
+ textEmails = formData[EMAIL_ADDRESS_TEXT_FORM_DATA].split(/\r\n|\n/).map(item => item.split(',')[0]);
+ }
+ if (formData[EMAIL_ADDRESS_CSV_FORM_DATA] && formData[EMAIL_ADDRESS_CSV_FORM_DATA].length) {
+ csvEmails = formData[EMAIL_ADDRESS_CSV_FORM_DATA].map(item => item.split(',')[0]);
+ }
+
+ allEmails = [...textEmails, ...csvEmails];
+
+ return {
+ textEmails,
+ csvEmails,
+ allEmails,
+ };
+};
+
const validateEmailAddressesFields = (formData) => {
// Validate that email address fields contain valid-looking emails.
// Expects Redux form data
@@ -90,11 +117,11 @@ const validateEmailAddressesFields = (formData) => {
_error: [],
};
- const textAreaEmails = formData[EMAIL_ADDRESS_TEXT_FORM_DATA] && formData[EMAIL_ADDRESS_TEXT_FORM_DATA].split(/\r\n|\n/);
- const csvEmails = formData[EMAIL_ADDRESS_CSV_FORM_DATA];
+ const { csvEmails, textEmails } = extractEmails(formData);
+ const emails = textEmails.length ? textEmails : csvEmails;
let {
invalidEmailIndices,
- } = validateEmailAddresses(textAreaEmails || csvEmails);
+ } = validateEmailAddresses(emails);
// 1 is added to every index to fix off-by-one error in messages shown to the user.
invalidEmailIndices = invalidEmailIndices.map(i => i + 1);
@@ -105,7 +132,7 @@ const validateEmailAddressesFields = (formData) => {
${invalidEmailIndices.length !== 0 ? `and ${lastEmail}` : `${lastEmail}`} \
is invalid. Please try again.`;
- errorsDict[textAreaEmails ? EMAIL_ADDRESS_TEXT_FORM_DATA : EMAIL_ADDRESS_CSV_FORM_DATA] = message;
+ errorsDict[textEmails.length ? EMAIL_ADDRESS_TEXT_FORM_DATA : EMAIL_ADDRESS_CSV_FORM_DATA] = message;
errorsDict._error.push(message);
}
@@ -155,19 +182,49 @@ const returnValidatedEmails = (formData) => {
if (errorsDict._error.length > 0) {
throw new SubmissionError(errorsDict);
}
- let emails = [];
+
+ const emails = _.union(extractEmails(formData).allEmails); // Dedup emails
+ return validateEmailAddresses(emails).validEmails;
+};
+
+// Combine all the rows from textarea and CSV and then make a map of email to salesforce id
+const getSalesforceIdsByEmail = (formData) => {
+ const rows = [];
+ const allRecords = {};
+
if (formData[EMAIL_ADDRESS_TEXT_FORM_DATA] && formData[EMAIL_ADDRESS_TEXT_FORM_DATA].length) {
- emails.push(...formData[EMAIL_ADDRESS_TEXT_FORM_DATA].split(/\r\n|\n/));
+ rows.push(...formData[EMAIL_ADDRESS_TEXT_FORM_DATA].split(/\r\n|\n/));
}
+
if (formData[EMAIL_ADDRESS_CSV_FORM_DATA] && formData[EMAIL_ADDRESS_CSV_FORM_DATA].length) {
- emails.push(...formData[EMAIL_ADDRESS_CSV_FORM_DATA]);
+ rows.push(...formData[EMAIL_ADDRESS_CSV_FORM_DATA]);
}
- emails = _.union(emails); // Dedup emails
- return validateEmailAddresses(emails).validEmails;
+
+ rows.forEach((row) => {
+ const [email, salesforceId] = row.split(',').map(item => item.trim());
+ allRecords[email] = salesforceId;
+ });
+
+ return allRecords;
+};
+
+// Extract salesforce ids for all validated emails
+const extractSalesforceIds = (formData, userEmails) => {
+ const salesforceIdsByEmail = getSalesforceIdsByEmail(formData);
+ const ids = [];
+
+ userEmails.forEach((email) => {
+ ids.push(salesforceIdsByEmail[email]);
+ });
+
+ // check if `ids` array contain non-empty, not-null values
+ const noIdsPresent = ids.every(item => !item);
+ return noIdsPresent ? undefined : ids;
};
/* eslint-enable no-underscore-dangle */
export {
+ extractSalesforceIds,
validateEmailAddresses,
validateEmailAddressesFields,
validateEmailTemplateForm,
diff --git a/src/data/validation/email.test.js b/src/data/validation/email.test.js
index bb32bec662..4cc46ac214 100644
--- a/src/data/validation/email.test.js
+++ b/src/data/validation/email.test.js
@@ -1,5 +1,6 @@
import _ from 'lodash';
import {
+ extractSalesforceIds,
validateEmailAddresses,
validateEmailAddressesFields,
validateEmailTemplateFields,
@@ -233,4 +234,105 @@ describe('email validation', () => {
);
});
});
+
+ describe('validate emails and ids extraction', () => {
+ it('extracted correct emails and ids from textarea and csv', () => {
+ const formData = new FormData();
+ formData[EMAIL_ADDRESS_TEXT_FORM_DATA] = [
+ 'abc@example.com,000000000000ABCABC',
+ 'asdf@example.com,',
+ 'zzz@example.com,000000000000XYZXYZ',
+ ].join('\n');
+ formData[EMAIL_ADDRESS_CSV_FORM_DATA] = [
+ 'one@example.com,000000000000YYYYYY',
+ 'two@example.com,000000000000ZZZZZZ',
+ 'three@example.com,000000000000ABCDDD',
+ 'wow@example.com,',
+ 'abc@example.com,000000000000ABCABC',
+ 'ama@example.com',
+ ];
+ const userEmails = [
+ 'abc@example.com',
+ 'asdf@example.com',
+ 'zzz@example.com',
+ 'one@example.com',
+ 'two@example.com',
+ 'three@example.com',
+ 'wow@example.com',
+ 'ama@example.com',
+ ];
+
+ const salesforceIds = extractSalesforceIds(formData, userEmails);
+ expect(salesforceIds).toEqual([
+ '000000000000ABCABC',
+ '',
+ '000000000000XYZXYZ',
+ '000000000000YYYYYY',
+ '000000000000ZZZZZZ',
+ '000000000000ABCDDD',
+ '',
+ undefined,
+ ]);
+ expect(userEmails.length).toEqual(salesforceIds.length);
+ });
+
+ it('extracted correct emails and ids from textarea only', () => {
+ const formData = new FormData();
+ formData[EMAIL_ADDRESS_TEXT_FORM_DATA] = [
+ 'aaa@example.com,000000000000ABCABC',
+ 'bbb@example.com,',
+ 'ccc@example.com,000000000000XYZXYZ',
+ 'ddd@example.com',
+ ].join('\n');
+
+ const userEmails = returnValidatedEmails(formData);
+ expect(userEmails).toEqual(['aaa@example.com', 'bbb@example.com', 'ccc@example.com', 'ddd@example.com']);
+ const salesforceIds = extractSalesforceIds(formData, userEmails);
+ expect(salesforceIds).toEqual([
+ '000000000000ABCABC',
+ '',
+ '000000000000XYZXYZ',
+ undefined,
+ ]);
+ expect(userEmails.length).toEqual(salesforceIds.length);
+ });
+
+ it('extracted correct emails and ids from csv only', () => {
+ const formData = new FormData();
+ formData[EMAIL_ADDRESS_CSV_FORM_DATA] = [
+ 'eee@example.com,000000000000YYYYYY',
+ 'fff@example.com,',
+ 'ggg@example.com,000000000000ZZZZZZ',
+ 'hhh@example.com',
+ ];
+
+ const userEmails = returnValidatedEmails(formData);
+ expect(userEmails).toEqual(['eee@example.com', 'fff@example.com', 'ggg@example.com', 'hhh@example.com']);
+ const salesforceIds = extractSalesforceIds(formData, userEmails);
+ expect(salesforceIds).toEqual([
+ '000000000000YYYYYY',
+ '',
+ '000000000000ZZZZZZ',
+ undefined,
+ ]);
+ expect(userEmails.length).toEqual(salesforceIds.length);
+ });
+
+ it('returns no salesforce ids for emails only', () => {
+ const formData = new FormData();
+ formData[EMAIL_ADDRESS_TEXT_FORM_DATA] = [
+ 'abc@example.com',
+ 'asdf@example.com,',
+ ].join('\n');
+ formData[EMAIL_ADDRESS_CSV_FORM_DATA] = [
+ 'one@example.com,',
+ 'two@example.com',
+ ];
+
+ const userEmails = returnValidatedEmails(formData);
+ expect(userEmails).toEqual(['abc@example.com', 'asdf@example.com', 'one@example.com', 'two@example.com']);
+ const salesforceIds = extractSalesforceIds(formData, userEmails);
+ expect(salesforceIds).toEqual(undefined);
+ });
+ });
});
From 7874784a7bee6c422177357dd0f2908e922cd662 Mon Sep 17 00:00:00 2001
From: jajjibhai008
Date: Fri, 15 Sep 2023 19:03:16 +0500
Subject: [PATCH 029/124] feat: show all enterprise budgets regardless of plan
and route correctly
fix: full page refresh issue when clicking 'Budgets', added test coverage and fixed lint issues
feat: show all enterprise budgets regardless of plan and route correctly
fix: moved LCM routes to separate file
fix: resolve spacing and repetitions issue
fix: resolve spacing and repetitions issue
fix: improved test coverage
refactor: improve code efficiency and readability
fix: incorporate adam's suggestions and nits
fix: incorporated adam's feedback
fix: incorporate adam's feedback
---
.../EnterpriseApp/EnterpriseAppRoutes.jsx | 8 +-
.../EnterpriseApp/data/constants.js | 11 ++
.../EnterpriseSubsidiesContext/data/hooks.js | 65 ++++---
.../data/tests/BudgetDetailPage.test.jsx | 138 ++++++++++++++
.../data/tests/hooks.test.js | 111 ++++++-----
.../BudgetCard-V2.jsx | 174 ++++--------------
.../BudgetDetailPage.jsx | 85 +++++++++
.../MultipleBudgetsPage.jsx | 17 +-
.../MultipleBudgetsPicker.jsx | 29 +--
.../SubBudgetCard.jsx | 103 +++++++++++
.../learner-credit-management/data/hooks.js | 13 +-
.../data/tests/hooks.test.js | 8 +
.../data/tests/utils.test.js | 72 +++++++-
.../learner-credit-management/data/utils.js | 45 ++++-
.../learner-credit-management/index.js | 3 -
.../learner-credit-management/index.jsx | 28 +++
.../tests/BudgetCard.test.jsx | 124 ++++++++++---
17 files changed, 769 insertions(+), 265 deletions(-)
create mode 100644 src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailPage.jsx
create mode 100644 src/components/learner-credit-management/SubBudgetCard.jsx
delete mode 100644 src/components/learner-credit-management/index.js
create mode 100644 src/components/learner-credit-management/index.jsx
diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
index b5853281d5..e46986950f 100644
--- a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
+++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
@@ -13,9 +13,9 @@ import { SubscriptionManagementPage } from '../subscriptions';
import { PlotlyAnalyticsPage } from '../PlotlyAnalytics';
import { ROUTE_NAMES } from './data/constants';
import BulkEnrollmentResultsDownloadPage from '../BulkEnrollmentResultsDownloadPage';
-import LearnerCreditManagement from '../learner-credit-management';
import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
import ContentHighlights from '../ContentHighlights';
+import LearnerCreditManagementRoutes from '../learner-credit-management';
const EnterpriseAppRoutes = ({
baseUrl,
@@ -98,10 +98,8 @@ const EnterpriseAppRoutes = ({
/>
{canManageLearnerCredit && (
-
)}
diff --git a/src/components/EnterpriseApp/data/constants.js b/src/components/EnterpriseApp/data/constants.js
index 5c881fefab..6feaac51f7 100644
--- a/src/components/EnterpriseApp/data/constants.js
+++ b/src/components/EnterpriseApp/data/constants.js
@@ -13,3 +13,14 @@ export const ROUTE_NAMES = {
subscriptionManagement: 'subscriptions',
contentHighlights: 'content-highlights',
};
+
+export const BUDGET_STATUSES = {
+ active: 'Active',
+ expired: 'Expired',
+ upcoming: 'Upcoming',
+};
+
+export const BUDGET_TYPES = {
+ ecommerce: 'ecommerce',
+ subsidy: 'subsidy',
+};
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index d699098cd0..6e9b040167 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -10,6 +10,7 @@ import { camelCaseObject } from '@edx/frontend-platform/utils';
import EcommerceApiService from '../../../data/services/EcommerceApiService';
import LicenseManagerApiService from '../../../data/services/LicenseManagerAPIService';
import SubsidyApiService from '../../../data/services/EnterpriseSubsidyApiService';
+import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen, enterpriseId }) => {
const [offers, setOffers] = useState([]);
@@ -25,42 +26,40 @@ export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen,
try {
const [enterpriseSubsidyResponse, ecommerceApiResponse] = await Promise.all([
SubsidyApiService.getSubsidyByCustomerUUID(enterpriseId, { subsidyType: 'learner_credit' }),
- EcommerceApiService.fetchEnterpriseOffers({
- isCurrent: true,
- }),
+ EcommerceApiService.fetchEnterpriseOffers(),
]);
- // If there are no subsidies in enterprise, fall back to the e-commerce API.
- let { results } = camelCaseObject(enterpriseSubsidyResponse.data);
- let source = 'subsidyApi';
+ // We have to consider both type of offers active and inactive.
- if (results.length === 0) {
- results = camelCaseObject(ecommerceApiResponse.data.results);
- source = 'ecommerceApi';
- }
- let activeSubsidyFound = false;
- if (results.length !== 0) {
- let subsidy = results[0];
- const offerData = [];
- let activeSubsidyData = {};
- for (let i = 0; i < results.length; i++) {
- subsidy = results[i];
- activeSubsidyFound = source === 'ecommerceApi'
- ? subsidy.isCurrent
- : subsidy.isActive;
- if (activeSubsidyFound === true) {
- activeSubsidyData = {
- id: subsidy.uuid || subsidy.id,
- name: subsidy.title || subsidy.displayName,
- start: subsidy.activeDatetime || subsidy.startDatetime,
- end: subsidy.expirationDatetime || subsidy.endDatetime,
- isCurrent: activeSubsidyFound,
- };
- offerData.push(activeSubsidyData);
- setCanManageLearnerCredit(true);
- }
- }
- setOffers(offerData);
+ const enterpriseSubsidyResults = camelCaseObject(enterpriseSubsidyResponse.data).results;
+ const ecommerceOffersResults = camelCaseObject(ecommerceApiResponse.data.results);
+
+ const offerData = [];
+
+ enterpriseSubsidyResults.forEach((result) => {
+ offerData.push({
+ source: BUDGET_TYPES.subsidy,
+ id: result.uuid,
+ name: result.title,
+ start: result.activeDatetime,
+ end: result.expirationDatetime,
+ isCurrent: result.isActive,
+ });
+ });
+
+ ecommerceOffersResults.forEach((result) => {
+ offerData.push({
+ source: BUDGET_TYPES.ecommerce,
+ id: (result.id).toString(),
+ name: result.displayName,
+ start: result.startDatetime,
+ end: result.endDatetime,
+ isCurrent: result.isCurrent,
+ });
+ });
+ setOffers(offerData);
+ if (offerData.length > 0) {
+ setCanManageLearnerCredit(true);
}
} catch (error) {
logError(error);
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx b/src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx
new file mode 100644
index 0000000000..483263fdce
--- /dev/null
+++ b/src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx
@@ -0,0 +1,138 @@
+/* eslint-disable react/prop-types */
+import React from 'react';
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import configureMockStore from 'redux-mock-store';
+import {
+ screen,
+ render,
+} from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { MemoryRouter } from 'react-router-dom';
+import BudgetDetailPage from '../../../learner-credit-management/BudgetDetailPage';
+import { useOfferSummary, useOfferRedemptions } from '../../../learner-credit-management/data/hooks';
+import { EXEC_ED_OFFER_TYPE } from '../../../learner-credit-management/data/constants';
+import { EnterpriseSubsidiesContext } from '../..';
+
+jest.mock('../../../learner-credit-management/data/hooks');
+
+useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: null,
+});
+useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+});
+
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseId = 'test-enterprise';
+const enterpriseUUID = '1234';
+const initialStore = {
+ portalConfiguration: {
+ enterpriseId,
+ enterpriseSlug: enterpriseId,
+
+ },
+};
+const store = getMockStore({ ...initialStore });
+
+const mockEnterpriseOfferId = '123';
+
+const mockOfferDisplayName = 'Test Enterprise Offer';
+const mockOfferSummary = {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerType: EXEC_ED_OFFER_TYPE,
+};
+
+const defaultEnterpriseSubsidiesContextValue = {
+ isLoading: false,
+};
+
+const BudgetDetailPageWrapper = ({
+ enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
+ ...rest
+}) => (
+
+
+
+
+
+
+
+
+
+
+);
+
+describe(' ', () => {
+ describe('with enterprise offer', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('displays table on clicking view budget', async () => {
+ const mockOffer = {
+ id: mockEnterpriseOfferId,
+ name: mockOfferDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ };
+ useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: mockOfferSummary,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+ render( );
+ expect(screen.getByText('Learner Credit Management'));
+ expect(screen.getByText('Overview'));
+ expect(screen.getByText('No results found'));
+ });
+
+ it('displays loading message while loading data', async () => {
+ useOfferSummary.mockReturnValue({
+ isLoading: true,
+ offerSummary: null,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: true,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+
+ render( );
+
+ expect(screen.getByText('loading'));
+ });
+ });
+});
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
index adf8580b52..ec1c5466af 100644
--- a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
+++ b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
@@ -4,6 +4,7 @@ import { useCoupons, useCustomerAgreement, useEnterpriseOffers } from '../hooks'
import EcommerceApiService from '../../../../data/services/EcommerceApiService';
import LicenseManagerApiService from '../../../../data/services/LicenseManagerAPIService';
import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
+import { BUDGET_TYPES } from '../../../EnterpriseApp/data/constants';
jest.mock('@edx/frontend-platform/config', () => ({
getConfig: jest.fn(() => ({
@@ -51,6 +52,7 @@ describe('useEnterpriseOffers', () => {
start: '2021-05-15T19:56:09Z',
end: '2100-05-15T19:56:09Z',
isCurrent: true,
+ source: BUDGET_TYPES.ecommerce,
}];
SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
@@ -69,9 +71,7 @@ describe('useEnterpriseOffers', () => {
await waitForNextUpdate();
- expect(EcommerceApiService.fetchEnterpriseOffers).toHaveBeenCalledWith({
- isCurrent: true,
- });
+ expect(EcommerceApiService.fetchEnterpriseOffers).toHaveBeenCalled();
expect(result.current).toEqual({
offers: mockOffers,
isLoading: false,
@@ -80,25 +80,35 @@ describe('useEnterpriseOffers', () => {
});
it('should fetch enterprise offers for the enterprise when data available in enterprise-subsidy', async () => {
- const mockOffers = [
+ const mockEnterpriseSubsidyResponse = [
{
- id: 'offer-id',
- name: 'offer-name',
- start: '2021-05-15T19:56:09Z',
- end: '2100-05-15T19:56:09Z',
- isCurrent: true,
+ uuid: 'offer-id',
+ title: 'offer-name',
+ activeDatetime: '2021-05-15T19:56:09Z',
+ expirationDatetime: '2100-05-15T19:56:09Z',
+ isActive: true,
},
];
- const mockSubsidyServiceResponse = [{
- uuid: 'offer-id',
- title: 'offer-name',
- active_datetime: '2021-05-15T19:56:09Z',
- expiration_datetime: '2100-05-15T19:56:09Z',
- is_active: true,
- }];
+
+ const mockEcommerceResponse = [
+ {
+ id: 'uuid',
+ display_name: 'offer-name',
+ start_datetime: '2021-05-15T19:56:09Z',
+ end_datetime: '2100-05-15T19:56:09Z',
+ is_current: true,
+ },
+ ];
+
SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
data: {
- results: mockSubsidyServiceResponse,
+ results: mockEnterpriseSubsidyResponse,
+ },
+ });
+
+ EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
+ data: {
+ results: mockEcommerceResponse,
},
});
@@ -113,36 +123,39 @@ describe('useEnterpriseOffers', () => {
TEST_ENTERPRISE_UUID,
{ subsidyType: 'learner_credit' },
);
+
+ const expectedOffers = [
+ {
+ id: 'offer-id',
+ name: 'offer-name',
+ start: '2021-05-15T19:56:09Z',
+ end: '2100-05-15T19:56:09Z',
+ isCurrent: true,
+ source: BUDGET_TYPES.subsidy,
+ },
+ {
+ id: 'uuid',
+ name: 'offer-name',
+ start: '2021-05-15T19:56:09Z',
+ end: '2100-05-15T19:56:09Z',
+ isCurrent: true,
+ source: BUDGET_TYPES.ecommerce,
+ },
+ ];
+
expect(result.current).toEqual({
- offers: mockOffers,
+ offers: expectedOffers,
isLoading: false,
canManageLearnerCredit: true,
});
});
it('should set canManageLearnerCredit to false if active enterprise offer or subsidy not found', async () => {
- const mockOffers = [{ subsidyUuid: 'offer-1' }, { subsidyUuid: 'offer-2' }];
- const mockSubsidyServiceResponse = [
- {
- uuid: 'offer-1',
- title: 'offer-name',
- active_datetime: '2005-05-15T19:56:09Z',
- expiration_datetime: '2006-05-15T19:56:09Z',
- is_active: false,
- },
- {
- uuid: 'offer-2',
- title: 'offer-name-2',
- active_datetime: '2006-05-15T19:56:09Z',
- expiration_datetime: '2007-05-15T19:56:09Z',
- is_active: false,
- },
- ];
- const mockOfferData = [];
+ const mockSubsidyServiceResponse = [];
EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
data: {
- results: mockOffers,
+ results: [],
},
});
SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
@@ -162,15 +175,22 @@ describe('useEnterpriseOffers', () => {
TEST_ENTERPRISE_UUID,
{ subsidyType: 'learner_credit' },
);
+
+ const hasActiveOffersOrSubsidies = mockSubsidyServiceResponse.some(offer => offer.is_active);
+ let canManageLearnerCredit = false;
+
+ if (hasActiveOffersOrSubsidies) {
+ canManageLearnerCredit = true;
+ }
+
expect(result.current).toEqual({
- offers: mockOfferData,
+ offers: [],
isLoading: false,
- canManageLearnerCredit: false,
+ canManageLearnerCredit,
});
});
it('should return the active enterprise offer or subsidy when multiple available', async () => {
- const mockOffers = [{ subsidyUuid: 'offer-1' }, { subsidyUuid: 'offer-2' }];
const mockSubsidyServiceResponse = [
{
uuid: 'offer-1',
@@ -188,18 +208,27 @@ describe('useEnterpriseOffers', () => {
},
];
const mockOfferData = [
+ {
+ id: 'offer-1',
+ name: 'offer-name',
+ start: '2005-05-15T19:56:09Z',
+ end: '2006-05-15T19:56:09Z',
+ isCurrent: false,
+ source: BUDGET_TYPES.subsidy,
+ },
{
id: 'offer-2',
name: 'offer-name-2',
start: '2006-05-15T19:56:09Z',
end: '2099-05-15T19:56:09Z',
isCurrent: true,
+ source: BUDGET_TYPES.subsidy,
},
];
EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
data: {
- results: mockOffers,
+ results: [],
},
});
SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
index b39b9297d9..e6780a61db 100644
--- a/src/components/learner-credit-management/BudgetCard-V2.jsx
+++ b/src/components/learner-credit-management/BudgetCard-V2.jsx
@@ -1,25 +1,17 @@
-import React, { useState } from 'react';
+/* eslint-disable react/jsx-no-useless-fragment */
+/* eslint-disable no-nested-ternary */
+import React from 'react';
import PropTypes from 'prop-types';
-import dayjs from 'dayjs';
-import {
- Card,
- Button,
- Stack,
- Row,
- Col,
- Breadcrumb,
-} from '@edx/paragon';
-
-import { useOfferRedemptions, useOfferSummary } from './data/hooks';
-import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
-import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import { useOfferSummary } from './data/hooks';
+import SubBudgetCard from './SubBudgetCard';
+import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
const BudgetCard = ({
offer,
enterpriseUUID,
enterpriseSlug,
- enableLearnerPortal,
+ offerType,
+ displayName,
}) => {
const {
start,
@@ -31,124 +23,37 @@ const BudgetCard = ({
offerSummary,
} = useOfferSummary(enterpriseUUID, offer);
- const {
- isLoading: isLoadingOfferRedemptions,
- offerRedemptions,
- fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, offer?.id);
- const [detailPage, setDetailPage] = useState(false);
- const [activeLabel, setActiveLabel] = useState('');
- const links = [
- { label: 'Budgets', url: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
- ];
- const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
- const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
- const navigateToBudgetRedemptions = (budgetType) => {
- setDetailPage(true);
- links.push({ label: budgetType, url: `/${enterpriseSlug}/admin/learner-credit` });
- setActiveLabel(budgetType);
- };
-
- const renderActions = (budgetType) => (
- navigateToBudgetRedemptions(budgetType)}
- >
- View Budget
-
- );
-
- const renderCardHeader = (budgetType) => {
- const subtitle = (
-
-
- {formattedStartDate} - {formattedExpirationDate}
-
-
- );
-
- return (
-
- {renderActions(budgetType)}
-
- )}
- />
- );
- };
-
- const renderCardSection = (available, spent) => (
-
-
-
- Available
- {available}
-
-
- Spent
- {spent}
-
-
-
- );
-
- const renderCardAggregate = () => (
-
-
-
- );
-
return (
-
-
-
-
-
-
- {!detailPage
- ? (
- <>
- {renderCardAggregate()}
- Budgets
-
-
-
- {renderCardHeader('Overview')}
- {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFunds)}
-
-
-
- >
- )
- : (
-
- )}
-
+ <>
+ {offerType === BUDGET_TYPES.ecommerce ? (
+
+ ) : (
+ <>
+ {offerSummary?.budgetsSummary?.map((budget) => (
+
+ ))}
+ >
+ )}
+ >
);
};
@@ -161,7 +66,8 @@ BudgetCard.propTypes = {
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
- enableLearnerPortal: PropTypes.bool.isRequired,
+ offerType: PropTypes.string.isRequired,
+ displayName: PropTypes.string,
};
export default BudgetCard;
diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx
new file mode 100644
index 0000000000..ad90b5ae89
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailPage.jsx
@@ -0,0 +1,85 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Row,
+ Col,
+ Breadcrumb,
+ Container,
+} from '@edx/paragon';
+import { connect } from 'react-redux';
+import { Helmet } from 'react-helmet';
+import { useParams, Link } from 'react-router-dom';
+import Hero from '../Hero';
+
+import LoadingMessage from '../LoadingMessage';
+import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
+
+import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
+import { useOfferRedemptions } from './data/hooks';
+import { isUUID } from './data/utils';
+import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+
+const PAGE_TITLE = 'Learner Credit Management';
+
+const BudgetDetailPage = ({
+ enterpriseUUID,
+ enterpriseSlug,
+ enableLearnerPortal,
+}) => {
+ const { budgetId } = useParams();
+ const enterpriseOfferId = isUUID(budgetId) ? null : budgetId;
+ const subsidyAccessPolicyId = isUUID(budgetId) ? budgetId : null;
+
+ const { isLoading } = useContext(EnterpriseSubsidiesContext);
+ const {
+ isLoading: isLoadingOfferRedemptions,
+ offerRedemptions,
+ fetchOfferRedemptions,
+ } = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
+ if (isLoading) {
+ return
;
+ }
+ const links = [
+ { label: 'Budgets', to: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
+ ];
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseUUID: state.portalConfiguration.enterpriseId,
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
+});
+
+BudgetDetailPage.propTypes = {
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
+};
+
+export default connect(mapStateToProps)(BudgetDetailPage);
diff --git a/src/components/learner-credit-management/MultipleBudgetsPage.jsx b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
index 3df18c465a..fe12ab2719 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPage.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
@@ -6,6 +6,7 @@ import {
Col,
Card,
Hyperlink,
+ Container,
} from '@edx/paragon';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
@@ -17,7 +18,7 @@ import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
import { configuration } from '../../config';
-const PAGE_TITLE = 'Learner Credit';
+const PAGE_TITLE = 'Learner Credit Management';
const MultipleBudgetsPage = ({
enterpriseUUID,
@@ -63,12 +64,14 @@ const MultipleBudgetsPage = ({
<>
-
+
+
+
>
);
};
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index 4c3da2d0ce..db6f178f2a 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -14,18 +14,25 @@ const MultipleBudgetsPicker = ({
enterpriseSlug,
enableLearnerPortal,
}) => (
-
+
-
- {offers.map(offer => (
-
- ))}
+ Budgets
+
+
+
+
+ {offers.map(offer => (
+
+ ))}
+
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
new file mode 100644
index 0000000000..d3360cea43
--- /dev/null
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -0,0 +1,103 @@
+import { Link } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import dayjs from 'dayjs';
+import {
+ Card,
+ Button,
+ Row,
+ Col,
+} from '@edx/paragon';
+
+import { BUDGET_STATUSES, ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import { formatPrice, getBudgetStatus } from './data/utils';
+
+const SubBudgetCard = ({
+ id,
+ start,
+ end,
+ available,
+ spent,
+ displayName,
+ enterpriseSlug,
+ isLoading,
+}) => {
+ const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
+ const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
+ const budgetStatus = getBudgetStatus(start, end);
+
+ const renderActions = (budgetId) => (
+
+ View budget
+
+ );
+
+ const renderCardHeader = (budgetType, budgetId) => {
+ const subtitle = (
+
+
+ {formattedStartDate} - {formattedExpirationDate}
+
+
+ );
+
+ return (
+
+ );
+ };
+
+ const renderCardSection = (availableBalance, spentBalance) => (
+
+
+
+ Available
+ {formatPrice(availableBalance)}
+
+
+ Spent
+ {formatPrice(spentBalance)}
+
+
+
+ );
+
+ return (
+
+
+ {renderCardHeader(displayName || 'Overview', id)}
+ {budgetStatus !== BUDGET_STATUSES.upcoming && renderCardSection(available, spent)}
+
+
+ );
+};
+
+SubBudgetCard.propTypes = {
+ enterpriseSlug: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ start: PropTypes.string,
+ end: PropTypes.string,
+ spent: PropTypes.number,
+ isLoading: PropTypes.bool,
+ available: PropTypes.number,
+ displayName: PropTypes.string,
+};
+
+export default SubBudgetCard;
diff --git a/src/components/learner-credit-management/data/hooks.js b/src/components/learner-credit-management/data/hooks.js
index 585970c35e..31577f36a7 100644
--- a/src/components/learner-credit-management/data/hooks.js
+++ b/src/components/learner-credit-management/data/hooks.js
@@ -74,7 +74,7 @@ const applyFiltersToOptions = (filters, options) => {
}
};
-export const useOfferRedemptions = (enterpriseUUID, offerId) => {
+export const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
const [offerRedemptions, setOfferRedemptions] = useState({
@@ -90,9 +90,14 @@ export const useOfferRedemptions = (enterpriseUUID, offerId) => {
const options = {
page: args.pageIndex + 1, // `DataTable` uses zero-indexed array
pageSize: args.pageSize,
- offerId,
ignoreNullCourseListPrice: true,
};
+ if (budgetId !== null) {
+ options.budgetId = budgetId;
+ }
+ if (offerId !== null) {
+ options.offerId = offerId;
+ }
if (args.sortBy?.length > 0) {
applySortByToOptions(args.sortBy, options);
}
@@ -129,10 +134,10 @@ export const useOfferRedemptions = (enterpriseUUID, offerId) => {
setIsLoading(false);
}
};
- if (offerId) {
+ if (offerId || budgetId) {
fetch();
}
- }, [enterpriseUUID, offerId, shouldTrackFetchEvents]);
+ }, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents]);
const debouncedFetchOfferRedemptions = useMemo(() => debounce(fetchOfferRedemptions, 300), [fetchOfferRedemptions]);
diff --git a/src/components/learner-credit-management/data/tests/hooks.test.js b/src/components/learner-credit-management/data/tests/hooks.test.js
index 8ab61bce2f..38ebaeaafd 100644
--- a/src/components/learner-credit-management/data/tests/hooks.test.js
+++ b/src/components/learner-credit-management/data/tests/hooks.test.js
@@ -72,6 +72,9 @@ describe('useOfferSummary', () => {
redeemedFundsOcm: NaN,
remainingFunds: 4800,
percentUtilized: 0.04,
+ offerId: 1,
+ budgetsSummary: [],
+ offerType: undefined,
};
expect(result.current).toEqual({
offerSummary: expectedResult,
@@ -83,9 +86,11 @@ describe('useOfferSummary', () => {
describe('useOfferRedemptions', () => {
it('should fetch enrollment/redemptions metadata for enterprise offer', async () => {
EnterpriseDataApiService.fetchCourseEnrollments.mockResolvedValueOnce({ data: mockOfferEnrollmentsResponse });
+ const budgetId = 'test-budget-id';
const { result, waitForNextUpdate } = renderHook(() => useOfferRedemptions(
TEST_ENTERPRISE_UUID,
mockEnterpriseOffer.id,
+ budgetId,
));
expect(result.current).toMatchObject({
@@ -119,6 +124,7 @@ describe('useOfferRedemptions', () => {
ordering: '-enrollment_date', // default sort order
searchAll: mockOfferEnrollments[0].user_email,
ignoreNullCourseListPrice: true,
+ budgetId,
};
expect(EnterpriseDataApiService.fetchCourseEnrollments).toHaveBeenCalledWith(
TEST_ENTERPRISE_UUID,
@@ -133,5 +139,7 @@ describe('useOfferRedemptions', () => {
isLoading: false,
fetchOfferRedemptions: expect.any(Function),
});
+
+ expect(expectedApiOptions.budgetId).toBe(budgetId);
});
});
diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js
index 33902d40fe..96061c8af7 100644
--- a/src/components/learner-credit-management/data/tests/utils.test.js
+++ b/src/components/learner-credit-management/data/tests/utils.test.js
@@ -1,4 +1,4 @@
-import { transformOfferSummary } from '../utils';
+import { transformOfferSummary, getBudgetStatus } from '../utils';
import { EXEC_ED_OFFER_TYPE } from '../constants';
describe('transformOfferSummary', () => {
@@ -23,6 +23,8 @@ describe('transformOfferSummary', () => {
remainingFunds: 0.0,
percentUtilized: 1.0,
offerType: EXEC_ED_OFFER_TYPE,
+ budgetsSummary: [],
+ offerId: undefined,
});
});
@@ -33,6 +35,8 @@ describe('transformOfferSummary', () => {
remainingBalance: null,
percentOfOfferSpent: null,
offerType: 'Site',
+ offerId: '123',
+ budgetsSummary: [],
};
expect(transformOfferSummary(offerSummary)).toEqual({
@@ -41,6 +45,72 @@ describe('transformOfferSummary', () => {
remainingFunds: null,
percentUtilized: null,
offerType: 'Site',
+ redeemedFundsExecEd: undefined,
+ redeemedFundsOcm: undefined,
+ offerId: '123',
+ budgetsSummary: [],
});
});
+
+ it('should handle when budgetsSummary is provided', () => {
+ const offerSummary = {
+ maxDiscount: 1000,
+ amountOfOfferSpent: 500,
+ remainingBalance: 500,
+ percentOfOfferSpent: 0.5,
+ offerType: 'Site',
+ offerId: '123',
+ budgets: [
+ {
+ id: 123,
+ start: '2022-01-01',
+ end: '2022-01-01',
+ available: 200,
+ spent: 100,
+ enterpriseSlug: 'test-enterprise',
+ }],
+ };
+
+ expect(transformOfferSummary(offerSummary)).toEqual({
+ totalFunds: 1000,
+ redeemedFunds: 500,
+ remainingFunds: 500,
+ percentUtilized: 0.5,
+ offerType: 'Site',
+ redeemedFundsExecEd: NaN,
+ redeemedFundsOcm: NaN,
+ offerId: '123',
+ budgetsSummary: [{
+ id: 123,
+ start: '2022-01-01',
+ end: '2022-01-01',
+ available: 200,
+ spent: 100,
+ enterpriseSlug: 'test-enterprise',
+ }],
+ });
+ });
+});
+
+describe('getBudgetStatus', () => {
+ it('should return "upcoming" when the current date is before the start date', () => {
+ const startDateStr = '2023-09-30';
+ const endDateStr = '2023-10-30';
+ const result = getBudgetStatus(startDateStr, endDateStr);
+ expect(result).toEqual('Upcoming');
+ });
+
+ it('should return "active" when the current date is between the start and end dates', () => {
+ const startDateStr = '2023-09-01';
+ const endDateStr = '2023-09-30';
+ const result = getBudgetStatus(startDateStr, endDateStr);
+ expect(result).toEqual('Active');
+ });
+
+ it('should return "expired" when the current date is after the end date', () => {
+ const startDateStr = '2023-08-01';
+ const endDateStr = '2023-08-31';
+ const result = getBudgetStatus(startDateStr, endDateStr);
+ expect(result).toEqual('Expired');
+ });
});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 65524c1346..5bd64d257d 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -3,6 +3,7 @@ import {
LOW_REMAINING_BALANCE_PERCENT_THRESHOLD,
NO_BALANCE_REMAINING_DOLLAR_THRESHOLD,
} from './constants';
+import { BUDGET_STATUSES } from '../../EnterpriseApp/data/constants';
/**
* Transforms offer summary from API for display in the UI, guarding
* against bad data (e.g., accounting for refunds).
@@ -12,6 +13,20 @@ import {
*/
export const transformOfferSummary = (offerSummary) => {
if (!offerSummary) { return null; }
+ const budgetsSummary = [];
+ if (offerSummary?.budgets) {
+ const budgets = offerSummary?.budgets;
+ for (let i = 0; i < budgets.length; i++) {
+ const redeemedFunds = budgets[i].amountOfPolicySpent && parseFloat(budgets[i].amountOfPolicySpent);
+ const remainingFunds = budgets[i].remainingBalance && parseFloat(budgets[i].remainingBalance);
+ const updatedBudgetDetail = {
+ redeemedFunds,
+ remainingFunds,
+ ...budgets[i],
+ };
+ budgetsSummary.push(updatedBudgetDetail);
+ }
+ }
const totalFunds = offerSummary.maxDiscount && parseFloat(offerSummary.maxDiscount);
let redeemedFunds = offerSummary.amountOfOfferSpent && parseFloat(offerSummary.amountOfOfferSpent);
@@ -38,7 +53,7 @@ export const transformOfferSummary = (offerSummary) => {
percentUtilized = Math.min(percentUtilized, 1.0);
}
const { offerType } = offerSummary;
-
+ const { offerId } = offerSummary;
return {
totalFunds,
redeemedFunds,
@@ -47,6 +62,8 @@ export const transformOfferSummary = (offerSummary) => {
remainingFunds,
percentUtilized,
offerType,
+ offerId,
+ budgetsSummary,
};
};
@@ -91,3 +108,29 @@ export const getProgressBarVariant = ({ percentUtilized, remainingFunds }) => {
}
return variant;
};
+
+// Utility function to check if the ID is a UUID
+export const isUUID = (id) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);
+
+// Utility function to check the budget status
+export const getBudgetStatus = (startDateStr, endDateStr) => {
+ const currentDate = new Date();
+ const startDate = new Date(startDateStr);
+ const endDate = new Date(endDateStr);
+
+ if (currentDate < startDate) {
+ return BUDGET_STATUSES.upcoming;
+ }
+ if (currentDate >= startDate && currentDate <= endDate) {
+ return BUDGET_STATUSES.active;
+ }
+ return BUDGET_STATUSES.expired;
+};
+
+export const formatPrice = (price) => {
+ const USDollar = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD',
+ });
+ return USDollar.format(Math.abs(price));
+};
diff --git a/src/components/learner-credit-management/index.js b/src/components/learner-credit-management/index.js
deleted file mode 100644
index 271f4453ed..0000000000
--- a/src/components/learner-credit-management/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import MultipleBudgetsPage from './MultipleBudgetsPage';
-
-export default MultipleBudgetsPage;
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
new file mode 100644
index 0000000000..2ce695b9a7
--- /dev/null
+++ b/src/components/learner-credit-management/index.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import { Route } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import MultipleBudgetsPage from './MultipleBudgetsPage';
+import BudgetDetailPage from './BudgetDetailPage';
+
+const LearnerCreditManagementRoutes = ({ baseUrl }) => (
+ <>
+
+
+
+ >
+);
+
+LearnerCreditManagementRoutes.propTypes = {
+ baseUrl: PropTypes.string.isRequired,
+};
+
+export default LearnerCreditManagementRoutes;
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index 7d8f349bda..d8aa511a4a 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -1,21 +1,20 @@
/* eslint-disable react/prop-types */
import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
-import userEvent from '@testing-library/user-event';
import configureMockStore from 'redux-mock-store';
import dayjs from 'dayjs';
import {
screen,
render,
- waitFor,
} from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import BudgetCard from '../BudgetCard-V2';
import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
-import { EXEC_ED_OFFER_TYPE } from '../data/constants';
+import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
jest.mock('../data/hooks');
useOfferSummary.mockReturnValue({
@@ -47,20 +46,15 @@ const mockEnterpriseOfferId = '123';
const mockEnterpriseOfferEnrollmentId = 456;
const mockOfferDisplayName = 'Test Enterprise Offer';
-const mockOfferSummary = {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerType: EXEC_ED_OFFER_TYPE,
-};
const BudgetCardWrapper = ({ ...rest }) => (
-
-
-
-
-
+
+
+
+
+
+
+
);
describe(' ', () => {
@@ -88,6 +82,16 @@ describe(' ', () => {
remainingFunds: 4800,
percentUtilized: 0.04,
offerType: 'Site',
+ budgetsSummary: [
+ {
+ id: 123,
+ start: '2022-01-01',
+ end: '2022-01-01',
+ available: 200,
+ spent: 100,
+ enterpriseSlug: enterpriseId,
+ },
+ ],
},
});
useOfferRedemptions.mockReturnValue({
@@ -106,42 +110,112 @@ describe(' ', () => {
/>);
expect(screen.getByText('Overview'));
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
- expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`));
const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
const elementsWithTestId = screen.getAllByTestId('offer-date');
const firstElementWithTestId = elementsWithTestId[0];
expect(firstElementWithTestId).toHaveTextContent(formattedString);
});
- it('displays table on clicking view budget', async () => {
+ it('renders SubBudgetCard when offerType is ecommerce', () => {
const mockOffer = {
id: mockEnterpriseOfferId,
name: mockOfferDisplayName,
start: '2022-01-01',
end: '2023-01-01',
+ offerType: BUDGET_TYPES.ecommerce,
+ };
+ const mockOfferRedemption = {
+ created: '2022-02-01',
+ enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
};
useOfferSummary.mockReturnValue({
isLoading: false,
- offerSummary: mockOfferSummary,
+ offerSummary: {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerType: 'learner_credit',
+ budgetsSummary: [
+ {
+ id: 123,
+ start: '2022-01-01',
+ end: '2022-01-01',
+ available: 200,
+ spent: 100,
+ enterpriseSlug: enterpriseId,
+ },
+ ],
+ },
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
+ results: [mockOfferRedemption],
+ itemCount: 1,
+ pageCount: 1,
},
fetchOfferRedemptions: jest.fn(),
});
+
render( );
- const elementsWithTestId = screen.getAllByTestId('view-budget');
- const firstElementWithTestId = elementsWithTestId[0];
- await waitFor(() => userEvent.click(firstElementWithTestId));
- expect(screen.getByText('No results found'));
+
+ expect(screen.getByTestId('view-budget')).toBeInTheDocument();
+ });
+
+ it('renders SubBudgetCard when offerType is not ecommerce', () => {
+ const mockOffer = {
+ id: mockEnterpriseOfferId,
+ name: mockOfferDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ offerType: 'otherOfferType',
+ };
+ const mockOfferRedemption = {
+ created: '2022-02-01',
+ enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
+ };
+ useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerType: 'learner_credit',
+ budgetsSummary: [
+ {
+ id: 123,
+ start: '2022-01-01',
+ end: '2022-01-01',
+ available: 200,
+ spent: 100,
+ enterpriseSlug: enterpriseId,
+ },
+ ],
+ },
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ results: [mockOfferRedemption],
+ itemCount: 1,
+ pageCount: 1,
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+
+ render( );
+
+ expect(screen.getByTestId('view-budget')).toBeInTheDocument();
});
});
});
From 9cc4dad8060c0724d6b9ae87b4fb19e2fba6e910 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 28 Sep 2023 08:59:17 -0400
Subject: [PATCH 030/124] fix: ensure highlights routes appear when learner
credit is available (#1043)
---
src/components/EnterpriseApp/EnterpriseAppRoutes.jsx | 7 ++++---
src/components/learner-credit-management/index.jsx | 11 ++++++-----
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
index e46986950f..07b10958cb 100644
--- a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
+++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
@@ -98,8 +98,9 @@ const EnterpriseAppRoutes = ({
/>
{canManageLearnerCredit && (
-
)}
@@ -110,7 +111,7 @@ const EnterpriseAppRoutes = ({
/>
)}
-
+
);
};
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
index 2ce695b9a7..e3b88632ff 100644
--- a/src/components/learner-credit-management/index.jsx
+++ b/src/components/learner-credit-management/index.jsx
@@ -1,28 +1,29 @@
import React from 'react';
import { Route } from 'react-router-dom';
import PropTypes from 'prop-types';
-import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
import MultipleBudgetsPage from './MultipleBudgetsPage';
import BudgetDetailPage from './BudgetDetailPage';
-const LearnerCreditManagementRoutes = ({ baseUrl }) => (
+const LearnerCreditManagementRoutes = ({ match }) => (
<>
>
);
LearnerCreditManagementRoutes.propTypes = {
- baseUrl: PropTypes.string.isRequired,
+ match: PropTypes.shape({
+ path: PropTypes.string.isRequired,
+ }).isRequired,
};
export default LearnerCreditManagementRoutes;
From 47938adc92a02c7b4cb920cb42b7449d9477d7a6 Mon Sep 17 00:00:00 2001
From: Mashal Malik <107556986+Mashal-m@users.noreply.github.com>
Date: Tue, 3 Oct 2023 14:07:16 +0500
Subject: [PATCH 031/124] refactor: add @openedx in renovate automate
configuration (#1045)
* refactor: add @openedx in renovate automate configuration
* refactor: updated util getBudgetStatus and respective test to resolve test failure
---------
Co-authored-by: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
---
renovate.json | 2 +-
.../learner-credit-management/data/tests/utils.test.js | 6 ++++--
src/components/learner-credit-management/data/utils.js | 3 +--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/renovate.json b/renovate.json
index b332af838c..8f6a3afef6 100644
--- a/renovate.json
+++ b/renovate.json
@@ -8,7 +8,7 @@
"rebaseStalePrs": true,
"packageRules": [
{
- "matchPackagePatterns": ["@edx"],
+ "matchPackagePatterns": ["@edx", "@openedx"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js
index 96061c8af7..ab19a94d27 100644
--- a/src/components/learner-credit-management/data/tests/utils.test.js
+++ b/src/components/learner-credit-management/data/tests/utils.test.js
@@ -96,14 +96,16 @@ describe('getBudgetStatus', () => {
it('should return "upcoming" when the current date is before the start date', () => {
const startDateStr = '2023-09-30';
const endDateStr = '2023-10-30';
- const result = getBudgetStatus(startDateStr, endDateStr);
+ const currentDateStr = '2023-09-28';
+ const result = getBudgetStatus(startDateStr, endDateStr, new Date(currentDateStr));
expect(result).toEqual('Upcoming');
});
it('should return "active" when the current date is between the start and end dates', () => {
const startDateStr = '2023-09-01';
const endDateStr = '2023-09-30';
- const result = getBudgetStatus(startDateStr, endDateStr);
+ const currentDateStr = '2023-09-05';
+ const result = getBudgetStatus(startDateStr, endDateStr, new Date(currentDateStr));
expect(result).toEqual('Active');
});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 5bd64d257d..c42cb4039c 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -113,8 +113,7 @@ export const getProgressBarVariant = ({ percentUtilized, remainingFunds }) => {
export const isUUID = (id) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);
// Utility function to check the budget status
-export const getBudgetStatus = (startDateStr, endDateStr) => {
- const currentDate = new Date();
+export const getBudgetStatus = (startDateStr, endDateStr, currentDate = new Date()) => {
const startDate = new Date(startDateStr);
const endDate = new Date(endDateStr);
From 4d7cb88e249f6637c32e504aa3994234ef8794e4 Mon Sep 17 00:00:00 2001
From: jajjibhai008
Date: Mon, 2 Oct 2023 16:58:49 +0500
Subject: [PATCH 032/124] feat: label and order each budget card on lCM page
---
.../EnterpriseApp/data/constants.js | 2 +-
.../MultipleBudgetsPicker.jsx | 52 +++++++------
.../SubBudgetCard.jsx | 24 +++---
.../data/tests/utils.test.js | 77 +++++++++++++++----
.../learner-credit-management/data/utils.js | 54 ++++++++++++-
.../tests/BudgetCard.test.jsx | 2 +-
.../tests/MultipleBudgetsPage.test.jsx | 25 +++++-
7 files changed, 180 insertions(+), 56 deletions(-)
diff --git a/src/components/EnterpriseApp/data/constants.js b/src/components/EnterpriseApp/data/constants.js
index 6feaac51f7..1773fd73e2 100644
--- a/src/components/EnterpriseApp/data/constants.js
+++ b/src/components/EnterpriseApp/data/constants.js
@@ -17,7 +17,7 @@ export const ROUTE_NAMES = {
export const BUDGET_STATUSES = {
active: 'Active',
expired: 'Expired',
- upcoming: 'Upcoming',
+ scheduled: 'Scheduled',
};
export const BUDGET_TYPES = {
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index db6f178f2a..44407531c0 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -7,36 +7,40 @@ import {
} from '@edx/paragon';
import BudgetCard from './BudgetCard-V2';
+import { orderOffers } from './data/utils';
const MultipleBudgetsPicker = ({
offers,
enterpriseUUID,
enterpriseSlug,
enableLearnerPortal,
-}) => (
-
-
- Budgets
-
-
-
-
- {offers.map(offer => (
-
- ))}
-
-
-
-
-);
+}) => {
+ const orderedOffers = orderOffers(offers);
+ return (
+
+
+ Budgets
+
+
+
+
+ {orderedOffers?.map(offer => (
+
+ ))}
+
+
+
+
+ );
+};
MultipleBudgetsPicker.propTypes = {
offers: PropTypes.arrayOf(PropTypes.shape()).isRequired,
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
index d3360cea43..3841aebea3 100644
--- a/src/components/learner-credit-management/SubBudgetCard.jsx
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -6,6 +6,8 @@ import {
Button,
Row,
Col,
+ Badge,
+ Stack,
} from '@edx/paragon';
import { BUDGET_STATUSES, ROUTE_NAMES } from '../EnterpriseApp/data/constants';
@@ -21,9 +23,8 @@ const SubBudgetCard = ({
enterpriseSlug,
isLoading,
}) => {
- const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
- const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
- const budgetStatus = getBudgetStatus(start, end);
+ const budgetLabel = getBudgetStatus(start, end);
+ const formattedDate = dayjs(budgetLabel?.date).format('MMMM D, YYYY');
const renderActions = (budgetId) => (
{
const subtitle = (
-
+
+ {budgetLabel.status}
- {formattedStartDate} - {formattedExpirationDate}
+ {budgetLabel.term} {formattedDate}
-
+
);
return (
@@ -50,10 +52,10 @@ const SubBudgetCard = ({
subtitle={subtitle}
className="mb-3"
actions={
- budgetStatus !== BUDGET_STATUSES.upcoming
- ? renderActions(budgetId)
- : undefined
- }
+ budgetLabel.status !== BUDGET_STATUSES.scheduled
+ ? renderActions(budgetId)
+ : undefined
+ }
/>
);
};
@@ -83,7 +85,7 @@ const SubBudgetCard = ({
>
{renderCardHeader(displayName || 'Overview', id)}
- {budgetStatus !== BUDGET_STATUSES.upcoming && renderCardSection(available, spent)}
+ {budgetLabel.status !== BUDGET_STATUSES.scheduled && renderCardSection(available, spent)}
);
diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js
index ab19a94d27..fa4ec2bb23 100644
--- a/src/components/learner-credit-management/data/tests/utils.test.js
+++ b/src/components/learner-credit-management/data/tests/utils.test.js
@@ -1,4 +1,4 @@
-import { transformOfferSummary, getBudgetStatus } from '../utils';
+import { transformOfferSummary, getBudgetStatus, orderOffers } from '../utils';
import { EXEC_ED_OFFER_TYPE } from '../constants';
describe('transformOfferSummary', () => {
@@ -93,26 +93,77 @@ describe('transformOfferSummary', () => {
});
describe('getBudgetStatus', () => {
- it('should return "upcoming" when the current date is before the start date', () => {
- const startDateStr = '2023-09-30';
- const endDateStr = '2023-10-30';
+ it('should return "Scheduled" when the current date is before the start date', () => {
+ const startDateStr = '2024-09-30';
+ const endDateStr = '2027-10-30';
const currentDateStr = '2023-09-28';
const result = getBudgetStatus(startDateStr, endDateStr, new Date(currentDateStr));
- expect(result).toEqual('Upcoming');
+ expect(result.status).toEqual('Scheduled');
});
- it('should return "active" when the current date is between the start and end dates', () => {
- const startDateStr = '2023-09-01';
- const endDateStr = '2023-09-30';
- const currentDateStr = '2023-09-05';
+ it('should return "Active" when the current date is between the start and end dates', () => {
+ const startDateStr = '2023-08-01';
+ const endDateStr = '2027-10-30';
+ const currentDateStr = '2023-09-28';
const result = getBudgetStatus(startDateStr, endDateStr, new Date(currentDateStr));
- expect(result).toEqual('Active');
+ expect(result.status).toEqual('Active');
});
- it('should return "expired" when the current date is after the end date', () => {
+ it('should return "Expired" when the current date is after the end date', () => {
const startDateStr = '2023-08-01';
const endDateStr = '2023-08-31';
- const result = getBudgetStatus(startDateStr, endDateStr);
- expect(result).toEqual('Expired');
+ const currentDateStr = '2023-09-28';
+ const result = getBudgetStatus(startDateStr, endDateStr, new Date(currentDateStr));
+ expect(result.status).toEqual('Expired');
+ });
+});
+
+// Example offer objects for testing
+const offers = [
+ {
+ name: 'Offer 1',
+ start: '2023-01-01T00:00:00Z',
+ end: '2023-01-10T00:00:00Z',
+ },
+ {
+ name: 'Offer 2',
+ start: '2022-12-01T00:00:00Z',
+ end: '2022-12-20T00:00:00Z',
+ },
+ {
+ name: 'Offer 3',
+ start: '2023-02-01T00:00:00Z',
+ end: '2023-02-15T00:00:00Z',
+ },
+ {
+ name: 'Offer 4',
+ start: '2023-01-15T00:00:00Z',
+ end: '2023-01-25T00:00:00Z',
+ },
+];
+
+describe('orderOffers', () => {
+ it('should sort offers correctly', () => {
+ const sortedOffers = orderOffers(offers);
+
+ // Expected order: Active offers (Offer 2), Upcoming offers (Offer 1, Offer 4), Expired offers (Offer 3)
+ expect(sortedOffers.map((offer) => offer.name)).toEqual(['Offer 2', 'Offer 1', 'Offer 4', 'Offer 3']);
+ });
+
+ it('should handle empty input', () => {
+ const sortedOffers = orderOffers([]);
+ expect(sortedOffers).toEqual([]);
+ });
+
+ it('should handle offers with the same status and end date', () => {
+ const duplicateOffers = [
+ { name: 'Offer A', start: '2023-01-01T00:00:00Z', end: '2023-01-15T00:00:00Z' },
+ { name: 'Offer B', start: '2023-01-01T00:00:00Z', end: '2023-01-15T00:00:00Z' },
+ ];
+
+ const sortedOffers = orderOffers(duplicateOffers);
+
+ // Since both offers have the same status ("active") and end date, they should be sorted alphabetically by name.
+ expect(sortedOffers.map((offer) => offer.name)).toEqual(['Offer A', 'Offer B']);
});
});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index c42cb4039c..376479f6a5 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -118,12 +118,27 @@ export const getBudgetStatus = (startDateStr, endDateStr, currentDate = new Date
const endDate = new Date(endDateStr);
if (currentDate < startDate) {
- return BUDGET_STATUSES.upcoming;
+ return {
+ status: BUDGET_STATUSES.scheduled,
+ badgeVariant: 'secondary',
+ term: 'Starts',
+ date: startDateStr,
+ };
}
if (currentDate >= startDate && currentDate <= endDate) {
- return BUDGET_STATUSES.active;
+ return {
+ status: BUDGET_STATUSES.active,
+ badgeVariant: 'primary',
+ term: 'Expires',
+ date: endDateStr,
+ };
}
- return BUDGET_STATUSES.expired;
+ return {
+ status: BUDGET_STATUSES.expired,
+ badgeVariant: 'light',
+ term: 'Expired',
+ date: endDateStr,
+ };
};
export const formatPrice = (price) => {
@@ -133,3 +148,36 @@ export const formatPrice = (price) => {
});
return USDollar.format(Math.abs(price));
};
+
+/**
+ * Orders a list of offers based on their status, end date, and name.
+ * Active offers come first, followed by scheduled offers, and then expired offers.
+ * Within each status, offers are sorted by their end date and name.
+ *
+ * @param {Array} offers - An array of offer objects.
+ * @returns {Array} - The sorted array of offer objects.
+ */
+export const orderOffers = (offers) => {
+ const statusOrder = {
+ Active: 0,
+ Scheduled: 1,
+ Expired: 2,
+ };
+
+ offers?.sort((offerA, offerB) => {
+ const statusA = getBudgetStatus(offerA.start, offerA.end).status;
+ const statusB = getBudgetStatus(offerB.start, offerB.end).status;
+
+ if (statusOrder[statusA] !== statusOrder[statusB]) {
+ return statusOrder[statusA] - statusOrder[statusB];
+ }
+
+ if (offerA.end !== offerB.end) {
+ return offerA.end.localeCompare(offerB.end);
+ }
+
+ return offerA.name.localeCompare(offerB.name);
+ });
+
+ return offers;
+};
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index d8aa511a4a..c1b20b9958 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -110,7 +110,7 @@ describe('
', () => {
/>);
expect(screen.getByText('Overview'));
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
- const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
+ const formattedString = `Expired ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
const elementsWithTestId = screen.getAllByTestId('offer-date');
const firstElementWithTestId = elementsWithTestId[0];
expect(firstElementWithTestId).toHaveTextContent(formattedString);
diff --git a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
index 8b22a9d4ec..785da8899d 100644
--- a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
+++ b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
@@ -23,10 +23,21 @@ const initialStore = {
const store = getMockStore({ ...initialStore });
const enterpriseUUID = '1234';
-const defaultEnterpriseSubsidiesContextValue = {
- offers: [],
+const emptyOffersContextValue = {
+ offers: [], // Empty offers array
};
+const defaultEnterpriseSubsidiesContextValue = {
+ offers: [{
+ source: 'subsidy',
+ id: '392f1fe1-ee91-4f44-b174-13ecf59866eb',
+ name: 'Subsidy 2 for Executive Education (2U) Integration QA',
+ start: '2023-06-07T15:38:29Z',
+ end: '2024-06-07T15:38:30Z',
+ isCurrent: true,
+ },
+ ],
+};
const MultipleBudgetsPageWrapper = ({
enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
...rest
@@ -40,8 +51,16 @@ const MultipleBudgetsPageWrapper = ({
describe('
', () => {
it('No budgets for your organization', () => {
- render(
);
+ render(
);
expect(screen.getByText('No budgets for your organization'));
expect(screen.getByText('Contact support'));
});
+ it('budgets for your organization', () => {
+ render(
);
+ expect(screen.getByText('Budgets'));
+ });
});
From 5f1cb88c259bce245f38a6bfd4f1ed75bf295833 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 5 Oct 2023 08:38:11 -0400
Subject: [PATCH 033/124] feat: add activity and catalog tabs to budget detail
page in LCM (#1048)
---
.eslintrc.js | 7 +
...ntHighlightCatalogVisibilityRadioInput.jsx | 2 +-
...hlightCatalogVisibilityRadioInput.test.jsx | 76 ++++--
.../ContentHighlights/data/constants.js | 2 +-
.../data/tests/BudgetDetailPage.test.jsx | 138 ----------
.../BudgetCard-V2.jsx | 85 ++++---
.../learner-credit-management/BudgetCard.jsx | 2 +-
.../BudgetDetailActivityTabContents.jsx | 45 ++++
.../BudgetDetailCatalogTabContents.jsx | 13 +
.../BudgetDetailPage.jsx | 44 +---
.../BudgetDetailTabsAndRoutes.jsx | 103 ++++++++
.../EmailAddressTableCell.jsx | 7 +-
.../LearnerCreditAllocationTable.jsx | 159 +++++-------
.../LearnerCreditManagement.jsx | 2 +-
.../SpendTableEmptyState.jsx | 12 +
.../SpendTableEnrollmentDetails.jsx | 57 +++++
.../data/constants.js | 8 +
.../data/hooks/index.js | 3 +
.../data/hooks/useBudgetDetailTabs.jsx | 52 ++++
.../useOfferRedemptions.js} | 51 +---
.../data/hooks/useOfferSummary.js | 41 +++
.../learner-credit-management/data/index.js | 3 +
.../learner-credit-management/index.jsx | 2 +-
.../tests/BudgetCard.test.jsx | 8 +-
.../tests/BudgetDetailPage.test.jsx | 240 ++++++++++++++++++
.../tests/EmailAddressTableCell.test.jsx | 25 +-
.../LearnerCreditAllocationTable.test.jsx | 60 +++--
.../ErrorReporting/tests/SyncHistory.test.jsx | 1 -
src/eventTracking.js | 6 +
29 files changed, 841 insertions(+), 413 deletions(-)
delete mode 100644 src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
create mode 100644 src/components/learner-credit-management/SpendTableEmptyState.jsx
create mode 100644 src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/index.js
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx
rename src/components/learner-credit-management/data/{hooks.js => hooks/useOfferRedemptions.js} (74%)
create mode 100644 src/components/learner-credit-management/data/hooks/useOfferSummary.js
create mode 100644 src/components/learner-credit-management/data/index.js
create mode 100644 src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
diff --git a/.eslintrc.js b/.eslintrc.js
index cf41a2a1c3..2e67ddb949 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -23,6 +23,13 @@ config.overrides = [
]
}
},
+ {
+ files: ['*.test.js', '*.test.jsx'],
+ rules: {
+ 'react/prop-types': 'off',
+ 'react/jsx-no-constructed-context-values': 'off',
+ },
+ },
];
diff --git a/src/components/ContentHighlights/CatalogVisibility/ContentHighlightCatalogVisibilityRadioInput.jsx b/src/components/ContentHighlights/CatalogVisibility/ContentHighlightCatalogVisibilityRadioInput.jsx
index 45c320f148..0421c72394 100644
--- a/src/components/ContentHighlights/CatalogVisibility/ContentHighlightCatalogVisibilityRadioInput.jsx
+++ b/src/components/ContentHighlights/CatalogVisibility/ContentHighlightCatalogVisibilityRadioInput.jsx
@@ -82,11 +82,11 @@ const ContentHighlightCatalogVisibilityRadioInput = () => {
setCatalogVisibilityAlert({
isOpen: false,
});
+ setValue(newTabValue);
dispatch(enterpriseCurationActions.setHighlightToast(ALERT_TEXT.TOAST_TEXT.catalogVisibility));
history.push(location.pathname, {
highlightToast: true,
});
- setValue(newTabValue);
}
} catch (error) {
logError(error);
diff --git a/src/components/ContentHighlights/CatalogVisibility/tests/ContentHighlightCatalogVisibilityRadioInput.test.jsx b/src/components/ContentHighlights/CatalogVisibility/tests/ContentHighlightCatalogVisibilityRadioInput.test.jsx
index eb7bdb2174..0fc3ed1782 100644
--- a/src/components/ContentHighlights/CatalogVisibility/tests/ContentHighlightCatalogVisibilityRadioInput.test.jsx
+++ b/src/components/ContentHighlights/CatalogVisibility/tests/ContentHighlightCatalogVisibilityRadioInput.test.jsx
@@ -8,7 +8,7 @@ import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import { camelCaseObject } from '@edx/frontend-platform';
import userEvent from '@testing-library/user-event';
-import { act } from 'react-test-renderer';
+import { act } from 'react-dom/test-utils';
import { useContentHighlightsContext } from '../../data/hooks';
import ContentHighlightCatalogVisibilityRadioInput from '../ContentHighlightCatalogVisibilityRadioInput';
import { EnterpriseAppContext } from '../../../EnterpriseApp/EnterpriseAppContextProvider';
@@ -34,7 +34,6 @@ const initialEnterpriseAppContextValue = {
updateEnterpriseCuration: jest.fn(),
dispatch: jest.fn(),
},
-
};
const ContentHighlightCatalogVisibilityRadioInputWrapper = ({
@@ -60,17 +59,14 @@ const ContentHighlightCatalogVisibilityRadioInputWrapper = ({
jest.mock('../../../../data/services/EnterpriseCatalogApiService');
-jest.mock('@edx/frontend-enterprise-utils', () => {
- const originalModule = jest.requireActual('@edx/frontend-enterprise-utils');
- return ({
- ...originalModule,
- sendEnterpriseTrackEvent: jest.fn(),
- });
-});
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-utils'),
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
jest.mock('../../data/hooks');
useContentHighlightsContext.mockReturnValue({
- setCatalogVisibilityAlert: false,
+ setCatalogVisibilityAlert: jest.fn(),
enterpriseCuration: {
enterpriseCuration: {
highlightSets: [],
@@ -81,6 +77,15 @@ useContentHighlightsContext.mockReturnValue({
},
});
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useHistory: jest.fn().mockReturnValue({
+ location: {
+ pathname: '/enterprise/test-enterprise/content-highlights',
+ },
+ }),
+}));
+
describe('ContentHighlightCatalogVisibilityRadioInput1', () => {
beforeEach(() => {
jest.clearAllMocks();
@@ -91,12 +96,26 @@ describe('ContentHighlightCatalogVisibilityRadioInput1', () => {
expect(screen.getByText(BUTTON_TEXT.catalogVisibilityRadio2)).toBeTruthy();
});
it('Spinner 2 shows on radio 2 click', async () => {
- EnterpriseCatalogApiService.updateEnterpriseCurationConfig.mockResolvedValue({
- data: {
- canOnlyViewHighlightSets: true,
+ const mockUpdateEnterpriseCuration = jest.fn();
+ const mockEnterpriseAppContextValue = {
+ enterpriseCuration: {
+ ...initialEnterpriseAppContextValue.enterpriseCuration,
+ enterpriseCuration: {
+ ...initialEnterpriseAppContextValue.enterpriseCuration.enterpriseCuration,
+ highlightSets: [{ uuid: 'test-uuid' }],
+ },
+ updateEnterpriseCuration: mockUpdateEnterpriseCuration,
},
+ };
+ mockUpdateEnterpriseCuration.mockResolvedValue({
+ canOnlyViewHighlightSets: true,
});
- renderWithRouter( );
+ renderWithRouter((
+
+ ));
const viewHighlightedContentButton = screen.getByText(BUTTON_TEXT.catalogVisibilityRadio2);
const radio2LoadingStateInitial = screen.queryByTestId(`${LEARNER_PORTAL_CATALOG_VISIBILITY.HIGHLIGHTED_CONTENT.value}-form-control`);
@@ -105,16 +124,14 @@ describe('ContentHighlightCatalogVisibilityRadioInput1', () => {
expect(radio2LoadingStateInitial).toBeFalsy();
expect(radio1CheckedState).toBeTruthy();
- await act(() => {
+ await act(async () => {
userEvent.click(viewHighlightedContentButton);
});
- await waitFor(() => EnterpriseCatalogApiService.updateEnterpriseCurationConfig({
- canOnlyViewHighlightSets: true,
- }).then(data => data));
-
- expect(EnterpriseCatalogApiService.updateEnterpriseCurationConfig).toHaveBeenCalledTimes(1);
- expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ await waitFor(() => {
+ expect(mockUpdateEnterpriseCuration).toHaveBeenCalledTimes(1);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ });
});
it('Spinner 1 shows on radio 1 click', async () => {
EnterpriseCatalogApiService.updateEnterpriseCurationConfig.mockResolvedValue({
@@ -122,6 +139,7 @@ describe('ContentHighlightCatalogVisibilityRadioInput1', () => {
canOnlyViewHighlightSets: false,
},
});
+ const mockUpdateEnterpriseCuration = jest.fn();
const viewingOnlyHighlightedContentContext = {
...initialEnterpriseAppContextValue,
enterpriseCuration: {
@@ -130,8 +148,12 @@ describe('ContentHighlightCatalogVisibilityRadioInput1', () => {
...initialEnterpriseAppContextValue.enterpriseCuration.enterpriseCuration,
canOnlyViewHighlightSets: true,
},
+ updateEnterpriseCuration: mockUpdateEnterpriseCuration,
},
};
+ mockUpdateEnterpriseCuration.mockResolvedValue({
+ canOnlyViewHighlightSets: true,
+ });
renderWithRouter(
{
expect(radio1LoadingStateInitial).toBeFalsy();
expect(radio2CheckedState).toBeTruthy();
- await act(() => {
+ await act(async () => {
userEvent.click(viewAllContentButton);
});
- await waitFor(() => EnterpriseCatalogApiService.updateEnterpriseCurationConfig({
- canOnlyViewHighlightSets: false,
- }).then(data => data));
-
- expect(EnterpriseCatalogApiService.updateEnterpriseCurationConfig).toHaveBeenCalledTimes(1);
- expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ await waitFor(() => {
+ expect(mockUpdateEnterpriseCuration).toHaveBeenCalledTimes(1);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ });
});
});
diff --git a/src/components/ContentHighlights/data/constants.js b/src/components/ContentHighlights/data/constants.js
index 2695700817..93269fab0a 100644
--- a/src/components/ContentHighlights/data/constants.js
+++ b/src/components/ContentHighlights/data/constants.js
@@ -15,7 +15,7 @@ export const sanitizeAndParseHTML = (htmlString) => {
// Test will fail as additional check to ensure this is set to false before pushing PR
export const TEST_FLAG = false;
// Test entepriseId for Content Highlights to display card selections and confirmation
-export const testEnterpriseId = 'f23ccd7d-fbbb-411a-824e-c2861942aac0';
+export const testEnterpriseId = '943b1234-58cf-4376-b8e0-0efcbf4bfdf9';
// function that passes through enterpriseId if TEST_FLAG is false, otherwise returns local testing enterpriseId
export const ENABLE_TESTING = (enterpriseId, enableTest = TEST_FLAG) => {
if (enableTest) {
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx b/src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx
deleted file mode 100644
index 483263fdce..0000000000
--- a/src/components/EnterpriseSubsidiesContext/data/tests/BudgetDetailPage.test.jsx
+++ /dev/null
@@ -1,138 +0,0 @@
-/* eslint-disable react/prop-types */
-import React from 'react';
-import { Provider } from 'react-redux';
-import thunk from 'redux-thunk';
-import configureMockStore from 'redux-mock-store';
-import {
- screen,
- render,
-} from '@testing-library/react';
-import '@testing-library/jest-dom/extend-expect';
-
-import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { MemoryRouter } from 'react-router-dom';
-import BudgetDetailPage from '../../../learner-credit-management/BudgetDetailPage';
-import { useOfferSummary, useOfferRedemptions } from '../../../learner-credit-management/data/hooks';
-import { EXEC_ED_OFFER_TYPE } from '../../../learner-credit-management/data/constants';
-import { EnterpriseSubsidiesContext } from '../..';
-
-jest.mock('../../../learner-credit-management/data/hooks');
-
-useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: null,
-});
-useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
- },
- fetchOfferRedemptions: jest.fn(),
-});
-
-const mockStore = configureMockStore([thunk]);
-const getMockStore = store => mockStore(store);
-const enterpriseId = 'test-enterprise';
-const enterpriseUUID = '1234';
-const initialStore = {
- portalConfiguration: {
- enterpriseId,
- enterpriseSlug: enterpriseId,
-
- },
-};
-const store = getMockStore({ ...initialStore });
-
-const mockEnterpriseOfferId = '123';
-
-const mockOfferDisplayName = 'Test Enterprise Offer';
-const mockOfferSummary = {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerType: EXEC_ED_OFFER_TYPE,
-};
-
-const defaultEnterpriseSubsidiesContextValue = {
- isLoading: false,
-};
-
-const BudgetDetailPageWrapper = ({
- enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
- ...rest
-}) => (
-
-
-
-
-
-
-
-
-
-
-);
-
-describe(' ', () => {
- describe('with enterprise offer', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('displays table on clicking view budget', async () => {
- const mockOffer = {
- id: mockEnterpriseOfferId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- };
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: mockOfferSummary,
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
- },
- fetchOfferRedemptions: jest.fn(),
- });
- render( );
- expect(screen.getByText('Learner Credit Management'));
- expect(screen.getByText('Overview'));
- expect(screen.getByText('No results found'));
- });
-
- it('displays loading message while loading data', async () => {
- useOfferSummary.mockReturnValue({
- isLoading: true,
- offerSummary: null,
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: true,
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
- },
- fetchOfferRedemptions: jest.fn(),
- });
-
- render( );
-
- expect(screen.getByText('loading'));
- });
- });
-});
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
index e6780a61db..3be534f1dd 100644
--- a/src/components/learner-credit-management/BudgetCard-V2.jsx
+++ b/src/components/learner-credit-management/BudgetCard-V2.jsx
@@ -1,11 +1,17 @@
-/* eslint-disable react/jsx-no-useless-fragment */
-/* eslint-disable no-nested-ternary */
import React from 'react';
import PropTypes from 'prop-types';
-import { useOfferSummary } from './data/hooks';
+import { useOfferSummary } from './data';
import SubBudgetCard from './SubBudgetCard';
import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
+/**
+ * Renders one or more budget cards for the given offer (enterprise or Subsidy from enterprise-subsidy). If the offer is
+ * an enterprise offer, it will render a single card. If the offer is a Subsidy, it will render one card for
+ * each associated budget.
+ *
+ * @param {*} offer Represents either an enterprise offer or a Subsidy (enterprise-subsidy).
+ * @returns Budget card component(s).
+ */
const BudgetCard = ({
offer,
enterpriseUUID,
@@ -13,48 +19,49 @@ const BudgetCard = ({
offerType,
displayName,
}) => {
- const {
- start,
- end,
- } = offer;
+ const { start, end } = offer;
const {
isLoading: isLoadingOfferSummary,
offerSummary,
} = useOfferSummary(enterpriseUUID, offer);
- return (
- <>
- {offerType === BUDGET_TYPES.ecommerce ? (
-
- ) : (
- <>
- {offerSummary?.budgetsSummary?.map((budget) => (
-
- ))}
- >
- )}
- >
- );
+ // Enterprise Offers will always have a single budget, so we can render a single card.
+ if (offerType === BUDGET_TYPES.ecommerce) {
+ return (
+
+ );
+ }
+
+ // We're now dealing with a Subsidy (enterprise-subsidy), but the analytics API isn't aware of any
+ // associated budgets; nothing should display.
+ if (!offerSummary?.budgetsSummary) {
+ return null;
+ }
+
+ // Render a card for each associated budget with the Subsidy (enterprise-subsidy)
+ return offerSummary.budgetsSummary.map((budget) => (
+
+ ));
};
BudgetCard.propTypes = {
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index 247769c715..caef8e13c4 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -11,7 +11,7 @@ import {
} from '@edx/paragon';
import { getCourseProductLineAbbreviation } from '../../utils';
-import { useOfferRedemptions, useOfferSummary } from './data/hooks';
+import { useOfferRedemptions, useOfferSummary } from './data';
import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
new file mode 100644
index 0000000000..ce5b1c0c0a
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { useParams } from 'react-router-dom';
+
+import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
+import { useOfferRedemptions } from './data';
+
+const BudgetDetailActivityTabContents = ({
+ enterpriseUUID,
+ enterpriseSlug,
+ enableLearnerPortal,
+}) => {
+ const { budgetId } = useParams();
+ const {
+ isLoading: isLoadingOfferRedemptions,
+ offerRedemptions,
+ fetchOfferRedemptions,
+ } = useOfferRedemptions(enterpriseUUID, budgetId);
+
+ return (
+
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseUUID: state.portalConfiguration.enterpriseId,
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
+});
+
+BudgetDetailActivityTabContents.propTypes = {
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
+};
+
+export default connect(mapStateToProps)(BudgetDetailActivityTabContents);
diff --git a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
new file mode 100644
index 0000000000..83745cdd7b
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import { Row, Col } from '@edx/paragon';
+
+const BudgetDetailCatalogTabContents = () => (
+
+
+ Budget Name
+ TODO
+
+
+);
+
+export default BudgetDetailCatalogTabContents;
diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx
index ad90b5ae89..29b83b4058 100644
--- a/src/components/learner-credit-management/BudgetDetailPage.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPage.jsx
@@ -8,47 +8,34 @@ import {
} from '@edx/paragon';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
-import { useParams, Link } from 'react-router-dom';
+import { Link } from 'react-router-dom';
import Hero from '../Hero';
import LoadingMessage from '../LoadingMessage';
import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
-
-import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import { useOfferRedemptions } from './data/hooks';
-import { isUUID } from './data/utils';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import BudgetDetailTabsAndRoutes from './BudgetDetailTabsAndRoutes';
const PAGE_TITLE = 'Learner Credit Management';
-const BudgetDetailPage = ({
- enterpriseUUID,
- enterpriseSlug,
- enableLearnerPortal,
-}) => {
- const { budgetId } = useParams();
- const enterpriseOfferId = isUUID(budgetId) ? null : budgetId;
- const subsidyAccessPolicyId = isUUID(budgetId) ? budgetId : null;
-
+const BudgetDetailPage = ({ enterpriseSlug }) => {
const { isLoading } = useContext(EnterpriseSubsidiesContext);
- const {
- isLoading: isLoadingOfferRedemptions,
- offerRedemptions,
- fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
if (isLoading) {
return ;
}
const links = [
- { label: 'Budgets', to: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
+ {
+ label: 'Budgets',
+ to: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}`,
+ },
];
return (
<>
-
-
+
+
-
+
>
);
};
const mapStateToProps = state => ({
- enterpriseUUID: state.portalConfiguration.enterpriseId,
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
- enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
});
BudgetDetailPage.propTypes = {
- enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
- enableLearnerPortal: PropTypes.bool.isRequired,
};
export default connect(mapStateToProps)(BudgetDetailPage);
diff --git a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
new file mode 100644
index 0000000000..9932cd4cdc
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
@@ -0,0 +1,103 @@
+import React, { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { useHistory, useParams } from 'react-router-dom';
+import { Tabs } from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+
+import {
+ BUDGET_DETAIL_ACTIVITY_TAB,
+ BUDGET_DETAIL_CATALOG_TAB,
+} from './data/constants';
+import { useBudgetDetailTabs } from './data';
+import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import NotFoundPage from '../NotFoundPage';
+import EVENT_NAMES from '../../eventTracking';
+import BudgetDetailActivityTabContents from './BudgetDetailActivityTabContents';
+import BudgetDetailCatalogTabContents from './BudgetDetailCatalogTabContents';
+
+const DEFAULT_TAB = BUDGET_DETAIL_ACTIVITY_TAB;
+
+function isSupportedTabKey({ tabKey, enterpriseFeatures }) {
+ const supportedTabs = [BUDGET_DETAIL_ACTIVITY_TAB];
+ if (enterpriseFeatures.topDownAssignmentRealTimeLcm) {
+ supportedTabs.push(BUDGET_DETAIL_CATALOG_TAB);
+ }
+ return supportedTabs.includes(tabKey);
+}
+
+function getInitialTabKey(routeActiveTabKey, { enterpriseFeatures }) {
+ const isValidTabKey = isSupportedTabKey({
+ tabKey: routeActiveTabKey,
+ enterpriseFeatures,
+ });
+ if (!isValidTabKey) {
+ return DEFAULT_TAB;
+ }
+ return routeActiveTabKey;
+}
+
+const BudgetDetailTabsAndRoutes = ({
+ enterpriseId,
+ enterpriseSlug,
+ enterpriseFeatures,
+}) => {
+ const history = useHistory();
+ const { budgetId, activeTabKey: routeActiveTabKey } = useParams();
+ const [activeTabKey, setActiveTabKey] = useState(getInitialTabKey(
+ routeActiveTabKey,
+ { enterpriseFeatures },
+ ));
+
+ useEffect(() => {
+ setActiveTabKey(getInitialTabKey(routeActiveTabKey, { enterpriseFeatures }));
+ }, [routeActiveTabKey, enterpriseFeatures]);
+
+ const handleTabSelect = (nextActiveTabKey) => {
+ setActiveTabKey(nextActiveTabKey);
+ history.push(`/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}/${budgetId}/${nextActiveTabKey}`);
+ const eventMetadata = { selectedTab: nextActiveTabKey };
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ `${EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.TAB_CHANGED}`,
+ eventMetadata,
+ );
+ };
+
+ const tabs = useBudgetDetailTabs({
+ activeTabKey,
+ enterpriseFeatures,
+ ActivityTabElement: BudgetDetailActivityTabContents,
+ CatalogTabElement: BudgetDetailCatalogTabContents,
+ });
+
+ if (!isSupportedTabKey({
+ tabKey: routeActiveTabKey || activeTabKey,
+ enterpriseFeatures,
+ })) {
+ return ;
+ }
+
+ return (
+
+ {tabs}
+
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
+});
+
+BudgetDetailTabsAndRoutes.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+ enterpriseFeatures: PropTypes.shape().isRequired,
+};
+
+export default connect(mapStateToProps)(BudgetDetailTabsAndRoutes);
diff --git a/src/components/learner-credit-management/EmailAddressTableCell.jsx b/src/components/learner-credit-management/EmailAddressTableCell.jsx
index ef628d0cf0..a85d1094c8 100644
--- a/src/components/learner-credit-management/EmailAddressTableCell.jsx
+++ b/src/components/learner-credit-management/EmailAddressTableCell.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
import {
Stack, OverlayTrigger, IconButton, Icon, Popover,
} from '@edx/paragon';
@@ -65,4 +66,8 @@ EmailAddressTableCell.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
};
-export default EmailAddressTableCell;
+const mapStateToProps = state => ({
+ enterpriseUUID: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(EmailAddressTableCell);
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index 555df1ca95..b74e51cd25 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -1,120 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
-import { DataTable, Hyperlink } from '@edx/paragon';
-import { getConfig } from '@edx/frontend-platform/config';
+import { DataTable } from '@edx/paragon';
import TableTextFilter from './TableTextFilter';
-import EmailAddressTableCell from './EmailAddressTableCell';
+import SpendTableEmptyState from './SpendTableEmptyState';
+import SpendTableEnrollmentDetails from './SpendTableEnrollmentDetails';
import { getCourseProductLineText } from '../../utils';
export const PAGE_SIZE = 20;
export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
-const getEnrollmentDetailsAccessor = row => ({
- courseTitle: row.courseTitle,
- userEmail: row.userEmail,
- courseKey: row.courseKey,
-});
-
const FilterStatus = (rest) => ;
const LearnerCreditAllocationTable = ({
isLoading,
tableData,
fetchTableData,
- enterpriseUUID,
- enterpriseSlug,
- enableLearnerPortal,
}) => {
const defaultFilter = [];
-
return (
- dayjs(row.values.enrollmentDate).format('MMM D, YYYY'),
- disableFilters: true,
- },
- {
- Header: 'Enrollment details',
- accessor: getEnrollmentDetailsAccessor,
- Cell: ({ row }) => (
- <>
-
-
- {enableLearnerPortal ? (
-
- {row.original.courseTitle}
-
- ) : (
- row.original.courseTitle
- )}
-
- >
- ),
- disableFilters: false,
- disableSortBy: true,
- },
- {
- Header: 'Amount',
- accessor: 'courseListPrice',
- Cell: ({ row }) => `$${row.values.courseListPrice}`,
- disableFilters: true,
- },
- {
- Header: 'Product',
- accessor: 'courseProductLine',
- Cell: ({ row }) => getCourseProductLineText(row.values.courseProductLine),
- disableFilters: true,
- },
- ]}
- initialTableOptions={{
- getRowId: row => row?.uuid?.toString(),
- }}
- initialState={{
- pageSize: PAGE_SIZE,
- pageIndex: DEFAULT_PAGE,
- sortBy: [
- { id: 'enrollmentDate', desc: true },
- ],
- filters: defaultFilter,
- }}
- fetchData={fetchTableData}
- data={tableData.results}
- itemCount={tableData.itemCount}
- pageCount={tableData.pageCount}
- EmptyTableComponent={
- () => {
- if (isLoading) {
- return null;
- }
- return ;
- }
- }
- />
+ <>
+ Spent
+
+ Spent activity is driven by completed enrollments. Enrollment data is automatically updated every 12 hours.
+ Come back later to view more recent enrollments.
+
+ dayjs(row.values.enrollmentDate).format('MMM D, YYYY'),
+ disableFilters: true,
+ },
+ {
+ Header: 'Enrollment details',
+ accessor: 'enrollmentDetails',
+ Cell: SpendTableEnrollmentDetails,
+ disableFilters: false,
+ disableSortBy: true,
+ },
+ {
+ Header: 'Amount',
+ accessor: 'courseListPrice',
+ Cell: ({ row }) => `$${row.values.courseListPrice}`,
+ disableFilters: true,
+ },
+ {
+ Header: 'Product',
+ accessor: 'courseProductLine',
+ Cell: ({ row }) => getCourseProductLineText(row.values.courseProductLine),
+ disableFilters: true,
+ },
+ ]}
+ initialTableOptions={{
+ getRowId: row => row?.uuid?.toString(),
+ }}
+ initialState={{
+ pageSize: PAGE_SIZE,
+ pageIndex: DEFAULT_PAGE,
+ sortBy: [
+ { id: 'enrollmentDate', desc: true },
+ ],
+ filters: defaultFilter,
+ }}
+ fetchData={fetchTableData}
+ data={tableData.results}
+ itemCount={tableData.itemCount}
+ pageCount={tableData.pageCount}
+ EmptyTableComponent={SpendTableEmptyState}
+ />
+ >
);
};
-/* eslint-enable */
LearnerCreditAllocationTable.propTypes = {
- enterpriseUUID: PropTypes.string.isRequired,
- enterpriseSlug: PropTypes.string.isRequired,
- enableLearnerPortal: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
tableData: PropTypes.shape({
results: PropTypes.arrayOf(PropTypes.shape({
diff --git a/src/components/learner-credit-management/LearnerCreditManagement.jsx b/src/components/learner-credit-management/LearnerCreditManagement.jsx
index 5f3ce77d4c..4786f3e72f 100644
--- a/src/components/learner-credit-management/LearnerCreditManagement.jsx
+++ b/src/components/learner-credit-management/LearnerCreditManagement.jsx
@@ -21,7 +21,7 @@ import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
import LearnerCreditDisclaimer from './LearnerCreditDisclaimer';
import OfferDates from './OfferDates';
import OfferNameHeading from './OfferNameHeading';
-import { useOfferSummary, useOfferRedemptions } from './data/hooks';
+import { useOfferSummary, useOfferRedemptions } from './data';
import { DATE_FORMAT } from './data/constants';
import OfferUtilizationAlerts from './OfferUtilizationAlerts';
diff --git a/src/components/learner-credit-management/SpendTableEmptyState.jsx b/src/components/learner-credit-management/SpendTableEmptyState.jsx
new file mode 100644
index 0000000000..20f5c9165a
--- /dev/null
+++ b/src/components/learner-credit-management/SpendTableEmptyState.jsx
@@ -0,0 +1,12 @@
+import React, { useContext } from 'react';
+import { DataTable, DataTableContext } from '@edx/paragon';
+
+const SpendTableEmptyState = () => {
+ const { isLoading } = useContext(DataTableContext);
+ if (isLoading) {
+ return null;
+ }
+ return ;
+};
+
+export default SpendTableEmptyState;
diff --git a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
new file mode 100644
index 0000000000..227f917290
--- /dev/null
+++ b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { Hyperlink } from '@edx/paragon';
+import { getConfig } from '@edx/frontend-platform';
+
+import EmailAddressTableCell from './EmailAddressTableCell';
+
+const SpendTableEnrollmentDetailsContents = ({
+ row,
+ enableLearnerPortal,
+ enterpriseSlug,
+}) => (
+ <>
+
+
+ {enableLearnerPortal ? (
+
+ {row.original.courseTitle}
+
+ ) : (
+ {row.original.courseTitle}
+ )}
+
+ >
+);
+
+const rowPropType = PropTypes.shape({
+ original: PropTypes.shape({
+ courseKey: PropTypes.string.isRequired,
+ courseTitle: PropTypes.string.isRequired,
+ }).isRequired,
+}).isRequired;
+
+const mapStateToProps = state => ({
+ enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+const ConnectedSpendTableEnrollmentDetailsContents = connect(mapStateToProps)(SpendTableEnrollmentDetailsContents);
+
+SpendTableEnrollmentDetailsContents.propTypes = {
+ row: rowPropType,
+ enableLearnerPortal: PropTypes.bool.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+};
+
+const SpendTableEnrollmentDetails = ({ row }) => ;
+
+SpendTableEnrollmentDetails.propTypes = {
+ row: rowPropType,
+};
+
+export default SpendTableEnrollmentDetails;
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 2e27404dee..defca8d674 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -16,3 +16,11 @@ export const NO_BALANCE_REMAINING_DOLLAR_THRESHOLD = 100;
export const DATE_FORMAT = 'MMMM DD, YYYY';
export const EXEC_ED_OFFER_TYPE = 'learner_credit';
+
+// Budget Detail Page Tabs
+export const BUDGET_DETAIL_ACTIVITY_TAB = 'activity';
+export const BUDGET_DETAIL_CATALOG_TAB = 'catalog';
+export const BUDGET_DETAIL_TAB_LABELS = {
+ [BUDGET_DETAIL_ACTIVITY_TAB]: 'Activity',
+ [BUDGET_DETAIL_CATALOG_TAB]: 'Catalog',
+};
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
new file mode 100644
index 0000000000..c3bebd2b82
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -0,0 +1,3 @@
+export { default as useBudgetDetailTabs } from './useBudgetDetailTabs';
+export { default as useOfferSummary } from './useOfferSummary';
+export { default as useOfferRedemptions } from './useOfferRedemptions';
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx b/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx
new file mode 100644
index 0000000000..af8186a7b1
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx
@@ -0,0 +1,52 @@
+import { useMemo } from 'react';
+import { Tab } from '@edx/paragon';
+
+import {
+ BUDGET_DETAIL_ACTIVITY_TAB,
+ BUDGET_DETAIL_CATALOG_TAB,
+ BUDGET_DETAIL_TAB_LABELS,
+} from '../constants';
+
+const TAB_CLASS_NAME = 'pt-4.5';
+
+export const useBudgetDetailTabs = ({
+ activeTabKey,
+ enterpriseFeatures,
+ ActivityTabElement,
+ CatalogTabElement,
+}) => {
+ const tabs = useMemo(() => {
+ const tabsArray = [];
+ tabsArray.push(
+
+ {activeTabKey === BUDGET_DETAIL_ACTIVITY_TAB && (
+
+ )}
+ ,
+ );
+ if (enterpriseFeatures.topDownAssignmentRealTimeLcm) {
+ tabsArray.push(
+
+ {activeTabKey === BUDGET_DETAIL_CATALOG_TAB && (
+
+ )}
+ ,
+ );
+ }
+ return tabsArray;
+ }, [activeTabKey, enterpriseFeatures, ActivityTabElement, CatalogTabElement]);
+
+ return tabs;
+};
+
+export default useBudgetDetailTabs;
diff --git a/src/components/learner-credit-management/data/hooks.js b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
similarity index 74%
rename from src/components/learner-credit-management/data/hooks.js
rename to src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
index 31577f36a7..356c05404d 100644
--- a/src/components/learner-credit-management/data/hooks.js
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
@@ -1,50 +1,17 @@
import {
- useCallback, useEffect, useState, useMemo, useRef,
+ useCallback,
+ useMemo,
+ useRef,
+ useState,
} from 'react';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import { logError } from '@edx/frontend-platform/logging';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import debounce from 'lodash.debounce';
-import EnterpriseDataApiService from '../../../data/services/EnterpriseDataApiService';
-import {
- transformOfferSummary,
- transformUtilizationTableResults,
-} from './utils';
-import { API_FIELDS_BY_TABLE_COLUMN_ACCESSOR } from './constants';
-
-export const useOfferSummary = (enterpriseUUID, enterpriseOffer) => {
- const [isLoading, setIsLoading] = useState(true);
- const [offerSummary, setOfferSummary] = useState();
-
- useEffect(() => {
- if (!enterpriseOffer) {
- setIsLoading(false);
- return;
- }
-
- const fetchData = async () => {
- try {
- setIsLoading(true);
- const response = await EnterpriseDataApiService.fetchEnterpriseOfferSummary(enterpriseUUID, enterpriseOffer.id);
- const data = camelCaseObject(response.data);
- const transformedOfferSummary = transformOfferSummary(data);
- setOfferSummary(transformedOfferSummary);
- } catch (error) {
- logError(error);
- } finally {
- setIsLoading(false);
- }
- };
-
- fetchData();
- }, [enterpriseUUID, enterpriseOffer]);
-
- return {
- isLoading,
- offerSummary,
- };
-};
+import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import { API_FIELDS_BY_TABLE_COLUMN_ACCESSOR } from '../constants';
+import { transformUtilizationTableResults } from '../utils';
const applySortByToOptions = (sortBy, options) => {
const orderingStrings = sortBy.map(({ id, desc }) => {
@@ -74,7 +41,7 @@ const applyFiltersToOptions = (filters, options) => {
}
};
-export const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => {
+const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
const [offerRedemptions, setOfferRedemptions] = useState({
@@ -147,3 +114,5 @@ export const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = n
fetchOfferRedemptions: debouncedFetchOfferRedemptions,
};
};
+
+export default useOfferRedemptions;
diff --git a/src/components/learner-credit-management/data/hooks/useOfferSummary.js b/src/components/learner-credit-management/data/hooks/useOfferSummary.js
new file mode 100644
index 0000000000..95a90b348b
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useOfferSummary.js
@@ -0,0 +1,41 @@
+import { useEffect, useState } from 'react';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+import { logError } from '@edx/frontend-platform/logging';
+
+import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import { transformOfferSummary } from '../utils';
+
+const useOfferSummary = (enterpriseUUID, enterpriseOffer) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [offerSummary, setOfferSummary] = useState();
+
+ useEffect(() => {
+ if (!enterpriseOffer) {
+ setIsLoading(false);
+ return;
+ }
+
+ const fetchData = async () => {
+ try {
+ setIsLoading(true);
+ const response = await EnterpriseDataApiService.fetchEnterpriseOfferSummary(enterpriseUUID, enterpriseOffer.id);
+ const data = camelCaseObject(response.data);
+ const transformedOfferSummary = transformOfferSummary(data);
+ setOfferSummary(transformedOfferSummary);
+ } catch (error) {
+ logError(error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [enterpriseUUID, enterpriseOffer]);
+
+ return {
+ isLoading,
+ offerSummary,
+ };
+};
+
+export default useOfferSummary;
diff --git a/src/components/learner-credit-management/data/index.js b/src/components/learner-credit-management/data/index.js
new file mode 100644
index 0000000000..c1e9d27f7e
--- /dev/null
+++ b/src/components/learner-credit-management/data/index.js
@@ -0,0 +1,3 @@
+export * from './constants';
+export * from './utils';
+export * from './hooks';
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
index e3b88632ff..43786e8a0b 100644
--- a/src/components/learner-credit-management/index.jsx
+++ b/src/components/learner-credit-management/index.jsx
@@ -14,7 +14,7 @@ const LearnerCreditManagementRoutes = ({ match }) => (
>
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index c1b20b9958..a2bc074944 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -13,10 +13,14 @@ import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import BudgetCard from '../BudgetCard-V2';
-import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
+import { useOfferSummary, useOfferRedemptions } from '../data';
import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
-jest.mock('../data/hooks');
+jest.mock('../data', () => ({
+ ...jest.requireActual('../data'),
+ useOfferSummary: jest.fn(),
+ useOfferRedemptions: jest.fn(),
+}));
useOfferSummary.mockReturnValue({
isLoading: false,
offerSummary: null,
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
new file mode 100644
index 0000000000..f09b42afb2
--- /dev/null
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -0,0 +1,240 @@
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import configureMockStore from 'redux-mock-store';
+import { screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import '@testing-library/jest-dom/extend-expect';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { renderWithRouter } from '@edx/frontend-enterprise-utils';
+import { act } from 'react-dom/test-utils';
+
+import BudgetDetailPage from '../BudgetDetailPage';
+import { useOfferSummary, useOfferRedemptions } from '../data';
+import { EXEC_ED_OFFER_TYPE } from '../data/constants';
+import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn().mockReturnValue({
+ budgetId: '123',
+ activeTabKey: 'activity',
+ }),
+}));
+
+jest.mock('../data', () => ({
+ ...jest.requireActual('../data'),
+ useOfferSummary: jest.fn(),
+ useOfferRedemptions: jest.fn(),
+}));
+
+useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: null,
+});
+useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+});
+
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseId = 'test-enterprise';
+const enterpriseUUID = '1234';
+const initialStoreState = {
+ portalConfiguration: {
+ enterpriseId,
+ enterpriseSlug: enterpriseId,
+ enableLearnerPortal: true,
+ enterpriseFeatures: {
+ topDownAssignmentRealTimeLcm: true,
+ },
+ },
+};
+
+const mockEnterpriseOfferId = '123';
+
+const mockOfferDisplayName = 'Test Enterprise Offer';
+const mockOfferSummary = {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerType: EXEC_ED_OFFER_TYPE,
+};
+
+const defaultEnterpriseSubsidiesContextValue = {
+ isLoading: false,
+};
+
+const BudgetDetailPageWrapper = ({
+ initialState = initialStoreState,
+ enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
+ ...rest
+}) => {
+ const store = getMockStore({ ...initialState });
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+describe(' ', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ afterEach(() => {
+ useParams.mockReturnValue({
+ budgetId: '123',
+ activeTabKey: 'activity',
+ });
+ });
+
+ it('displays spend table in "Activity" tab with empty results', async () => {
+ const mockOffer = {
+ id: mockEnterpriseOfferId,
+ name: mockOfferDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ };
+ useOfferSummary.mockReturnValue({
+ isLoading: false,
+ offerSummary: mockOfferSummary,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ fetchOfferRedemptions: jest.fn(),
+ });
+ renderWithRouter(
+ ,
+ );
+ // Hero
+ expect(screen.getByText('Learner Credit Management'));
+ // Breadcrumb
+ expect(screen.getByText('Overview'));
+ // Activity tab exists and is active
+ expect(screen.getByText('Activity').getAttribute('aria-selected')).toBe('true');
+ // Spend table is visible within Activity tab contents
+ expect(screen.getByText('No results found'));
+ // Catalog tab exists and is NOT active
+ expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('false');
+ });
+
+ it('renders with catalog tab active on initial load', async () => {
+ useParams.mockReturnValue({
+ budgetId: '123',
+ activeTabKey: 'catalog',
+ });
+ renderWithRouter(
+ ,
+ );
+ // Catalog tab exists and is active
+ expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('true');
+ });
+
+ it('hides catalog tab when enterpriseFeatures.topDownAssignmentRealTimeLcm is false', () => {
+ const initialState = {
+ portalConfiguration: {
+ ...initialStoreState.portalConfiguration,
+ enterpriseFeatures: {
+ topDownAssignmentRealTimeLcm: false,
+ },
+ },
+ };
+ renderWithRouter(
+ ,
+ );
+ // Catalog tab does NOT exist
+ expect(screen.queryByText('Catalog')).toBeFalsy();
+ });
+
+ it('defaults to activity tab is no activeTabKey is provided', () => {
+ useParams.mockReturnValue({
+ budgetId: '123',
+ activeTabKey: undefined,
+ });
+ renderWithRouter(
+ ,
+ );
+ // Activity tab exists and is active
+ expect(screen.getByText('Activity').getAttribute('aria-selected')).toBe('true');
+ });
+
+ it('displays not found message is invalid activeTabKey is provided', () => {
+ useParams.mockReturnValue({
+ budgetId: '123',
+ activeTabKey: 'invalid',
+ });
+ renderWithRouter(
+ ,
+ );
+ expect(screen.getByText('404')).toBeInTheDocument();
+ expect(screen.getByText('something went wrong', { exact: false })).toBeInTheDocument();
+ });
+
+ it('handles user switching to catalog tab', async () => {
+ renderWithRouter(
+ ,
+ );
+ const catalogTab = screen.getByText('Catalog');
+
+ await act(async () => {
+ userEvent.click(catalogTab);
+ });
+
+ await waitFor(() => {
+ expect(screen.getByTestId('budget-detail-catalog-tab-contents')).toBeInTheDocument();
+ });
+ });
+
+ it('displays loading message while loading data', () => {
+ renderWithRouter(
+ ,
+ );
+ expect(screen.getByText('Loading'));
+ });
+});
diff --git a/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx b/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
index 630315aeb2..093363a737 100644
--- a/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
+++ b/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
@@ -4,9 +4,28 @@ import {
render,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import configureMockStore from 'redux-mock-store';
+import { Provider } from 'react-redux';
import EmailAddressTableCell from '../EmailAddressTableCell';
+const mockStore = configureMockStore();
+
+const mockInitialState = {
+ portalConfiguration: {
+ enterpriseId: 'test-enterprise',
+ },
+};
+
+const EmailAddressTableCellWrapper = ({
+ initialStoreState = mockInitialState,
+ ...props
+}) => (
+
+
+
+);
+
describe(' ', () => {
it('with email is present, display it', () => {
const userEmail = 'edx@example.com';
@@ -15,7 +34,7 @@ describe(' ', () => {
userEmail,
},
};
- render( );
+ render( );
expect(screen.getByText(userEmail));
});
@@ -25,9 +44,9 @@ describe(' ', () => {
userEmail: null,
},
};
- render( );
+ render( );
expect(screen.getByText('Email hidden'));
userEvent.click(screen.getByLabelText('More details'));
- await screen.findByText('Learner data disabled', { exact: false });
+ expect(await screen.findByText('Learner data disabled', { exact: false }));
});
});
diff --git a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
index 9099404e4f..692405c4ed 100644
--- a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
+++ b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
@@ -3,7 +3,9 @@ import {
screen,
render,
} from '@testing-library/react';
+import { Provider } from 'react-redux';
import { IntlProvider } from '@edx/frontend-platform/i18n';
+import configureMockStore from 'redux-mock-store';
import LearnerCreditAllocationTable from '../LearnerCreditAllocationTable';
@@ -11,27 +13,39 @@ jest.mock('@edx/frontend-platform/config', () => ({
getConfig: () => ({ ENTERPRISE_LEARNER_PORTAL_URL: 'https://enterprise.edx.org' }),
}));
-const LearnerCreditAllocationTableWrapper = (props) => (
+const mockStore = configureMockStore();
+const defaultStore = mockStore({
+ portalConfiguration: {
+ enterpriseId: 'test-enterprise-id',
+ enterpriseSlug: 'test-enterprise-slug',
+ enableLearnerPortal: true,
+ },
+});
+
+const LearnerCreditAllocationTableWrapper = ({
+ store = defaultStore,
+ ...props
+}) => (
-
+
+
+
);
describe(' ', () => {
it('renders with table data', () => {
const props = {
- enterpriseUUID: 'test-enterprise-id',
isLoading: false,
- budgetType: 'OCM',
- enterpriseSlug: 'test-enterprise-slug',
- enableLearnerPortal: true,
tableData: {
results: [{
+ uuid: 'test-uuid',
userEmail: 'test@example.com',
courseTitle: 'course-title',
courseListPrice: 100,
enrollmentDate: '2-2-23',
courseProductLine: 'OCM',
+ courseKey: 'edX+DemoX',
}],
itemCount: 1,
pageCount: 1,
@@ -39,9 +53,7 @@ describe(' ', () => {
fetchTableData: jest.fn(),
};
props.fetchTableData.mockReturnValue(props.tableData);
-
render( );
-
expect(screen.getByText(props.tableData.results[0].userEmail.toString(), {
exact: false,
}));
@@ -53,6 +65,7 @@ describe(' ', () => {
}));
expect(screen.getByText('Feb', { exact: false }));
});
+
it('renders with empty table data', () => {
const props = {
enterpriseUUID: 'test-enterprise-id',
@@ -66,24 +79,19 @@ describe(' ', () => {
fetchTableData: jest.fn(),
};
props.fetchTableData.mockReturnValue(props.tableData);
-
render( );
-
expect(screen.getByText('No results found', { exact: false }));
});
it('constructs the correct URL for the course', () => {
const props = {
- enterpriseUUID: 'test-enterprise-id',
isLoading: false,
- budgetType: 'OCM',
- enterpriseSlug: 'test-enterprise-slug',
- enableLearnerPortal: true,
tableData: {
results: [{
+ uuid: 'test-uuid',
userEmail: 'test@example.com',
courseTitle: 'course-title',
- courseKey: 'course-v1:edX=CTL.SC101x.3T2019',
+ courseKey: 'edX+CTL.SC101x',
courseListPrice: 100,
enrollmentDate: '2-2-23',
courseProductLine: 'OCM',
@@ -94,27 +102,28 @@ describe(' ', () => {
fetchTableData: jest.fn(),
};
props.fetchTableData.mockReturnValue(props.tableData);
-
render( );
-
- const expectedLink = 'https://enterprise.edx.org/test-enterprise-slug/course/course-v1:edX=CTL.SC101x.3T2019';
+ const expectedLink = 'https://enterprise.edx.org/test-enterprise-slug/course/edX+CTL.SC101x';
const courseLinkElement = screen.getByText('course-title');
-
expect(courseLinkElement.getAttribute('href')).toBe(expectedLink);
});
it('does not render the course link if the learner portal is disabled', () => {
+ const store = mockStore({
+ portalConfiguration: {
+ enterpriseId: 'test-enterprise-id',
+ enterpriseSlug: 'test-enterprise-slug',
+ enableLearnerPortal: false,
+ },
+ });
const props = {
- enterpriseUUID: 'test-enterprise-id',
isLoading: false,
- budgetType: 'OCM',
- enterpriseSlug: 'test-enterprise-slug',
- enableLearnerPortal: false,
tableData: {
results: [{
+ uuid: 'test-uuid',
userEmail: 'test@example.com',
courseTitle: 'course-title',
- courseKey: 'course-v1:edX=CTL.SC101x.3T2019',
+ courseKey: 'edX+CTL.SC101x',
courseListPrice: 100,
enrollmentDate: '2-2-23',
courseProductLine: 'OCM',
@@ -125,8 +134,7 @@ describe(' ', () => {
fetchTableData: jest.fn(),
};
props.fetchTableData.mockReturnValue(props.tableData);
-
- render( );
+ render( );
const courseTitleElement = screen.queryByText('course-title');
expect(courseTitleElement.closest('a')).toBeNull();
});
diff --git a/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx b/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx
index ffcede173a..6028ff6392 100644
--- a/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx
+++ b/src/components/settings/SettingsLMSTab/ErrorReporting/tests/SyncHistory.test.jsx
@@ -153,6 +153,5 @@ describe('Test sync history page full flow', () => {
await waitFor(() => expect(mockFetch).toHaveBeenCalledWith('1'));
// opens stepper
await waitFor(() => expect(screen.getByText('New learning platform integration')));
- screen.debug(undefined, 100000);
});
});
diff --git a/src/eventTracking.js b/src/eventTracking.js
index b0a9e7bd6f..f126e15c6e 100644
--- a/src/eventTracking.js
+++ b/src/eventTracking.js
@@ -16,6 +16,7 @@ const PROJECT_NAME = 'edx.ui.enterprise.admin_portal';
const SUBSCRIPTION_PREFIX = `${PROJECT_NAME}.subscriptions`;
const SETTINGS_PREFIX = `${PROJECT_NAME}.settings`;
const CONTENT_HIGHLIGHTS_PREFIX = `${PROJECT_NAME}.content_highlights`;
+const LEARNER_CREDIT_MANAGEMENT_PREFIX = `${PROJECT_NAME}.learner_credit_management`;
const SUBSCRIPTION_TABLE_PREFIX = `${SUBSCRIPTION_PREFIX}.table`;
const CONTENT_HIGHLIGHT_STEPPER_BASE_PREFIX = `${CONTENT_HIGHLIGHTS_PREFIX}.stepper`;
@@ -91,9 +92,14 @@ export const SUBSCRIPTION_EVENTS = {
TABLE: SUBSCRIPTION_TABLE_EVENTS,
};
+export const LEARNER_CREDIT_MANAGEMENT_EVENTS = {
+ TAB_CHANGED: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget-detail.tab.changed`,
+};
+
const EVENT_NAMES = {
SUBSCRIPTIONS: SUBSCRIPTION_EVENTS,
CONTENT_HIGHLIGHTS: CONTENT_HIGHLIGHTS_EVENTS,
+ LEARNER_CREDIT_MANAGEMENT: LEARNER_CREDIT_MANAGEMENT_EVENTS,
};
export default EVENT_NAMES;
From 6a47c984164d0bf7c2448daaa089ac6908eb7fcd Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 5 Oct 2023 10:11:49 -0400
Subject: [PATCH 034/124] fix: ensure budget_id query param is passed to
analytics api (#1049)
---
.../BudgetDetailActivityTabContents.jsx | 6 ++-
.../tests/BudgetDetailPage.test.jsx | 45 ++++++++++++++-----
2 files changed, 37 insertions(+), 14 deletions(-)
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index ce5b1c0c0a..a593012a63 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { useParams } from 'react-router-dom';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import { useOfferRedemptions } from './data';
+import { useOfferRedemptions, isUUID } from './data';
const BudgetDetailActivityTabContents = ({
enterpriseUUID,
@@ -12,11 +12,13 @@ const BudgetDetailActivityTabContents = ({
enableLearnerPortal,
}) => {
const { budgetId } = useParams();
+ const enterpriseOfferId = isUUID(budgetId) ? null : budgetId;
+ const subsidyAccessPolicyId = isUUID(budgetId) ? budgetId : null;
const {
isLoading: isLoadingOfferRedemptions,
offerRedemptions,
fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, budgetId);
+ } = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
return (
mockStore(store);
-const enterpriseId = 'test-enterprise';
+const enterpriseSlug = 'test-enterprise';
const enterpriseUUID = '1234';
const initialStoreState = {
portalConfiguration: {
- enterpriseId,
- enterpriseSlug: enterpriseId,
+ enterpriseId: enterpriseUUID,
+ enterpriseSlug,
enableLearnerPortal: true,
enterpriseFeatures: {
topDownAssignmentRealTimeLcm: true,
@@ -59,6 +59,7 @@ const initialStoreState = {
};
const mockEnterpriseOfferId = '123';
+const mockSubsidyAccessPolicyUUID = 'c17de32e-b80b-468f-b994-85e68fd32751';
const mockOfferDisplayName = 'Test Enterprise Offer';
const mockOfferSummary = {
@@ -102,13 +103,29 @@ describe(' ', () => {
});
});
- it('displays spend table in "Activity" tab with empty results', async () => {
+ it.each([
+ {
+ budgetId: mockEnterpriseOfferId,
+ expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null],
+ },
+ {
+ budgetId: mockSubsidyAccessPolicyUUID,
+ expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID],
+ },
+ ])('displays spend table in "Activity" tab with empty results (%s)', async ({
+ budgetId,
+ expectedUseOfferRedemptionsArgs,
+ }) => {
const mockOffer = {
- id: mockEnterpriseOfferId,
+ id: budgetId,
name: mockOfferDisplayName,
start: '2022-01-01',
end: '2023-01-01',
};
+ useParams.mockReturnValue({
+ budgetId,
+ activeTabKey: 'activity',
+ });
useOfferSummary.mockReturnValue({
isLoading: false,
offerSummary: mockOfferSummary,
@@ -125,10 +142,14 @@ describe(' ', () => {
renderWithRouter(
,
);
+
+ expect(useOfferRedemptions).toHaveBeenCalledTimes(1);
+ expect(useOfferRedemptions).toHaveBeenCalledWith(...expectedUseOfferRedemptionsArgs);
+
// Hero
expect(screen.getByText('Learner Credit Management'));
// Breadcrumb
@@ -149,7 +170,7 @@ describe(' ', () => {
renderWithRouter(
,
);
// Catalog tab exists and is active
@@ -169,7 +190,7 @@ describe(' ', () => {
,
);
// Catalog tab does NOT exist
@@ -184,7 +205,7 @@ describe(' ', () => {
renderWithRouter(
,
);
// Activity tab exists and is active
@@ -199,7 +220,7 @@ describe(' ', () => {
renderWithRouter(
,
);
expect(screen.getByText('404')).toBeInTheDocument();
@@ -210,7 +231,7 @@ describe(' ', () => {
renderWithRouter(
,
);
const catalogTab = screen.getByText('Catalog');
@@ -228,7 +249,7 @@ describe(' ', () => {
renderWithRouter(
Date: Fri, 6 Oct 2023 11:38:55 +0500
Subject: [PATCH 035/124] feat: display AI analytics section on LPR page
---
.../AIAnalyticsSummary/data/hooks.js | 37 +
.../AIAnalyticsSummary/tests/hooks.test.js | 102 +
src/components/Admin/AIAnalyticsSummary.jsx | 118 +
.../Admin/AIAnalyticsSummary.test.jsx | 110 +
.../Admin/AIAnalyticsSummarySkeleton.jsx | 11 +
src/components/Admin/Admin.test.jsx | 67 +
.../AIAnalyticsSummary.test.jsx.snap | 217 ++
.../Admin/__snapshots__/Admin.test.jsx.snap | 2270 +++++++++++++++--
src/components/Admin/index.jsx | 20 +
src/containers/AdminPage/AdminPage.test.jsx | 4 +
src/containers/AdminPage/index.jsx | 9 +
.../EnterpriseApp/EnterpriseApp.test.jsx | 1 +
src/data/actions/dashboardInsights.js | 41 +
src/data/constants/dashboardInsights.js | 11 +
src/data/reducers/dashboardInsights.js | 47 +
src/data/reducers/dashboardInsights.test.js | 107 +
src/data/reducers/index.js | 2 +
src/data/services/EnterpriseDataApiService.js | 7 +
src/data/services/LmsApiService.js | 5 +
19 files changed, 2988 insertions(+), 198 deletions(-)
create mode 100644 src/components/AIAnalyticsSummary/data/hooks.js
create mode 100644 src/components/AIAnalyticsSummary/tests/hooks.test.js
create mode 100644 src/components/Admin/AIAnalyticsSummary.jsx
create mode 100644 src/components/Admin/AIAnalyticsSummary.test.jsx
create mode 100644 src/components/Admin/AIAnalyticsSummarySkeleton.jsx
create mode 100644 src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
create mode 100644 src/data/actions/dashboardInsights.js
create mode 100644 src/data/constants/dashboardInsights.js
create mode 100644 src/data/reducers/dashboardInsights.js
create mode 100644 src/data/reducers/dashboardInsights.test.js
diff --git a/src/components/AIAnalyticsSummary/data/hooks.js b/src/components/AIAnalyticsSummary/data/hooks.js
new file mode 100644
index 0000000000..c319bdcbb9
--- /dev/null
+++ b/src/components/AIAnalyticsSummary/data/hooks.js
@@ -0,0 +1,37 @@
+import { useEffect, useState } from 'react';
+import { logError } from '@edx/frontend-platform/logging';
+import LmsApiService from '../../../data/services/LmsApiService';
+
+const useAIAnalyticsSummary = (enterpriseId, insights) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [analyticsSummaryData, setAnalyticsSummaryData] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const response = await LmsApiService.generateAIAnalyticsSummary(enterpriseId, insights);
+ setAnalyticsSummaryData(response.data);
+ } catch (err) {
+ setError(err);
+ logError(err);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ if (enterpriseId && insights) {
+ fetchData();
+ } else {
+ setIsLoading(false);
+ }
+ }, [enterpriseId, insights]);
+
+ return {
+ isLoading,
+ error,
+ data: analyticsSummaryData,
+ };
+};
+
+export default useAIAnalyticsSummary;
diff --git a/src/components/AIAnalyticsSummary/tests/hooks.test.js b/src/components/AIAnalyticsSummary/tests/hooks.test.js
new file mode 100644
index 0000000000..4bf41fed25
--- /dev/null
+++ b/src/components/AIAnalyticsSummary/tests/hooks.test.js
@@ -0,0 +1,102 @@
+import { renderHook } from '@testing-library/react-hooks';
+import useAIAnalyticsSummary from '../data/hooks';
+import LmsApiService from '../../../data/services/LmsApiService';
+
+jest.mock('@edx/frontend-platform/logging', () => ({
+ logError: jest.fn(),
+}));
+
+jest.mock('../../../data/services/LmsApiService', () => ({
+ generateAIAnalyticsSummary: jest.fn(),
+}));
+
+const TEST_ENTERPRISE_ID = 'test-enterprise-uuid';
+
+const mockInsightsData = {
+ learner_progress: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ active_subscription_plan: true,
+ assigned_licenses: 0,
+ activated_licenses: 0,
+ assigned_licenses_percentage: 0.0,
+ activated_licenses_percentage: 0.0,
+ active_enrollments: 1026,
+ at_risk_enrollment_less_than_one_hour: 26,
+ at_risk_enrollment_end_date_soon: 15,
+ at_risk_enrollment_dormant: 918,
+ created_at: '2023-10-02T03:24:17Z',
+ },
+ learner_engagement: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ enrolls: 49,
+ enrolls_prior: 45,
+ passed: 2,
+ passed_prior: 0,
+ engage: 67,
+ engage_prior: 50,
+ hours: 62,
+ hours_prior: 49,
+ contract_end_date: '2022-06-13T00:00:00Z',
+ active_contract: false,
+ created_at: '2023-10-02T03:24:40Z',
+ },
+};
+const mockAnalyticsData = {
+ learner_progress: 'As an administrator running an online learning program on edX For Business, currently, none of the licenses are active',
+ learner_engagement: 'In the last 30 days, your online learning program on edX For Business has seen positive growth.',
+};
+
+describe('useAIAnalyticsSummary', () => {
+ it('should fetch AI analytics summary data', async () => {
+ LmsApiService.generateAIAnalyticsSummary.mockResolvedValueOnce({ data: mockAnalyticsData });
+ const { result, waitForNextUpdate } = renderHook(() => useAIAnalyticsSummary(TEST_ENTERPRISE_ID, mockInsightsData));
+
+ expect(result.current).toEqual({
+ isLoading: true,
+ error: null,
+ data: null,
+ });
+
+ await waitForNextUpdate();
+
+ expect(LmsApiService.generateAIAnalyticsSummary).toHaveBeenCalled();
+ expect(result.current).toEqual({
+ isLoading: false,
+ error: null,
+ data: mockAnalyticsData,
+ });
+ });
+
+ it('should handle error when fetching AI analytics summary data', async () => {
+ const error = new Error('An error occurred');
+ LmsApiService.generateAIAnalyticsSummary.mockRejectedValueOnce(error);
+ const { result, waitForNextUpdate } = renderHook(() => useAIAnalyticsSummary(TEST_ENTERPRISE_ID, mockInsightsData));
+
+ expect(result.current).toEqual({
+ isLoading: true,
+ error: null,
+ data: null,
+ });
+
+ await waitForNextUpdate();
+
+ expect(LmsApiService.generateAIAnalyticsSummary).toHaveBeenCalled();
+ expect(result.current).toEqual({
+ isLoading: false,
+ error,
+ data: null,
+ });
+ });
+
+ it('should not fetch data if enterpriseId or insights is missing', async () => {
+ const { result } = renderHook(() => useAIAnalyticsSummary(TEST_ENTERPRISE_ID));
+
+ expect(result.current).toEqual({
+ isLoading: false,
+ data: null,
+ error: null,
+ });
+ });
+});
diff --git a/src/components/Admin/AIAnalyticsSummary.jsx b/src/components/Admin/AIAnalyticsSummary.jsx
new file mode 100644
index 0000000000..58ca4c7814
--- /dev/null
+++ b/src/components/Admin/AIAnalyticsSummary.jsx
@@ -0,0 +1,118 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import {
+ Button, Card, Stack, Badge, useToggle,
+} from '@edx/paragon';
+import { FormattedMessage } from '@edx/frontend-platform/i18n';
+import { AutoFixHigh, Groups } from '@edx/paragon/icons';
+import useAIAnalyticsSummary from '../AIAnalyticsSummary/data/hooks';
+
+const AnalyticsDetailCard = ({
+ onClose,
+ isLoading,
+ error,
+ data,
+}) => (
+
+
+
+
+
+
+
+
+
+
+ Dismiss
+
+
+
+
+
+
+
+);
+
+AnalyticsDetailCard.propTypes = {
+ onClose: PropTypes.func.isRequired,
+ isLoading: PropTypes.bool,
+ error: PropTypes.instanceOf(Error),
+ data: PropTypes.string,
+};
+
+const AIAnalyticsSummary = ({ enterpriseId, insights }) => {
+ const [summarizeCardIsOpen, showSummarizeCard, hideSummarizeCard] = useToggle(false);
+ const [trackProgressCardIsOpen, showTrackProgressCard, hideTrackProgressCard] = useToggle(false);
+
+ const { data: analyticsSummary, isLoading, error } = useAIAnalyticsSummary(enterpriseId, insights);
+
+ return (
+ <>
+
+ {
+ showSummarizeCard(true);
+ hideTrackProgressCard(true);
+ }}
+ data-testid="summarize-analytics"
+ >
+ <>
+
+
+ >
+
+ {
+ showTrackProgressCard(true);
+ hideSummarizeCard(true);
+ }}
+ data-testid="track-progress"
+ >
+ <>
+
+
+ >
+
+
+ {summarizeCardIsOpen && (
+ hideSummarizeCard(true)}
+ isLoading={isLoading}
+ error={error}
+ />
+ )}
+ {trackProgressCardIsOpen && (
+ hideTrackProgressCard(true)}
+ isLoading={isLoading}
+ error={error}
+ />
+ )}
+ >
+ );
+};
+
+const mapStateToProps = state => ({
+ insights: state.dashboardInsights.insights,
+});
+
+AIAnalyticsSummary.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+ insights: PropTypes.objectOf(PropTypes.shape),
+};
+
+export default connect(mapStateToProps)(AIAnalyticsSummary);
diff --git a/src/components/Admin/AIAnalyticsSummary.test.jsx b/src/components/Admin/AIAnalyticsSummary.test.jsx
new file mode 100644
index 0000000000..89b9a0c760
--- /dev/null
+++ b/src/components/Admin/AIAnalyticsSummary.test.jsx
@@ -0,0 +1,110 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { Provider } from 'react-redux';
+import renderer from 'react-test-renderer';
+import configureMockStore from 'redux-mock-store';
+import { MemoryRouter } from 'react-router-dom';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import thunk from 'redux-thunk';
+import AIAnalyticsSummary from './AIAnalyticsSummary';
+
+const mockedInsights = {
+ learner_progress: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ active_subscription_plan: true,
+ assigned_licenses: 0,
+ activated_licenses: 0,
+ assigned_licenses_percentage: 0.0,
+ activated_licenses_percentage: 0.0,
+ active_enrollments: 1026,
+ at_risk_enrollment_less_than_one_hour: 26,
+ at_risk_enrollment_end_date_soon: 15,
+ at_risk_enrollment_dormant: 918,
+ created_at: '2023-10-02T03:24:17Z',
+ },
+ learner_engagement: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ enrolls: 49,
+ enrolls_prior: 45,
+ passed: 2,
+ passed_prior: 0,
+ engage: 67,
+ engage_prior: 50,
+ hours: 62,
+ hours_prior: 49,
+ contract_end_date: '2022-06-13T00:00:00Z',
+ active_contract: false,
+ created_at: '2023-10-02T03:24:40Z',
+ },
+};
+const mockStore = configureMockStore([thunk]);
+const store = mockStore({
+ portalConfiguration: {
+ enterpriseId: 'test-enterprise-id',
+ },
+ dashboardInsights: mockedInsights,
+});
+
+const AIAnalyticsSummaryWrapper = props => (
+
+
+
+ ,
+
+
+
+);
+
+describe(' ', () => {
+ it('should render action buttons correctly', () => {
+ const tree = renderer
+ .create((
+
+ ))
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should display AnalyticsDetailCard with learner_engagement data when Summarize Analytics button is clicked', () => {
+ const wrapper = mount( );
+ wrapper.find('[data-testid="summarize-analytics"]').first().simulate('click');
+
+ const tree = renderer
+ .create( )
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should display AnalyticsDetailCard with learner_progress data when Track Progress button is clicked', () => {
+ const wrapper = mount( );
+ wrapper.find('[data-testid="track-progress"]').first().simulate('click');
+
+ const tree = renderer
+ .create( )
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should handle null analytics data', () => {
+ const insightsData = { ...mockedInsights, learner_engagement: null };
+ const wrapper = mount( );
+ wrapper.find('[data-testid="summarize-analytics"]').first().simulate('click');
+
+ const tree = renderer
+ .create( )
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/src/components/Admin/AIAnalyticsSummarySkeleton.jsx b/src/components/Admin/AIAnalyticsSummarySkeleton.jsx
new file mode 100644
index 0000000000..b44e6da4bd
--- /dev/null
+++ b/src/components/Admin/AIAnalyticsSummarySkeleton.jsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import { Skeleton, Stack } from '@edx/paragon';
+
+const AIAnalyticsSummarySkeleton = () => (
+
+
+
+
+);
+
+export default AIAnalyticsSummarySkeleton;
diff --git a/src/components/Admin/Admin.test.jsx b/src/components/Admin/Admin.test.jsx
index 4362ae6805..1dad753b64 100644
--- a/src/components/Admin/Admin.test.jsx
+++ b/src/components/Admin/Admin.test.jsx
@@ -36,6 +36,10 @@ const store = mockStore({
number_of_users: 3,
course_completions: 1,
},
+ dashboardInsights: {
+ loading: null,
+ insights: null,
+ },
});
const AdminWrapper = props => (
@@ -61,6 +65,8 @@ const AdminWrapper = props => (
url: '/',
}}
{...props}
+ fetchDashboardInsights={() => {}}
+ clearDashboardInsights={() => {}}
/>
@@ -77,6 +83,7 @@ describe(' ', () => {
courseCompletions: 1,
lastUpdatedDate: '2018-07-31T23:14:35Z',
numberOfUsers: 3,
+ insights: null,
};
describe('renders correctly', () => {
@@ -290,6 +297,66 @@ describe(' ', () => {
.toJSON();
expect(tree).toMatchSnapshot();
});
+
+ it('with no dashboard insights data', () => {
+ const insights = null;
+ const tree = renderer
+ .create((
+
+ ))
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ describe('with dashboard insights data', () => {
+ it('renders dashboard insights data correctly', () => {
+ const insights = {
+ learner_progress: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ active_subscription_plan: true,
+ assigned_licenses: 0,
+ activated_licenses: 0,
+ assigned_licenses_percentage: 0.0,
+ activated_licenses_percentage: 0.0,
+ active_enrollments: 1026,
+ at_risk_enrollment_less_than_one_hour: 26,
+ at_risk_enrollment_end_date_soon: 15,
+ at_risk_enrollment_dormant: 918,
+ created_at: '2023-10-02T03:24:17Z',
+ },
+ learner_engagement: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ enrolls: 49,
+ enrolls_prior: 45,
+ passed: 2,
+ passed_prior: 0,
+ engage: 67,
+ engage_prior: 50,
+ hours: 62,
+ hours_prior: 49,
+ contract_end_date: '2022-06-13T00:00:00Z',
+ active_contract: false,
+ created_at: '2023-10-02T03:24:40Z',
+ },
+ };
+ const tree = renderer
+ .create((
+
+ ))
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+ });
});
describe('handle changes to enterpriseId prop', () => {
diff --git a/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap b/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
new file mode 100644
index 0000000000..2013f63e74
--- /dev/null
+++ b/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
@@ -0,0 +1,217 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` should display AnalyticsDetailCard with learner_engagement data when Summarize Analytics button is clicked 1`] = `
+Array [
+
+
+
+
+
+ Summarize Analytics
+
+
+
+
+
+ Track Progress
+
+
,
+ ",",
+]
+`;
+
+exports[` should display AnalyticsDetailCard with learner_progress data when Track Progress button is clicked 1`] = `
+Array [
+
+
+
+
+
+ Summarize Analytics
+
+
+
+
+
+ Track Progress
+
+
,
+ ",",
+]
+`;
+
+exports[` should handle null analytics data 1`] = `
+Array [
+
+
+
+
+
+ Summarize Analytics
+
+
+
+
+
+ Track Progress
+
+
,
+ ",",
+]
+`;
+
+exports[` should render action buttons correctly 1`] = `
+Array [
+
+
+
+
+
+ Summarize Analytics
+
+
+
+
+
+ Track Progress
+
+
,
+ ",",
+]
+`;
diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
index fd763719cf..5fdf48d830 100644
--- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
@@ -34,6 +34,13 @@ exports[` renders correctly calls fetchDashboardAnalytics prop 1`] = `
+
@@ -197,6 +204,13 @@ exports[`
renders correctly with dashboard analytics data renders # cou
+
@@ -910,6 +924,13 @@ exports[`
renders correctly with dashboard analytics data renders # of
+
@@ -1623,6 +1644,13 @@ exports[`
renders correctly with dashboard analytics data renders # of
+
@@ -2340,6 +2368,13 @@ exports[`
renders correctly with dashboard analytics data renders colla
+
@@ -3200,6 +3235,13 @@ exports[`
renders correctly with dashboard analytics data renders full
+
@@ -4060,6 +4102,13 @@ exports[`
renders correctly with dashboard analytics data renders inact
+
@@ -4777,6 +4826,13 @@ exports[`
renders correctly with dashboard analytics data renders inact
+
@@ -5494,6 +5550,13 @@ exports[`
renders correctly with dashboard analytics data renders learn
+
@@ -6211,6 +6274,13 @@ exports[`
renders correctly with dashboard analytics data renders regis
+
@@ -6924,6 +6994,13 @@ exports[`
renders correctly with dashboard analytics data renders top a
+
@@ -7607,7 +7684,7 @@ exports[`
renders correctly with dashboard analytics data renders top a
`;
-exports[`
renders correctly with error state 1`] = `
+exports[`
renders correctly with dashboard insights data renders dashboard insights data correctly 1`] = `
renders correctly with error state 1`] = `
-
-
-
+
-
-
- Hey, nice to see you
-
-
- Try refreshing your screen
- Network Error
-
-
-
+
+
+ Track Progress
+
- Loading...
-
- Loading
-
+
+
+
+ 3
+
+
+
+
+
+
+
+
+ total number of learners registered
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
-
-
-
- Full Report
-
-
-
+
+ 1
+
+
+
+
+
+
+
+
+ learners enrolled in at least one course
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+ active learners in the past week
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+ course completions
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+ Loading
+
+
+
+
+
+
+
+
+
+ Full Report
+
+
+
+
+
+
+
+
+
+
+
+ Showing data as of
+ July 31, 2018
+
+
+
+
+
+
+ Download full report (CSV)
+
+
+
+
+
+
+
+
+
+ Filter by course
+
+
+
+
+ All Courses
+
+
+
+
+
+
+
+
+ Filter by start date
+
+
+
+
+
+
+
+
+
+ Choose a course
+
+
+
+
+
+
+
+
+ Filter by email
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` renders correctly with error state 1`] = `
+
+
+
+
+ Learner Progress Report
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hey, nice to see you
+
+
+ Try refreshing your screen
+ Network Error
+
+
+
+
+
+
+
+
+
+ Loading...
+
+ Loading
+
+
+
+
+
+
+
+
+
+ Full Report
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` renders correctly with loading state 1`] = `
+
+
+
+
+ Learner Progress Report
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+ Loading
+
+
+
+
+
+
+
+
+
+ Full Report
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` renders correctly with no dashboard analytics data 1`] = `
+
+
+ Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` renders correctly with no dashboard insights data 1`] = `
+
+
+
+
+ Learner Progress Report
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+
+
+
+ total number of learners registered
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
+
+
+
+ className="card"
+ >
+
+
+
+ 1
+
+
+
+
+
+
+
+
+ learners enrolled in at least one course
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+ active learners in the past week
+
+
+
+
+
+
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
-
-
-
-
-`;
-
-exports[` renders correctly with loading state 1`] = `
-
-
-
-
- Learner Progress Report
-
-
-
-
-
-
-
-
-
- Loading...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ 1
+
+
+
+
+
+
+
+
+ course completions
+
+
+
+
-
-
-
-
-
-
-
+
+
+ Details
+
+
+
+
+
+
+
+ Show details
+
+
+
+
+
+
+
+
@@ -7908,6 +9617,216 @@ exports[` renders correctly with loading state 1`] = `
+
+
+ Showing data as of
+ July 31, 2018
+
+
+
+
+
+
+ Download full report (CSV)
+
+
+
+
+
+
+
+
+
+ Filter by course
+
+
+
+
+ All Courses
+
+
+
+
+
+
+
+
+ Filter by start date
+
+
+
+
+
+
+
+
+
+ Choose a course
+
+
+
+
+
+
+
+
+ Filter by email
+
+
+
+
+
+
@@ -7916,48 +9835,3 @@ exports[`
renders correctly with loading state 1`] = `
`;
-
-exports[` renders correctly with no dashboard analytics data 1`] = `
-
-
- Loading...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/components/Admin/index.jsx b/src/components/Admin/index.jsx
index 45db4b5059..b5ccee0bc3 100644
--- a/src/components/Admin/index.jsx
+++ b/src/components/Admin/index.jsx
@@ -24,12 +24,15 @@ import { formatTimestamp } from '../../utils';
import AdminCardsSkeleton from './AdminCardsSkeleton';
import { SubscriptionData } from '../subscriptions';
import EmbeddedSubscription from './EmbeddedSubscription';
+import AIAnalyticsSummary from './AIAnalyticsSummary';
+import AIAnalyticsSummarySkeleton from './AIAnalyticsSummarySkeleton';
class Admin extends React.Component {
componentDidMount() {
const { enterpriseId } = this.props;
if (enterpriseId) {
this.props.fetchDashboardAnalytics(enterpriseId);
+ this.props.fetchDashboardInsights(enterpriseId);
}
}
@@ -37,12 +40,14 @@ class Admin extends React.Component {
const { enterpriseId } = this.props;
if (enterpriseId && enterpriseId !== prevProps.enterpriseId) {
this.props.fetchDashboardAnalytics(enterpriseId);
+ this.props.fetchDashboardInsights(enterpriseId);
}
}
componentWillUnmount() {
// Clear the overview data
this.props.clearDashboardAnalytics();
+ this.props.clearDashboardInsights();
}
getMetadataForAction(actionSlug) {
@@ -281,6 +286,8 @@ class Admin extends React.Component {
enterpriseId,
match,
location: { search },
+ insights,
+ insightsLoading,
} = this.props;
const { params: { actionSlug } } = match;
@@ -309,6 +316,13 @@ class Admin extends React.Component {
Overview
+
+
+ {insightsLoading ?
: (
+ insights &&
+ )}
+
+
{(error || loading) ? (
@@ -402,11 +416,15 @@ Admin.defaultProps = {
},
csv: null,
table: null,
+ insightsLoading: false,
+ insights: null,
};
Admin.propTypes = {
fetchDashboardAnalytics: PropTypes.func.isRequired,
clearDashboardAnalytics: PropTypes.func.isRequired,
+ fetchDashboardInsights: PropTypes.func.isRequired,
+ clearDashboardInsights: PropTypes.func.isRequired,
enterpriseId: PropTypes.string,
searchEnrollmentsList: PropTypes.func.isRequired,
location: PropTypes.shape({
@@ -431,6 +449,8 @@ Admin.propTypes = {
}).isRequired,
}).isRequired,
table: PropTypes.shape({}),
+ insightsLoading: PropTypes.bool,
+ insights: PropTypes.objectOf(PropTypes.shape),
};
export default Admin;
diff --git a/src/containers/AdminPage/AdminPage.test.jsx b/src/containers/AdminPage/AdminPage.test.jsx
index 29c0770f40..2e30fbade3 100644
--- a/src/containers/AdminPage/AdminPage.test.jsx
+++ b/src/containers/AdminPage/AdminPage.test.jsx
@@ -28,6 +28,10 @@ const store = mockStore({
table: {
enrollments: {},
},
+ dashboardInsights: {
+ loading: null,
+ insights: null,
+ },
});
describe('
', () => {
diff --git a/src/containers/AdminPage/index.jsx b/src/containers/AdminPage/index.jsx
index 675cb1fbf5..502f7d18b6 100644
--- a/src/containers/AdminPage/index.jsx
+++ b/src/containers/AdminPage/index.jsx
@@ -9,6 +9,7 @@ import {
import Admin from '../../components/Admin';
import { paginateTable } from '../../data/actions/table';
import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiService';
+import { fetchDashboardInsights, clearDashboardInsights } from '../../data/actions/dashboardInsights';
const mapStateToProps = state => ({
loading: state.dashboardAnalytics.loading,
@@ -21,6 +22,8 @@ const mapStateToProps = state => ({
enterpriseId: state.portalConfiguration.enterpriseId,
csv: state.csv,
table: state.table,
+ insightsLoading: state.dashboardInsights.loading,
+ insights: state.dashboardInsights.insights,
});
const mapDispatchToProps = dispatch => ({
@@ -33,6 +36,12 @@ const mapDispatchToProps = dispatch => ({
searchEnrollmentsList: () => {
dispatch(paginateTable('enrollments', EnterpriseDataApiService.fetchCourseEnrollments));
},
+ fetchDashboardInsights: (enterpriseId) => {
+ dispatch(fetchDashboardInsights(enterpriseId));
+ },
+ clearDashboardInsights: () => {
+ dispatch(clearDashboardInsights());
+ },
});
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Admin));
diff --git a/src/containers/EnterpriseApp/EnterpriseApp.test.jsx b/src/containers/EnterpriseApp/EnterpriseApp.test.jsx
index bde4287dcc..6281ab463d 100644
--- a/src/containers/EnterpriseApp/EnterpriseApp.test.jsx
+++ b/src/containers/EnterpriseApp/EnterpriseApp.test.jsx
@@ -91,6 +91,7 @@ const initialState = {
isExpanded: false,
isExpandedByToggle: false,
},
+ dashboardInsights: {},
};
// eslint-disable-next-line react/prop-types
diff --git a/src/data/actions/dashboardInsights.js b/src/data/actions/dashboardInsights.js
new file mode 100644
index 0000000000..b9b65a040d
--- /dev/null
+++ b/src/data/actions/dashboardInsights.js
@@ -0,0 +1,41 @@
+import { logError } from '@edx/frontend-platform/logging';
+import {
+ FETCH_DASHBOARD_INSIGHTS_REQUEST,
+ FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ FETCH_DASHBOARD_INSIGHTS_FAILURE,
+ CLEAR_DASHBOARD_INSIGHTS,
+} from '../constants/dashboardInsights';
+import EnterpriseDataApiService from '../services/EnterpriseDataApiService';
+
+const fetchDashboardInsightsRequest = () => ({ type: FETCH_DASHBOARD_INSIGHTS_REQUEST });
+const fetchDashboardInsightsSuccess = data => ({
+ type: FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ payload: { data },
+});
+const fetchDashboardInsightsFailure = error => ({
+ type: FETCH_DASHBOARD_INSIGHTS_FAILURE,
+ payload: { error },
+});
+
+const fetchDashboardInsights = enterpriseId => (
+ (dispatch) => {
+ dispatch(fetchDashboardInsightsRequest());
+ return EnterpriseDataApiService.fetchDashboardInsights(enterpriseId)
+ .then((response) => {
+ dispatch(fetchDashboardInsightsSuccess(response.data));
+ })
+ .catch((error) => {
+ logError(error);
+ dispatch(fetchDashboardInsightsFailure(error));
+ });
+ }
+);
+
+const clearDashboardInsights = () => dispatch => (dispatch({
+ type: CLEAR_DASHBOARD_INSIGHTS,
+}));
+
+export {
+ fetchDashboardInsights,
+ clearDashboardInsights,
+};
diff --git a/src/data/constants/dashboardInsights.js b/src/data/constants/dashboardInsights.js
new file mode 100644
index 0000000000..05b81a28e9
--- /dev/null
+++ b/src/data/constants/dashboardInsights.js
@@ -0,0 +1,11 @@
+const FETCH_DASHBOARD_INSIGHTS_REQUEST = 'FETCH_DASHBOARD_INSIGHTS_REQUEST';
+const FETCH_DASHBOARD_INSIGHTS_SUCCESS = 'FETCH_DASHBOARD_INSIGHTS_SUCCESS';
+const FETCH_DASHBOARD_INSIGHTS_FAILURE = 'FETCH_DASHBOARD_INSIGHTS_FAILURE';
+const CLEAR_DASHBOARD_INSIGHTS = 'CLEAR_DASHBOARD_INSIGHTS';
+
+export {
+ FETCH_DASHBOARD_INSIGHTS_REQUEST,
+ FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ FETCH_DASHBOARD_INSIGHTS_FAILURE,
+ CLEAR_DASHBOARD_INSIGHTS,
+};
diff --git a/src/data/reducers/dashboardInsights.js b/src/data/reducers/dashboardInsights.js
new file mode 100644
index 0000000000..6c25972617
--- /dev/null
+++ b/src/data/reducers/dashboardInsights.js
@@ -0,0 +1,47 @@
+import {
+ FETCH_DASHBOARD_INSIGHTS_REQUEST,
+ FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ FETCH_DASHBOARD_INSIGHTS_FAILURE,
+ CLEAR_DASHBOARD_INSIGHTS,
+} from '../constants/dashboardInsights';
+
+const initialState = {
+ loading: false,
+ error: null,
+ insights: null,
+};
+
+const dashboardInsights = (state = initialState, action) => {
+ switch (action.type) {
+ case FETCH_DASHBOARD_INSIGHTS_REQUEST:
+ return {
+ ...state,
+ loading: true,
+ error: null,
+ };
+ case FETCH_DASHBOARD_INSIGHTS_SUCCESS:
+ return {
+ ...state,
+ loading: false,
+ insights: action.payload.data,
+ };
+ case FETCH_DASHBOARD_INSIGHTS_FAILURE:
+ return {
+ ...state,
+ loading: false,
+ error: action.payload.error,
+ insights: null,
+ };
+ case CLEAR_DASHBOARD_INSIGHTS:
+ return {
+ ...state,
+ loading: false,
+ error: null,
+ insights: null,
+ };
+ default:
+ return state;
+ }
+};
+
+export default dashboardInsights;
diff --git a/src/data/reducers/dashboardInsights.test.js b/src/data/reducers/dashboardInsights.test.js
new file mode 100644
index 0000000000..7e2d3cd713
--- /dev/null
+++ b/src/data/reducers/dashboardInsights.test.js
@@ -0,0 +1,107 @@
+import dashboardInsights from './dashboardInsights';
+import {
+ FETCH_DASHBOARD_INSIGHTS_REQUEST,
+ FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ FETCH_DASHBOARD_INSIGHTS_FAILURE,
+ CLEAR_DASHBOARD_INSIGHTS,
+} from '../constants/dashboardInsights';
+
+const initialState = {
+ loading: false,
+ error: null,
+ insights: null,
+};
+
+const mockInsightsData = {
+ learner_progress: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ active_subscription_plan: true,
+ assigned_licenses: 0,
+ activated_licenses: 0,
+ assigned_licenses_percentage: 0.0,
+ activated_licenses_percentage: 0.0,
+ active_enrollments: 1026,
+ at_risk_enrollment_less_than_one_hour: 26,
+ at_risk_enrollment_end_date_soon: 15,
+ at_risk_enrollment_dormant: 918,
+ created_at: '2023-10-02T03:24:17Z',
+ },
+ learner_engagement: {
+ enterprise_customer_uuid: 'aac56d39-f38d-4510-8ef9-085cab048ea9',
+ enterprise_customer_name: 'Microsoft Corporation',
+ enrolls: 49,
+ enrolls_prior: 45,
+ passed: 2,
+ passed_prior: 0,
+ engage: 67,
+ engage_prior: 50,
+ hours: 62,
+ hours_prior: 49,
+ contract_end_date: '2022-06-13T00:00:00Z',
+ active_contract: false,
+ created_at: '2023-10-02T03:24:40Z',
+ },
+};
+
+describe('dashboardInsights reducer', () => {
+ it('has initial state', () => {
+ expect(dashboardInsights(undefined, {})).toEqual(initialState);
+ });
+
+ it('updates fetch insights request state', () => {
+ const expected = {
+ ...initialState,
+ loading: true,
+ error: null,
+ };
+ expect(dashboardInsights(undefined, {
+ type: FETCH_DASHBOARD_INSIGHTS_REQUEST,
+ })).toEqual(expected);
+ });
+
+ it('updates fetch insights success state', () => {
+ const expected = {
+ ...initialState,
+ loading: false,
+ insights: mockInsightsData,
+ error: null,
+ };
+ expect(dashboardInsights(undefined, {
+ type: FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ payload: { data: mockInsightsData },
+ })).toEqual(expected);
+ });
+
+ it('updates fetch insights failure state', () => {
+ const error = Error('Network Request');
+ const expected = {
+ ...initialState,
+ loading: false,
+ error,
+ insights: null,
+ };
+ expect(dashboardInsights(undefined, {
+ type: FETCH_DASHBOARD_INSIGHTS_FAILURE,
+ payload: { error },
+ })).toEqual(expected);
+ });
+
+ it('updates clear insights state', () => {
+ const state = dashboardInsights(undefined, {
+ type: FETCH_DASHBOARD_INSIGHTS_SUCCESS,
+ payload: { data: mockInsightsData },
+ });
+
+ const expected = {
+ ...initialState,
+ loading: false,
+ error: null,
+ insights: null,
+ };
+
+ expect(dashboardInsights(state, {
+ type: CLEAR_DASHBOARD_INSIGHTS,
+ })).toEqual(expected);
+ });
+});
diff --git a/src/data/reducers/index.js b/src/data/reducers/index.js
index 27b18f99f5..c3d5abfaf3 100644
--- a/src/data/reducers/index.js
+++ b/src/data/reducers/index.js
@@ -13,6 +13,7 @@ import licenseRevoke from './licenseRevoke';
import emailTemplate from './emailTemplate';
import licenseRemind from './licenseRemind';
import userSubscription from './userSubscription';
+import dashboardInsights from './dashboardInsights';
const identityReducer = (state) => {
const newState = { ...state };
@@ -36,6 +37,7 @@ const rootReducer = combineReducers({
emailTemplate,
licenseRemind,
userSubscription,
+ dashboardInsights,
});
export default rootReducer;
diff --git a/src/data/services/EnterpriseDataApiService.js b/src/data/services/EnterpriseDataApiService.js
index bf00da3de6..ef7b5078e5 100644
--- a/src/data/services/EnterpriseDataApiService.js
+++ b/src/data/services/EnterpriseDataApiService.js
@@ -9,6 +9,8 @@ class EnterpriseDataApiService {
static enterpriseBaseUrl = `${configuration.DATA_API_BASE_URL}/enterprise/api/v1/enterprise/`;
+ static enterpriseAdminBaseUrl = `${configuration.DATA_API_BASE_URL}/enterprise/api/v1/admin/`;
+
static fetchDashboardAnalytics(enterpriseId) {
const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/enrollments/overview/`;
return EnterpriseDataApiService.apiClient().get(url);
@@ -111,6 +113,11 @@ class EnterpriseDataApiService {
const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/${endpoint}/?${queryParams.toString()}`;
return EnterpriseDataApiService.apiClient().get(url);
}
+
+ static fetchDashboardInsights(enterpriseId) {
+ const url = `${EnterpriseDataApiService.enterpriseAdminBaseUrl}insights/${enterpriseId}`;
+ return EnterpriseDataApiService.apiClient().get(url);
+ }
}
export default EnterpriseDataApiService;
diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js
index 2ed35e8274..31bc12fce7 100644
--- a/src/data/services/LmsApiService.js
+++ b/src/data/services/LmsApiService.js
@@ -379,6 +379,11 @@ class LmsApiService {
};
return LmsApiService.apiClient().put(`${LmsApiService.apiCredentialsUrl}${enterpriseUUID}/regenerate_credentials`, requestData);
}
+
+ static generateAIAnalyticsSummary(enterpriseUUID, formData) {
+ const url = `${LmsApiService.baseUrl}/enterprise/api/v1/analytics-summary/${enterpriseUUID}/`;
+ return LmsApiService.apiClient().post(url, formData);
+ }
}
export default LmsApiService;
From 279d0e30759f5aa45a236e84669809051f9906ac Mon Sep 17 00:00:00 2001
From: mahamakifdar19
Date: Wed, 11 Oct 2023 12:26:38 +0500
Subject: [PATCH 036/124] fix: removed trailing / from ai analytics enpoint url
---
src/data/services/LmsApiService.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js
index 31bc12fce7..18f2cca502 100644
--- a/src/data/services/LmsApiService.js
+++ b/src/data/services/LmsApiService.js
@@ -381,7 +381,7 @@ class LmsApiService {
}
static generateAIAnalyticsSummary(enterpriseUUID, formData) {
- const url = `${LmsApiService.baseUrl}/enterprise/api/v1/analytics-summary/${enterpriseUUID}/`;
+ const url = `${LmsApiService.baseUrl}/enterprise/api/v1/analytics-summary/${enterpriseUUID}`;
return LmsApiService.apiClient().post(url, formData);
}
}
From f902ee1ee26efb4a39dd13e73ed27d470fb4f9ea Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Wed, 11 Oct 2023 15:28:14 -0600
Subject: [PATCH 037/124] feat: Adding search/filtering for top-down assignment
(#1047)
* fix: draft PR
* Merge branch 'master' into kiram15/ENT-7339
* fix: adding search filters
* fix: removing categories from page
* fix: extra fixes
* fix: adding tests and fixes
* fix: PR review
* fix: PR reviews
---
__mocks__/react-instantsearch-dom.jsx | 4 +-
.../Admin/EmbeddedSubscription.test.jsx | 1 -
.../Admin/SubscriptionDetailPage.test.jsx | 1 -
.../licenses/LicenseAllocationHeader.test.jsx | 2 -
.../CourseSearchResults.test.jsx | 1 -
.../stepper/BulkEnrollmentSubmit.test.jsx | 1 -
.../CodeAssignmentModal.test.jsx | 2 +-
.../tests/ContentConfirmContentCard.test.jsx | 2 +-
.../HighlightStepperConfirmContent.test.jsx | 1 -
...ghlightStepperSelectContentSearch.test.jsx | 1 -
.../ContentHighlights/data/constants.js | 4 +-
.../data/tests/constants.test.js | 2 +-
.../EnterpriseApp/EnterpriseApp.test.jsx | 2 +-
.../EnterpriseList/EnterpriseList.test.jsx | 1 -
.../BudgetDetailCatalogTabContents.jsx | 52 +++++-
.../cards/CourseCard.jsx | 97 +++++++++++
.../cards/CourseCard.test.jsx | 82 ++++++++++
.../data/constants.js | 10 ++
.../data/hooks/hooks.js | 13 ++
.../learner-credit-management/data/utils.js | 6 +
.../learner-credit-management/index.js | 4 +
.../learner-credit.scss | 25 +++
.../search/CatalogSearch.jsx | 44 +++++
.../search/CatalogSearchResults.jsx | 149 +++++++++++++++++
.../tests/CatalogSearch.test.jsx | 42 +++++
.../tests/CatalogSearchResults.test.jsx | 154 ++++++++++++++++++
.../settings/tests/SettingsTabs.test.jsx | 1 -
.../bulk-actions/EnrollBulkAction.test.jsx | 1 -
.../bulk-actions/RemindBulkAction.test.jsx | 1 -
.../bulk-actions/RevokeBulkAction.test.jsx | 1 -
.../tests/index.test.jsx | 1 -
.../tests/MultipleSubscriptionsPage.test.jsx | 1 -
.../SubscriptionExpirationBanner.test.jsx | 2 +-
.../SubscriptionExpirationModals.test.jsx | 2 +-
src/components/test/testUtils.jsx | 1 -
.../EnterpriseApp/EnterpriseApp.test.jsx | 5 +-
src/data/hooks.js | 15 +-
37 files changed, 696 insertions(+), 38 deletions(-)
create mode 100644 src/components/learner-credit-management/cards/CourseCard.jsx
create mode 100644 src/components/learner-credit-management/cards/CourseCard.test.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/hooks.js
create mode 100644 src/components/learner-credit-management/index.js
create mode 100644 src/components/learner-credit-management/learner-credit.scss
create mode 100644 src/components/learner-credit-management/search/CatalogSearch.jsx
create mode 100644 src/components/learner-credit-management/search/CatalogSearchResults.jsx
create mode 100644 src/components/learner-credit-management/tests/CatalogSearch.test.jsx
create mode 100644 src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
diff --git a/__mocks__/react-instantsearch-dom.jsx b/__mocks__/react-instantsearch-dom.jsx
index f62f6554ca..4444b52c33 100644
--- a/__mocks__/react-instantsearch-dom.jsx
+++ b/__mocks__/react-instantsearch-dom.jsx
@@ -15,8 +15,8 @@ const advertised_course_run = {
/* eslint-disable camelcase */
const fakeHits = [
- { objectID: '1', aggregation_key: 'course:Bees101', title: 'bla', advertised_course_run, key: 'Bees101' },
- { objectID: '2', aggregation_key: 'course:Wasps200', title: 'blp', advertised_course_run, key: 'Wasps200' },
+ { objectID: '1', aggregation_key: 'course:Bees101', title: 'bla', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Bees101' },
+ { objectID: '2', aggregation_key: 'course:Wasps200', title: 'blp', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Wasps200' },
];
/* eslint-enable camelcase */
diff --git a/src/components/Admin/EmbeddedSubscription.test.jsx b/src/components/Admin/EmbeddedSubscription.test.jsx
index 16e36f5f97..5c767330d3 100644
--- a/src/components/Admin/EmbeddedSubscription.test.jsx
+++ b/src/components/Admin/EmbeddedSubscription.test.jsx
@@ -36,7 +36,6 @@ const defaultAppContext = {
},
};
-// eslint-disable-next-line react/prop-types
const AppContextProvider = ({ children }) => (
{children}
diff --git a/src/components/Admin/SubscriptionDetailPage.test.jsx b/src/components/Admin/SubscriptionDetailPage.test.jsx
index 82d6b48369..7287107f1e 100644
--- a/src/components/Admin/SubscriptionDetailPage.test.jsx
+++ b/src/components/Admin/SubscriptionDetailPage.test.jsx
@@ -46,7 +46,6 @@ const initialSubsidyRequestContextValue = {
},
};
-// eslint-disable-next-line react/prop-types
const AppContextProvider = ({ children }) => (
{children}
diff --git a/src/components/Admin/licenses/LicenseAllocationHeader.test.jsx b/src/components/Admin/licenses/LicenseAllocationHeader.test.jsx
index bba9f04e4f..8179fad6d0 100644
--- a/src/components/Admin/licenses/LicenseAllocationHeader.test.jsx
+++ b/src/components/Admin/licenses/LicenseAllocationHeader.test.jsx
@@ -11,7 +11,6 @@ import { SubsidyRequestsContext } from '../../subsidy-requests';
describe('LicenseAllocationHeader', () => {
const mockStore = configureMockStore();
- // eslint-disable-next-line react/prop-types
const SubscriptionDetailContextWrapper = ({ children }) => (
// eslint-disable-next-line react/jsx-no-constructed-context-values
{
);
- // eslint-disable-next-line react/prop-types
const SubsidyRequestsContextWrapper = ({ children }) => (
// eslint-disable-next-line react/jsx-no-constructed-context-values
(
diff --git a/src/components/BulkEnrollmentPage/stepper/BulkEnrollmentSubmit.test.jsx b/src/components/BulkEnrollmentPage/stepper/BulkEnrollmentSubmit.test.jsx
index 58a42934cf..b72e8e3dee 100644
--- a/src/components/BulkEnrollmentPage/stepper/BulkEnrollmentSubmit.test.jsx
+++ b/src/components/BulkEnrollmentPage/stepper/BulkEnrollmentSubmit.test.jsx
@@ -85,7 +85,6 @@ const bulkEnrollWithCoursesSelectedRows = {
courses: [selectedCourses, coursesDispatch],
};
-// eslint-disable-next-line react/prop-types
const BulkEnrollmentSubmitWrapper = ({ bulkEnrollInfo = defaultBulkEnrollInfo, ...props }) => (
diff --git a/src/components/CodeAssignmentModal/CodeAssignmentModal.test.jsx b/src/components/CodeAssignmentModal/CodeAssignmentModal.test.jsx
index a62e71dc20..b06b247812 100644
--- a/src/components/CodeAssignmentModal/CodeAssignmentModal.test.jsx
+++ b/src/components/CodeAssignmentModal/CodeAssignmentModal.test.jsx
@@ -20,7 +20,7 @@ import {
jest.mock('redux-form', () => ({
...jest.requireActual('redux-form'),
- // eslint-disable-next-line react/prop-types
+
Field: ({ label, ...rest }) => {label}
,
}));
diff --git a/src/components/ContentHighlights/HighlightStepper/tests/ContentConfirmContentCard.test.jsx b/src/components/ContentHighlights/HighlightStepper/tests/ContentConfirmContentCard.test.jsx
index 1f8f8f3a7a..b5b836dac8 100644
--- a/src/components/ContentHighlights/HighlightStepper/tests/ContentConfirmContentCard.test.jsx
+++ b/src/components/ContentHighlights/HighlightStepper/tests/ContentConfirmContentCard.test.jsx
@@ -45,7 +45,7 @@ const searchClient = algoliasearch(
);
const ContentHighlightContentCardWrapper = ({
- // eslint-disable-next-line react/prop-types
+
store = mockStore(initialState),
}) => {
const contextValue = useState({
diff --git a/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperConfirmContent.test.jsx b/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperConfirmContent.test.jsx
index 68022a1df8..34a9c64a67 100644
--- a/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperConfirmContent.test.jsx
+++ b/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperConfirmContent.test.jsx
@@ -30,7 +30,6 @@ const searchClient = algoliasearch(
configuration.ALGOLIA.SEARCH_API_KEY,
);
-// eslint-disable-next-line react/prop-types
const HighlightStepperConfirmContentWrapper = ({ children, currentSelectedRowIds = [] }) => {
const contextValue = useState({
stepperModal: {
diff --git a/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperSelectContentSearch.test.jsx b/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperSelectContentSearch.test.jsx
index c4f329b011..4d489c6d03 100644
--- a/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperSelectContentSearch.test.jsx
+++ b/src/components/ContentHighlights/HighlightStepper/tests/HighlightStepperSelectContentSearch.test.jsx
@@ -38,7 +38,6 @@ const searchClient = algoliasearch(
configuration.ALGOLIA.SEARCH_API_KEY,
);
-// eslint-disable-next-line react/prop-types
const HighlightStepperSelectContentSearchWrapper = ({ children, currentSelectedRowIds = [] }) => {
const contextValue = useState({
stepperModal: {
diff --git a/src/components/ContentHighlights/data/constants.js b/src/components/ContentHighlights/data/constants.js
index 93269fab0a..fafc699ba4 100644
--- a/src/components/ContentHighlights/data/constants.js
+++ b/src/components/ContentHighlights/data/constants.js
@@ -14,7 +14,7 @@ export const sanitizeAndParseHTML = (htmlString) => {
// Set to false before pushing PR!! otherwise set to true to enable local testing of ContentHighlights components
// Test will fail as additional check to ensure this is set to false before pushing PR
export const TEST_FLAG = false;
-// Test entepriseId for Content Highlights to display card selections and confirmation
+// Test enterpriseId for Content Highlights to display card selections and confirmation
export const testEnterpriseId = '943b1234-58cf-4376-b8e0-0efcbf4bfdf9';
// function that passes through enterpriseId if TEST_FLAG is false, otherwise returns local testing enterpriseId
export const ENABLE_TESTING = (enterpriseId, enableTest = TEST_FLAG) => {
@@ -42,7 +42,7 @@ export const TAB_TITLES = {
// Max length of highlight title in stepper
export const MAX_HIGHLIGHT_TITLE_LENGTH = 60;
-// Max highlight sets per enteprise curation
+// Max highlight sets per enterprise curation
export const MAX_HIGHLIGHT_SETS_PER_ENTERPRISE_CURATION = 12;
// Max number of content items per highlight set
diff --git a/src/components/ContentHighlights/data/tests/constants.test.js b/src/components/ContentHighlights/data/tests/constants.test.js
index b12f3f1982..00125b4f57 100644
--- a/src/components/ContentHighlights/data/tests/constants.test.js
+++ b/src/components/ContentHighlights/data/tests/constants.test.js
@@ -33,7 +33,7 @@ describe('constants', () => {
});
it('renders title name in string functions', () => {
const highlightTitle = 'test-title';
- // eslint-disable-next-line react/prop-types
+
const TestComponent = ({ children }) => (
{children}
diff --git a/src/components/EnterpriseApp/EnterpriseApp.test.jsx b/src/components/EnterpriseApp/EnterpriseApp.test.jsx
index 2aeace95d8..04ea668d69 100644
--- a/src/components/EnterpriseApp/EnterpriseApp.test.jsx
+++ b/src/components/EnterpriseApp/EnterpriseApp.test.jsx
@@ -36,7 +36,7 @@ const EnterpriseAppContextProvider = ({ children }) => (
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
__esModule: true,
- // eslint-disable-next-line react/prop-types
+
Route: (props) => {props.path} ,
Switch: (props) => props.children,
Redirect: () => 'Redirect',
diff --git a/src/components/EnterpriseList/EnterpriseList.test.jsx b/src/components/EnterpriseList/EnterpriseList.test.jsx
index 47b04098c0..d466f08366 100644
--- a/src/components/EnterpriseList/EnterpriseList.test.jsx
+++ b/src/components/EnterpriseList/EnterpriseList.test.jsx
@@ -35,7 +35,6 @@ const store = mockStore({
},
});
-// eslint-disable-next-line react/prop-types
const EnterpriseListWrapper = ({ initialEntries, ...rest }) => (
diff --git a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
index 83745cdd7b..a99f268db3 100644
--- a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
@@ -1,13 +1,49 @@
import React from 'react';
+import { InstantSearch } from 'react-instantsearch-dom';
+import algoliasearch from 'algoliasearch/lite';
import { Row, Col } from '@edx/paragon';
-const BudgetDetailCatalogTabContents = () => (
-
-
- Budget Name
- TODO
-
-
-);
+import { SearchData, SEARCH_FACET_FILTERS } from '@edx/frontend-enterprise-catalog-search';
+import CatalogSearch from './search/CatalogSearch';
+import { LANGUAGE_REFINEMENT, LEARNING_TYPE_REFINEMENT } from './data';
+import { configuration } from '../../config';
+
+const BudgetDetailCatalogTabContents = () => {
+ const language = {
+ attribute: LANGUAGE_REFINEMENT,
+ title: 'Language',
+ };
+ const learningType = {
+ attribute: LEARNING_TYPE_REFINEMENT,
+ title: 'Learning Type',
+ };
+ // Add search facet filters if they don't exist in the list yet
+ [language, learningType].forEach((refinement) => {
+ if (!SEARCH_FACET_FILTERS.some((filter) => filter.attribute === refinement.attribute)) {
+ SEARCH_FACET_FILTERS.push(refinement);
+ }
+ });
+
+ const searchClient = algoliasearch(
+ configuration.ALGOLIA.APP_ID,
+ configuration.ALGOLIA.SEARCH_API_KEY,
+ );
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
export default BudgetDetailCatalogTabContents;
diff --git a/src/components/learner-credit-management/cards/CourseCard.jsx b/src/components/learner-credit-management/cards/CourseCard.jsx
new file mode 100644
index 0000000000..9284369239
--- /dev/null
+++ b/src/components/learner-credit-management/cards/CourseCard.jsx
@@ -0,0 +1,97 @@
+/* eslint-disable @typescript-eslint/naming-convention */
+// variables taken from algolia not in camelcase
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { camelCaseObject } from '@edx/frontend-platform';
+import cardFallbackImg from '@edx/brand/paragon/images/card-imagecap-fallback.png';
+import {
+ Badge, Button, Card, Hyperlink,
+} from '@edx/paragon';
+import { EXEC_COURSE_TYPE } from '../data/constants';
+import { formatDate } from '../data/utils';
+
+const CourseCard = ({
+ onClick, original,
+}) => {
+ const {
+ title,
+ cardImageUrl,
+ courseType,
+ normalizedMetadata,
+ partners,
+ } = camelCaseObject(original);
+
+ let priceText;
+ const altText = `${title} course image`;
+
+ return (
+ onClick(original)}
+ orientation="horizontal"
+ tabIndex="0"
+ >
+
+
+
+
{title}
+
{partners[0]?.name}
+ {courseType === EXEC_COURSE_TYPE && (
+
+ Executive Education
+
+ )}
+ {courseType !== EXEC_COURSE_TYPE && (
+
+ )}
+
+ Starts {formatDate(normalizedMetadata?.start_date)} •
+ Learner must register by {formatDate(normalizedMetadata?.enroll_by_date)}
+
+
+
+ {priceText}
+ Per learner price
+
+ View course
+
+ Assign
+
+
+
+
+ );
+};
+
+CourseCard.defaultProps = {
+ onClick: () => {},
+};
+
+CourseCard.propTypes = {
+ onClick: PropTypes.func,
+ original: PropTypes.shape({
+ title: PropTypes.string,
+ cardImageUrl: PropTypes.string,
+ partners: PropTypes.arrayOf(
+ PropTypes.shape({
+ name: PropTypes.string,
+ logo_image_url: PropTypes.string,
+ }),
+ ),
+ normalizedMetadata: PropTypes.shape({
+ startDate: PropTypes.string,
+ endDate: PropTypes.string,
+ enrollByDate: PropTypes.string,
+ }),
+ courseType: PropTypes.string,
+ }).isRequired,
+};
+
+export default CourseCard;
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
new file mode 100644
index 0000000000..963137e178
--- /dev/null
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import CourseCard from './CourseCard';
+import { CONTENT_TYPE_COURSE, EXEC_ED_TITLE } from '../data/constants';
+
+jest.mock('@edx/frontend-platform', () => ({
+ ...jest.requireActual('@edx/frontend-platform'),
+}));
+
+const TEST_CATALOG = ['ayylmao'];
+
+const originalData = {
+ title: 'Course Title',
+ card_image_url: undefined,
+ partners: [{ logo_image_url: '', name: 'Course Provider' }],
+ first_enrollable_paid_seat_price: 100,
+ original_image_url: '',
+ enterprise_catalog_query_titles: TEST_CATALOG,
+ advertised_course_run: { pacing_type: 'self_paced' },
+};
+
+const defaultProps = {
+ original: originalData,
+ learningType: CONTENT_TYPE_COURSE,
+};
+
+const execEdData = {
+ title: 'Exec Ed Course Title',
+ card_image_url: undefined,
+ partners: [{ logo_image_url: '', name: 'Course Provider' }],
+ first_enrollable_paid_seat_price: 100,
+ original_image_url: '',
+ enterprise_catalog_query_titles: TEST_CATALOG,
+ advertised_course_run: { pacing_type: 'instructor_paced' },
+ entitlements: [{ price: '999.00' }],
+};
+
+const execEdProps = {
+ original: execEdData,
+ learningType: EXEC_ED_TITLE,
+};
+
+describe('Course card works as expected', () => {
+ test('card renders as expected', () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.queryByText(defaultProps.original.title)).toBeInTheDocument();
+ expect(
+ screen.queryByText(defaultProps.original.partners[0].name),
+ ).toBeInTheDocument();
+ expect(screen.queryByText('Course Title')).toBeInTheDocument();
+ expect(screen.queryByText('Per learner price')).toBeInTheDocument();
+ });
+ test('exec ed card renders as expected', () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.queryByText(execEdProps.original.title)).toBeInTheDocument();
+ expect(
+ screen.queryByText(execEdProps.original.partners[0].name),
+ ).toBeInTheDocument();
+ expect(screen.queryByText('Exec Ed Course Title')).toBeInTheDocument();
+ });
+ test('test card renders default image', async () => {
+ render(
+
+
+ ,
+ );
+ const imageAltText = `${originalData.title} course image`;
+ fireEvent.error(screen.getByAltText(imageAltText));
+ await expect(screen.getByAltText(imageAltText).src).not.toBeUndefined;
+ });
+});
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index defca8d674..37f453bd4a 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -24,3 +24,13 @@ export const BUDGET_DETAIL_TAB_LABELS = {
[BUDGET_DETAIL_ACTIVITY_TAB]: 'Activity',
[BUDGET_DETAIL_CATALOG_TAB]: 'Catalog',
};
+
+// Facet filters
+export const LEARNING_TYPE_REFINEMENT = 'learning_type';
+export const LANGUAGE_REFINEMENT = 'language';
+
+// Learning types
+export const CONTENT_TYPE_COURSE = 'course';
+export const EXEC_ED_TITLE = 'Executive Education';
+
+export const EXEC_COURSE_TYPE = 'executive-education-2u';
diff --git a/src/components/learner-credit-management/data/hooks/hooks.js b/src/components/learner-credit-management/data/hooks/hooks.js
new file mode 100644
index 0000000000..306ad58fde
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/hooks.js
@@ -0,0 +1,13 @@
+import { useMemo, useState } from 'react';
+
+import { CONTENT_TYPE_COURSE } from '../constants';
+
+// eslint-disable-next-line import/prefer-default-export
+export const useSelectedCourse = () => {
+ const [course, setCourse] = useState(null);
+ const isCourse = useMemo(
+ () => course?.contentType === CONTENT_TYPE_COURSE,
+ [course],
+ );
+ return [course, setCourse, isCourse];
+};
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 376479f6a5..4705d62507 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -1,4 +1,6 @@
import { v4 as uuidv4 } from 'uuid';
+import dayjs from 'dayjs';
+
import {
LOW_REMAINING_BALANCE_PERCENT_THRESHOLD,
NO_BALANCE_REMAINING_DOLLAR_THRESHOLD,
@@ -181,3 +183,7 @@ export const orderOffers = (offers) => {
return offers;
};
+
+export function formatDate(date) {
+ return dayjs(date).format('MMM D, YYYY');
+}
diff --git a/src/components/learner-credit-management/index.js b/src/components/learner-credit-management/index.js
new file mode 100644
index 0000000000..ff16dee97e
--- /dev/null
+++ b/src/components/learner-credit-management/index.js
@@ -0,0 +1,4 @@
+import MultipleBudgetsPage from './MultipleBudgetsPage';
+import './learner-credit.scss';
+
+export default MultipleBudgetsPage;
diff --git a/src/components/learner-credit-management/learner-credit.scss b/src/components/learner-credit-management/learner-credit.scss
new file mode 100644
index 0000000000..c26c3e9859
--- /dev/null
+++ b/src/components/learner-credit-management/learner-credit.scss
@@ -0,0 +1,25 @@
+.card-container {
+ display: flex;
+ padding: 1rem;
+ flex-grow: 1;
+ justify-content: space-between;
+
+ .section-1 {
+ flex-direction: column;
+ }
+ .section-2 {
+ margin-left: 0;
+ text-align: end !important;
+ min-width: 400px;
+ padding-right: 0;
+ justify-content: space-between;
+ .footer {
+ justify-content: end;
+ padding: 0;
+ }
+ }
+}
+
+.badge {
+ margin: 4px;
+}
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
new file mode 100644
index 0000000000..43cc7aae8d
--- /dev/null
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import algoliasearch from 'algoliasearch/lite';
+import { Configure, InstantSearch } from 'react-instantsearch-dom';
+
+import { FormattedMessage } from '@edx/frontend-platform/i18n';
+import { SearchHeader } from '@edx/frontend-enterprise-catalog-search';
+
+import { configuration } from '../../../config';
+import CatalogSearchResults from './CatalogSearchResults';
+
+const CatalogSearch = () => {
+ const { budgetId } = useParams();
+ const searchClient = algoliasearch(configuration.ALGOLIA.APP_ID, configuration.ALGOLIA.SEARCH_API_KEY);
+
+ const searchFilters = `enterprise_catalog_query_uuids:${budgetId}`;
+
+ return (
+
+ );
+};
+
+export default CatalogSearch;
diff --git a/src/components/learner-credit-management/search/CatalogSearchResults.jsx b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
new file mode 100644
index 0000000000..60c20e0262
--- /dev/null
+++ b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
@@ -0,0 +1,149 @@
+import React, { useEffect, useMemo } from 'react';
+import { connectStateResults } from 'react-instantsearch-dom';
+import PropTypes from 'prop-types';
+
+import { SearchPagination } from '@edx/frontend-enterprise-catalog-search';
+import { FormattedMessage, injectIntl } from '@edx/frontend-platform/i18n';
+import {
+ Alert, CardView, DataTable, Skeleton,
+} from '@edx/paragon';
+
+import CourseCard from '../cards/CourseCard';
+
+export const ERROR_MESSAGE = 'An error occurred while retrieving data';
+
+export const SKELETON_DATA_TESTID = 'enterprise-catalog-skeleton';
+
+/**
+ * The core search results rendering component.
+ *
+ * Wrapping this in `connectStateResults()` will inject the first few props.
+ *
+ * @param {object} args arguments
+ * @param {object} args.searchResults Results of search (see: `connectStateResults``)
+ * @param {Boolean} args.isSearchStalled Whether search is stalled (see: `connectStateResults`)
+ * @param {object} args.error Error with `message` field if available (see: `connectStateResults``)
+ */
+
+export const BaseCatalogSearchResults = ({
+ searchResults,
+ // algolia recommends this prop instead of searching
+ isSearchStalled,
+ error,
+ setNoContent,
+}) => {
+ const courseColumns = useMemo(
+ () => [
+ {
+ Header: 'Course name',
+ accessor: 'title',
+ },
+ {
+ Header: 'Partner',
+ accessor: 'partners[0].name',
+ },
+ {
+ Header: 'A la carte course price',
+ accessor: 'first_enrollable_paid_seat_price',
+ },
+ {
+ Header: 'Associated catalogs',
+ accessor: 'enterprise_catalog_query_titles',
+ },
+ ],
+ [],
+ );
+
+ const tableData = useMemo(
+ () => searchResults?.hits || [],
+ [searchResults?.hits],
+ );
+
+ const renderCardComponent = (props) => ;
+
+ useEffect(() => {
+ setNoContent(searchResults === null || searchResults?.nbHits === 0);
+ }, [searchResults, setNoContent]);
+
+ if (isSearchStalled) {
+ return (
+
+
+
+ );
+ }
+ if (error) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ renderCardComponent(props)}
+ />
+
+
+
+
+ );
+};
+
+BaseCatalogSearchResults.defaultProps = {
+ searchResults: { disjunctiveFacetsRefinements: [], nbHits: 0, hits: [] },
+ error: null,
+ paginationComponent: SearchPagination,
+ row: null,
+ preview: false,
+ setNoContent: () => {},
+};
+
+BaseCatalogSearchResults.propTypes = {
+ // from Algolia
+ searchResults: PropTypes.shape({
+ _state: PropTypes.shape({
+ disjunctiveFacetsRefinements: PropTypes.shape({}),
+ }),
+ disjunctiveFacetsRefinements: PropTypes.arrayOf(PropTypes.shape({})),
+ nbHits: PropTypes.number,
+ hits: PropTypes.arrayOf(PropTypes.shape({})),
+ nbPages: PropTypes.number,
+ hitsPerPage: PropTypes.number,
+ page: PropTypes.number,
+ }),
+ isSearchStalled: PropTypes.bool.isRequired,
+ error: PropTypes.shape({
+ message: PropTypes.string,
+ }),
+
+ searchState: PropTypes.shape({
+ page: PropTypes.number,
+ }).isRequired,
+ paginationComponent: PropTypes.func,
+ // eslint-disable-next-line react/no-unused-prop-types
+ row: PropTypes.string,
+ contentType: PropTypes.string.isRequired,
+ preview: PropTypes.bool,
+ setNoContent: PropTypes.func,
+};
+
+export default connectStateResults(injectIntl(BaseCatalogSearchResults));
diff --git a/src/components/learner-credit-management/tests/CatalogSearch.test.jsx b/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
new file mode 100644
index 0000000000..ee74751e87
--- /dev/null
+++ b/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import '@testing-library/jest-dom/extend-expect';
+import {
+ SEARCH_FACET_FILTERS,
+ SearchContext,
+} from '@edx/frontend-enterprise-catalog-search';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { screen } from '@testing-library/react';
+import { renderWithRouter } from '../../test/testUtils';
+import CatalogSearch from '../search/CatalogSearch';
+
+jest.mock('react-instantsearch-dom', () => ({
+ ...jest.requireActual('react-instantsearch-dom'),
+ InstantSearch: () => SEARCH
,
+ Index: () => SEARCH
,
+}));
+
+const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
+
+const SearchDataWrapper = ({ children, searchContextValue }) => (
+
+
+ {children}
+
+
+);
+
+describe('Catalog Search component', () => {
+ it('properly renders component', () => {
+ renderWithRouter(
+
+
+ ,
+ );
+ expect(screen.getByText('SEARCH')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
new file mode 100644
index 0000000000..34a75e3ac0
--- /dev/null
+++ b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
@@ -0,0 +1,154 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+import { SearchContext } from '@edx/frontend-enterprise-catalog-search';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+
+import { BaseCatalogSearchResults, SKELETON_DATA_TESTID } from '../search/CatalogSearchResults';
+
+import { renderWithRouter } from '../../test/testUtils';
+
+import { CONTENT_TYPE_COURSE } from '../data/constants';
+
+// Mocking this connected component so as not to have to mock the algolia Api
+const PAGINATE_ME = 'PAGINATE ME :)';
+const PaginationComponent = () => {PAGINATE_ME}
;
+
+// all we are testing is routes, we don't need InstantSearch to work here
+jest.mock('react-instantsearch-dom', () => ({
+ ...jest.requireActual('react-instantsearch-dom'),
+ InstantSearch: () => Popular Courses
,
+ Index: () => Popular Courses
,
+}));
+
+const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
+
+const SearchDataWrapper = ({
+
+ children,
+
+ searchContextValue = DEFAULT_SEARCH_CONTEXT_VALUE,
+}) => (
+
+ {children}
+
+);
+
+const mockConfig = () => ({
+ EDX_FOR_BUSINESS_TITLE: 'ayylmao',
+ EDX_ENTERPRISE_ALACARTE_TITLE: 'baz',
+ FEATURE_CARD_VIEW_ENABLED: 'True',
+});
+
+jest.mock('@edx/frontend-platform', () => ({
+ ...jest.requireActual('@edx/frontend-platform'),
+ getConfig: () => mockConfig(),
+}));
+
+const TEST_COURSE_NAME = 'test course';
+const TEST_PARTNER = 'edx';
+const TEST_CATALOGS = ['baz'];
+
+const TEST_COURSE_NAME_2 = 'test course 2';
+const TEST_PARTNER_2 = 'edx 2';
+const TEST_CATALOGS_2 = ['baz', 'ayylmao'];
+
+const searchResults = {
+ nbHits: 2,
+ hitsPerPage: 10,
+ pageIndex: 10,
+ pageCount: 5,
+ nbPages: 6,
+ hits: [
+ {
+ title: TEST_COURSE_NAME,
+ partners: [{ name: TEST_PARTNER, logo_image_url: '' }],
+ enterprise_catalog_query_titles: TEST_CATALOGS,
+ card_image_url: 'http://url.test.location',
+ first_enrollable_paid_seat_price: 100,
+ original_image_url: '',
+ availability: ['Available Now'],
+ content_type: CONTENT_TYPE_COURSE,
+ advertised_course_run: {
+ start: '2020-01-24T05:00:00Z',
+ end: '2080-01-01T17:00:00Z',
+ upgrade_deadline: 1892678399,
+ pacing_type: 'self_paced',
+ },
+ },
+ {
+ title: TEST_COURSE_NAME_2,
+ partners: [{ name: TEST_PARTNER_2, logo_image_url: '' }],
+ enterprise_catalog_query_titles: TEST_CATALOGS_2,
+ card_image_url: 'http://url.test2.location',
+ first_enrollable_paid_seat_price: 99,
+ original_image_url: '',
+ availability: ['Available Now'],
+ content_type: CONTENT_TYPE_COURSE,
+ advertised_course_run: {
+ start: '2020-01-24T05:00:00Z',
+ end: '2080-01-01T17:00:00Z',
+ upgrade_deadline: 1892678399,
+ pacing_type: 'self_paced',
+ },
+ },
+ ],
+ page: 1,
+ _state: { disjunctiveFacetsRefinements: { foo: 'bar' } },
+};
+
+const defaultProps = {
+ paginationComponent: PaginationComponent,
+ searchResults,
+ isSearchStalled: false,
+ searchState: { page: 1 },
+ error: null,
+ contentType: CONTENT_TYPE_COURSE,
+ // mock i18n requirements
+ intl: {
+ formatMessage: (header) => header.defaultMessage,
+ formatDate: () => {},
+ formatTime: () => {},
+ formatRelative: () => {},
+ formatNumber: () => {},
+ formatPlural: () => {},
+ formatHTMLMessage: () => {},
+ now: () => {},
+ },
+};
+
+describe('Main Catalogs view works as expected', () => {
+ const OLD_ENV = process.env;
+ beforeEach(() => {
+ jest.resetModules(); // Most important - it clears the cache
+ process.env = { ...OLD_ENV }; // Make a copy
+ });
+ afterEach(() => {
+ process.env = OLD_ENV; // Restore old environment
+ });
+
+ test('all courses rendered when search results available', async () => {
+ render(
+
+
+
+
+ ,
+ ,
+ );
+ expect(screen.queryByText(TEST_COURSE_NAME)).toBeInTheDocument();
+ expect(screen.queryByText(TEST_COURSE_NAME_2)).toBeInTheDocument();
+ expect(screen.getAllByText('Showing 2 of 2.')[0]).toBeInTheDocument();
+ });
+ test('isSearchStalled leads to rendering skeleton and not content', () => {
+ renderWithRouter(
+
+
+ ,
+ );
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ expect(screen.queryByText(TEST_COURSE_NAME)).not.toBeInTheDocument();
+ expect(screen.getByTestId(SKELETON_DATA_TESTID)).toBeInTheDocument();
+ });
+});
diff --git a/src/components/settings/tests/SettingsTabs.test.jsx b/src/components/settings/tests/SettingsTabs.test.jsx
index 7dff9d0a67..ed8576396d 100644
--- a/src/components/settings/tests/SettingsTabs.test.jsx
+++ b/src/components/settings/tests/SettingsTabs.test.jsx
@@ -76,7 +76,6 @@ const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
const defaultStore = getMockStore({ ...initialStore });
-// eslint-disable-next-line react/prop-types
const SettingsTabsWithRouter = ({ store = defaultStore }) => (
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/EnrollBulkAction.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/EnrollBulkAction.test.jsx
index c603c2ebd8..3d42aaa9a2 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/EnrollBulkAction.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/EnrollBulkAction.test.jsx
@@ -50,7 +50,6 @@ const initialStore = mockStore({
},
});
-// eslint-disable-next-line react/prop-types
const EnrollBulkActionWithProvider = ({ store = initialStore, ...rest }) => (
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx
index e01e23160d..329cff540b 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.test.jsx
@@ -42,7 +42,6 @@ const initialStore = mockStore({
},
});
-// eslint-disable-next-line react/prop-types
const RemindBulkActionWithProvider = ({ store = initialStore, ...rest }) => (
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx
index 2e2bbcf624..e5f947b638 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.test.jsx
@@ -39,7 +39,6 @@ const initialStore = mockStore({
},
});
-// eslint-disable-next-line react/prop-types
const RevokeBulkActionWithProvider = ({ store = initialStore, ...rest }) => (
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx
index 6ac5cc5086..de07041417 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/tests/index.test.jsx
@@ -58,7 +58,6 @@ const expiredSubscriptionPlan = (
};
};
-// eslint-disable-next-line react/prop-types
const LicenseManagementTableWrapper = ({ subscriptionPlan, ...props }) => (
diff --git a/src/components/subscriptions/tests/MultipleSubscriptionsPage.test.jsx b/src/components/subscriptions/tests/MultipleSubscriptionsPage.test.jsx
index e476bdce5a..7e3633180d 100644
--- a/src/components/subscriptions/tests/MultipleSubscriptionsPage.test.jsx
+++ b/src/components/subscriptions/tests/MultipleSubscriptionsPage.test.jsx
@@ -63,7 +63,6 @@ const defaultSubscriptions = {
const mockStore = configureMockStore([thunk]);
-// eslint-disable-next-line react/prop-types
const MultipleSubscriptionsPageWrapper = ({ subscriptions = defaultSubscriptions, ...props }) => (
diff --git a/src/components/subscriptions/tests/expiration/SubscriptionExpirationBanner.test.jsx b/src/components/subscriptions/tests/expiration/SubscriptionExpirationBanner.test.jsx
index a0dc6aea45..e743f3ae70 100644
--- a/src/components/subscriptions/tests/expiration/SubscriptionExpirationBanner.test.jsx
+++ b/src/components/subscriptions/tests/expiration/SubscriptionExpirationBanner.test.jsx
@@ -27,7 +27,7 @@ jest.mock('@edx/frontend-enterprise-utils', () => {
});
// PropType validation for state is done by SubscriptionManagementContext
-// eslint-disable-next-line react/prop-types
+
const ExpirationBannerWrapper = ({ detailState, isSubscriptionPlanDetails = false }) => (
diff --git a/src/components/subscriptions/tests/expiration/SubscriptionExpirationModals.test.jsx b/src/components/subscriptions/tests/expiration/SubscriptionExpirationModals.test.jsx
index 2dbbfe237a..a86f455417 100644
--- a/src/components/subscriptions/tests/expiration/SubscriptionExpirationModals.test.jsx
+++ b/src/components/subscriptions/tests/expiration/SubscriptionExpirationModals.test.jsx
@@ -29,7 +29,7 @@ jest.mock('@edx/frontend-enterprise-utils', () => {
});
// PropType validation for state is done by SubscriptionManagementContext
-// eslint-disable-next-line react/prop-types
+
const ExpirationModalsWithContext = ({ detailState }) => (
diff --git a/src/components/test/testUtils.jsx b/src/components/test/testUtils.jsx
index b5ac1bf34e..02a98208fb 100644
--- a/src/components/test/testUtils.jsx
+++ b/src/components/test/testUtils.jsx
@@ -12,7 +12,6 @@ export function renderWithRouter(
history = createMemoryHistory({ initialEntries: [route] }),
} = {},
) {
- // eslint-disable-next-line react/prop-types
const Wrapper = ({ children }) => (
{children}
);
diff --git a/src/containers/EnterpriseApp/EnterpriseApp.test.jsx b/src/containers/EnterpriseApp/EnterpriseApp.test.jsx
index 6281ab463d..f10e8b0dd8 100644
--- a/src/containers/EnterpriseApp/EnterpriseApp.test.jsx
+++ b/src/containers/EnterpriseApp/EnterpriseApp.test.jsx
@@ -47,13 +47,13 @@ const EnterpriseAppContextProvider = ({
jest.mock('../../components/EnterpriseApp/EnterpriseAppContextProvider', () => ({
__esModule: true,
...jest.requireActual('../../components/EnterpriseApp/EnterpriseAppContextProvider'),
- // eslint-disable-next-line react/prop-types
+
default: ({ children }) => {children} ,
}));
jest.mock('../Sidebar', () => ({
__esModule: true,
- // eslint-disable-next-line react/prop-types
+
default: ({ children }) => {children}
,
}));
@@ -94,7 +94,6 @@ const initialState = {
dashboardInsights: {},
};
-// eslint-disable-next-line react/prop-types
const EnterpriseAppWrapper = ({ store, initialEntries, ...props }) => (
diff --git a/src/data/hooks.js b/src/data/hooks.js
index 5f9e7df7b1..c44b512dd5 100644
--- a/src/data/hooks.js
+++ b/src/data/hooks.js
@@ -1,4 +1,8 @@
-import { useEffect, useRef } from 'react';
+import {
+ useEffect, useMemo, useState, useRef,
+} from 'react';
+
+import { CONTENT_TYPE_COURSE } from '../components/learner-credit-management/data/constants';
export function useInterval(callback, delay) {
const savedCallback = useRef();
@@ -41,3 +45,12 @@ export function useTimeout(callback, delay) {
timeoutIdRef.current = null;
}, [callback, delay]);
}
+
+export const useSelectedCourse = () => {
+ const [course, setCourse] = useState(null);
+ const isCourse = useMemo(
+ () => course?.contentType === CONTENT_TYPE_COURSE,
+ [course],
+ );
+ return [course, setCourse, isCourse];
+};
From cb6b6f89d2716eec449793f8167fa9a303f586d5 Mon Sep 17 00:00:00 2001
From: mahamakifdar19
Date: Thu, 12 Oct 2023 11:49:50 +0500
Subject: [PATCH 038/124] fix: hide track progress tab temporarily due to data
issues
---
src/components/Admin/AIAnalyticsSummary.jsx | 3 +-
.../Admin/AIAnalyticsSummary.test.jsx | 17 +++---
.../Admin/AIAnalyticsSummarySkeleton.jsx | 3 +-
.../AIAnalyticsSummary.test.jsx.snap | 60 +------------------
.../Admin/__snapshots__/Admin.test.jsx.snap | 2 +-
5 files changed, 17 insertions(+), 68 deletions(-)
diff --git a/src/components/Admin/AIAnalyticsSummary.jsx b/src/components/Admin/AIAnalyticsSummary.jsx
index 58ca4c7814..020e18df1a 100644
--- a/src/components/Admin/AIAnalyticsSummary.jsx
+++ b/src/components/Admin/AIAnalyticsSummary.jsx
@@ -71,9 +71,10 @@ const AIAnalyticsSummary = ({ enterpriseId, insights }) => {
>
+ {/* Track Progress is currently hidden due to data inconsistency. It will be addressed as part of ENT-7812 */}
{
showTrackProgressCard(true);
hideSummarizeCard(true);
diff --git a/src/components/Admin/AIAnalyticsSummary.test.jsx b/src/components/Admin/AIAnalyticsSummary.test.jsx
index 89b9a0c760..71d5cadf69 100644
--- a/src/components/Admin/AIAnalyticsSummary.test.jsx
+++ b/src/components/Admin/AIAnalyticsSummary.test.jsx
@@ -85,16 +85,17 @@ describe(' ', () => {
expect(tree).toMatchSnapshot();
});
- it('should display AnalyticsDetailCard with learner_progress data when Track Progress button is clicked', () => {
- const wrapper = mount( );
- wrapper.find('[data-testid="track-progress"]').first().simulate('click');
+ // Currently disabled due to data inconsistencies, will be addressed as a part of ENT-7812.
+ // it('should display AnalyticsDetailCard with learner_progress data when Track Progress button is clicked', () => {
+ // const wrapper = mount( );
+ // wrapper.find('[data-testid="track-progress"]').first().simulate('click');
- const tree = renderer
- .create( )
- .toJSON();
+ // const tree = renderer
+ // .create( )
+ // .toJSON();
- expect(tree).toMatchSnapshot();
- });
+ // expect(tree).toMatchSnapshot();
+ // });
it('should handle null analytics data', () => {
const insightsData = { ...mockedInsights, learner_engagement: null };
diff --git a/src/components/Admin/AIAnalyticsSummarySkeleton.jsx b/src/components/Admin/AIAnalyticsSummarySkeleton.jsx
index b44e6da4bd..89839f8f6e 100644
--- a/src/components/Admin/AIAnalyticsSummarySkeleton.jsx
+++ b/src/components/Admin/AIAnalyticsSummarySkeleton.jsx
@@ -4,7 +4,8 @@ import { Skeleton, Stack } from '@edx/paragon';
const AIAnalyticsSummarySkeleton = () => (
-
+ {/* Placeholder for Track Progress is currently hidden due to data inconsistency, will be addressed in ENT-7812 */}
+
);
diff --git a/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap b/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
index 2013f63e74..73d186b0e3 100644
--- a/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
@@ -28,61 +28,7 @@ Array [
Summarize Analytics
-
-
-
- Track Progress
-
-
,
- ",",
-]
-`;
-
-exports[`
should display AnalyticsDetailCard with learner_progress data when Track Progress button is clicked 1`] = `
-Array [
-
-
-
-
-
- Summarize Analytics
-
-
renders correctly with dashboard insights data renders dashbo
Summarize Analytics
Date: Thu, 12 Oct 2023 16:11:51 +0500
Subject: [PATCH 039/124] fix: detailpage routes issue
---
src/components/learner-credit-management/index.js | 4 ----
src/components/learner-credit-management/index.jsx | 1 +
2 files changed, 1 insertion(+), 4 deletions(-)
delete mode 100644 src/components/learner-credit-management/index.js
diff --git a/src/components/learner-credit-management/index.js b/src/components/learner-credit-management/index.js
deleted file mode 100644
index ff16dee97e..0000000000
--- a/src/components/learner-credit-management/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import MultipleBudgetsPage from './MultipleBudgetsPage';
-import './learner-credit.scss';
-
-export default MultipleBudgetsPage;
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
index 43786e8a0b..eb68ef0a21 100644
--- a/src/components/learner-credit-management/index.jsx
+++ b/src/components/learner-credit-management/index.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import { Route } from 'react-router-dom';
import PropTypes from 'prop-types';
import MultipleBudgetsPage from './MultipleBudgetsPage';
+import './learner-credit.scss';
import BudgetDetailPage from './BudgetDetailPage';
const LearnerCreditManagementRoutes = ({ match }) => (
From b71bdfffcc8668571ef320cd6fb56cfd1b90b146 Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Fri, 29 Sep 2023 15:53:27 +0000
Subject: [PATCH 040/124] feat: implementing sso orchestrator existing configs
page
---
.env.development | 1 +
package-lock.json | 44 ++++
package.json | 1 +
.../SettingsSSOTab/NewExistingSSOConfigs.jsx | 181 +++++++++++++
.../SettingsSSOTab/NewSSOConfigAlerts.jsx | 90 +++++++
.../SettingsSSOTab/NewSSOConfigCard.jsx | 215 +++++++++++++++
.../settings/SettingsSSOTab/hooks.js | 53 ++--
.../settings/SettingsSSOTab/index.jsx | 134 +++++++++-
.../tests/NewExistingSSOConfigs.test.jsx | 245 ++++++++++++++++++
.../tests/NewSSOConfigAlerts.test.jsx | 165 ++++++++++++
.../tests/NewSSOConfigCard.test.jsx | 222 ++++++++++++++++
.../tests/SettingsSSOTab.test.jsx | 45 +++-
src/components/settings/SettingsTabs.jsx | 20 +-
src/data/services/LmsApiService.js | 2 +-
14 files changed, 1386 insertions(+), 32 deletions(-)
create mode 100644 src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
create mode 100644 src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
create mode 100644 src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
create mode 100644 src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
create mode 100644 src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
create mode 100644 src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
diff --git a/.env.development b/.env.development
index 9e9f84cf22..8a4494712a 100644
--- a/.env.development
+++ b/.env.development
@@ -52,3 +52,4 @@ USE_API_CACHE='true'
SUBSCRIPTION_LPR='true'
PLOTLY_SERVER_URL='http://localhost:8050'
AUTH0_SELF_SERVICE_INTEGRATION='true'
+FEATURE_SSO_SETTINGS_TAB='true'
diff --git a/package-lock.json b/package-lock.json
index ea06117091..fe42c17d03 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
+ "@tanstack/react-query": "^4.35.7",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
@@ -5553,6 +5554,41 @@
"url": "https://github.com/sponsors/gregberge"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "4.35.7",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.35.7.tgz",
+ "integrity": "sha512-PgDJtX75ubFS0WCYFM7DqEoJ4QbxU3S5OH3gJSI40xr7UVVax3/J4CM3XUMOTs+EOT5YGEfssi3tfRVGte4DEw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "4.35.7",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.35.7.tgz",
+ "integrity": "sha512-0MankquP/6EOM2ATfEov6ViiKemey5uTbjGlFMX1xGotwNaqC76YKDMJdHumZupPbZcZPWAeoPGEHQmVKIKoOQ==",
+ "dependencies": {
+ "@tanstack/query-core": "4.35.7",
+ "use-sync-external-store": "^1.2.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-native": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz",
@@ -22817,6 +22853,14 @@
}
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/package.json b/package.json
index c358cbf26d..fbd1b8ffac 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
"@edx/paragon": "20.39.2",
+ "@tanstack/react-query": "^4.35.7",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
diff --git a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
new file mode 100644
index 0000000000..6474ef6a59
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
@@ -0,0 +1,181 @@
+import _ from 'lodash';
+import {
+ CardGrid,
+ Skeleton,
+ useToggle,
+} from '@edx/paragon';
+import { useQuery, useQueryClient } from '@tanstack/react-query';
+import PropTypes from 'prop-types';
+import React, { useEffect, useState } from 'react';
+import { connect } from 'react-redux';
+import LmsApiService from '../../../data/services/LmsApiService';
+import NewSSOConfigAlerts from './NewSSOConfigAlerts';
+import NewSSOConfigCard from './NewSSOConfigCard';
+
+const FRESH_CONFIG_POLLING_INTERVAL = 30000;
+const UPDATED_CONFIG_POLLING_INTERVAL = 2000;
+
+const NewExistingSSOConfigs = ({
+ configs, refreshBool, setRefreshBool, enterpriseId,
+}) => {
+ const [inactiveConfigs, setInactiveConfigs] = useState([]);
+ const [activeConfigs, setActiveConfigs] = useState([]);
+ const [inProgressConfigs, setInProgressConfigs] = useState([]);
+ const [untestedConfigs, setUntestedConfigs] = useState([]);
+ const [liveConfigs, setLiveConfigs] = useState([]);
+ const [notConfiguredConfigs, setNotConfiguredConfigs] = useState([]);
+ const [queryForTestedConfigs, setQueryForTestedConfigs] = useState(false);
+ const [queryForConfiguredConfigs, setQueryForConfiguredConfigs] = useState(false);
+ const [intervalMs, setIntervalMs] = React.useState(FRESH_CONFIG_POLLING_INTERVAL);
+ const [loading, setLoading] = useState(false);
+ const [showAlerts, openAlerts, closeAlerts] = useToggle(false);
+
+ const queryClient = useQueryClient();
+
+ const renderCards = (gridTitle, configList) => {
+ if (configList.length > 0) {
+ return (
+
+
{gridTitle}
+
+ {configList.map((config) => (
+
+ ))}
+
+
+ );
+ }
+ return null;
+ };
+
+ useEffect(() => {
+ const [active, inactive] = _.partition(configs, config => config.active);
+ const inProgress = configs.filter(
+ config => (config.submitted_at && !config.configured_at) || (config.configured_at < config.submitted_at),
+ );
+ const untested = configs.filter(config => !config.validated_at);
+ const live = configs.filter(
+ config => (config.validated_at && config.active && config.validated_at > config.configured_at),
+ );
+ const notConfigured = configs.filter(config => !config.configured_at);
+
+ if (live.length >= 1) {
+ setLiveConfigs(live);
+ openAlerts();
+ }
+
+ setUntestedConfigs(untested);
+ if (untested.length >= 1) {
+ setQueryForTestedConfigs(true);
+ openAlerts();
+ }
+ setInProgressConfigs(inProgress);
+ if (inProgress.length >= 1) {
+ const beenConfigured = inProgress.filter(config => config.configured_at);
+ if (beenConfigured.length >= 1) {
+ setIntervalMs(UPDATED_CONFIG_POLLING_INTERVAL);
+ }
+ setQueryForConfiguredConfigs(true);
+ openAlerts();
+ }
+
+ if (notConfigured.length >= 1) {
+ setNotConfiguredConfigs(notConfigured);
+ }
+
+ setActiveConfigs(active);
+ setInactiveConfigs(inactive);
+ setLoading(false);
+ }, [configs, refreshBool, openAlerts]);
+
+ useQuery({
+ queryKey: ['ssoOrchestratorConfigPoll'],
+ queryFn: async () => {
+ const res = await LmsApiService.listEnterpriseSsoOrchestrationRecords(enterpriseId);
+ const inProgress = res.data.filter(
+ config => (config.submitted_at && !config.configured_at) || (config.configured_at < config.submitted_at),
+ );
+ const untested = res.data.filter(config => !config.validated_at || config.validated_at < config.configured_at);
+
+ if (queryForConfiguredConfigs) {
+ if (inProgress.length === 0) {
+ setRefreshBool(!refreshBool);
+ setQueryForConfiguredConfigs(false);
+ }
+ }
+
+ if (queryForTestedConfigs) {
+ if (untested.length === 0) {
+ setRefreshBool(!refreshBool);
+ setQueryForTestedConfigs(false);
+ }
+ }
+
+ if (inProgress.length === 0 && untested.length === 0) {
+ queryClient.invalidateQueries({ queryKey: ['ssoOrchestratorConfigPoll'] });
+ }
+
+ return res.data;
+ },
+ refetchInterval: intervalMs,
+ enabled: queryForTestedConfigs || queryForConfiguredConfigs,
+ refetchOnWindowFocus: true,
+ });
+
+ return (
+ <>
+ {!loading && (
+ <>
+ {showAlerts && (
+
+ )}
+ {renderCards('Active', activeConfigs)}
+ {renderCards('Inactive', inactiveConfigs)}
+ >
+ )}
+ {loading && (
+
+
+
+
+
+
+ )}
+ >
+ );
+};
+
+NewExistingSSOConfigs.propTypes = {
+ configs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ refreshBool: PropTypes.bool.isRequired,
+ setRefreshBool: PropTypes.func.isRequired,
+ enterpriseId: PropTypes.string.isRequired,
+};
+
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(NewExistingSSOConfigs);
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
new file mode 100644
index 0000000000..01fd8e047e
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
@@ -0,0 +1,90 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import {
+ CheckCircle, Warning,
+} from '@edx/paragon/icons';
+import { Alert } from '@edx/paragon';
+
+const NewSSOConfigAlerts = ({
+ inProgressConfigs,
+ untestedConfigs,
+ liveConfigs,
+ notConfigured,
+ contactEmail,
+ closeAlerts,
+}) => (
+ <>
+ {inProgressConfigs.length >= 1 && (
+
+ Your SSO Integration is in progress
+
+ edX is configuring your SSO. This step takes approximately{' '}
+ {notConfigured.length > 0 ? `five minutes. You will receive an email at ${contactEmail} when the configuration is complete` : 'fifteen seconds'}.
+
+
+ )}
+ {untestedConfigs.length >= 1 && inProgressConfigs.length === 0 && (
+
+ You need to test your SSO connection
+
+ Your SSO configuration has completed,
+ and you should have received an email with the following instructions:
+
+ 1. Copy the URL for your learner Portal dashboard below:
+
+ http://courses.edx.org/dashboard?tpa_hint=saml-bestrun-hana
+
+ 2: Launch a new incognito or private window and paste the copied URL into the URL bar to load your
+ learner Portal dashboard.
+
+ 3: When prompted, enter login credentials supported by your IDP to test your connection to edX.
+
+ Return to this window after completing the testing instructions.
+ This window will automatically update when a successful test is detected.
+
+
+ )}
+ {liveConfigs.length >= 1 && inProgressConfigs.length === 0 && untestedConfigs.length === 0 && (
+
+ Your SSO integration is live!
+
+ Great news! Your test was successful and your new SSO integration is live and ready to use.
+
+
+ )}
+ >
+);
+
+NewSSOConfigAlerts.propTypes = {
+ inProgressConfigs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ untestedConfigs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ liveConfigs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ notConfigured: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+ closeAlerts: PropTypes.func.isRequired,
+ contactEmail: PropTypes.string.isRequired,
+};
+
+const mapStateToProps = state => ({
+ contactEmail: state.portalConfiguration.contactEmail,
+});
+
+export default connect(mapStateToProps)(NewSSOConfigAlerts);
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
new file mode 100644
index 0000000000..9e83ef4b21
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
@@ -0,0 +1,215 @@
+import React, { useContext } from 'react';
+import {
+ Card, Badge, Button, Dropdown, IconButton, Icon, Tooltip, OverlayTrigger,
+} from '@edx/paragon';
+import PropTypes from 'prop-types';
+import {
+ Key, KeyOff, MoreVert,
+} from '@edx/paragon/icons';
+import { SSOConfigContext } from './SSOConfigContext';
+import LmsApiService from '../../../data/services/LmsApiService';
+
+const NewSSOConfigCard = ({
+ config,
+ setLoading,
+ setRefreshBool,
+ refreshBool,
+}) => {
+ const VALIDATED = config.validated_at;
+ const ENABLED = config.active;
+ const CONFIGURED = config.configured_at && (config.submitted_at < config.configured_at);
+ const SUBMITTED = config.submitted_at;
+
+ const { setProviderConfig } = useContext(SSOConfigContext);
+
+ const convertToReadableDate = (date) => {
+ const dateObj = new Date(date);
+ const options = { year: 'numeric', month: 'short', day: 'numeric' };
+ return new Intl.DateTimeFormat('en-US', options).format(dateObj);
+ };
+
+ const onDeleteClick = (deletedConfig) => {
+ setLoading(true);
+ LmsApiService.deleteEnterpriseSsoOrchestrationRecord(deletedConfig.uuid).then(() => {
+ setRefreshBool(!refreshBool);
+ });
+ };
+
+ const onDisableClick = (disabledConfig) => {
+ setLoading(true);
+ LmsApiService.updateEnterpriseSsoOrchestrationRecord({ active: false }, disabledConfig.uuid).then(() => {
+ setRefreshBool(!refreshBool);
+ });
+ };
+
+ const onEnableClick = (enabledConfig) => {
+ setLoading(true);
+ LmsApiService.updateEnterpriseSsoOrchestrationRecord({ active: true }, enabledConfig.uuid).then(() => {
+ setRefreshBool(!refreshBool);
+ });
+ };
+
+ const renderCardStatusIcon = () => (
+ <>
+ {VALIDATED && ENABLED && (
+ The integration is verified and working}
+ >
+
+
+ )}
+ {!VALIDATED && (
+ This integration has not been validated. Please follow the testing instructions to validate your integration.
+ )}
+ >
+
+
+ )}
+ {VALIDATED && !ENABLED && (
+
+ )}
+ >
+ );
+
+ const renderCardBadge = () => (
+ <>
+ {(!VALIDATED && SUBMITTED) && (
+
+ In-progress
+
+ )}
+ {VALIDATED && CONFIGURED && !ENABLED && (
+
+ Disabled
+
+ )}
+ >
+ );
+
+ const renderCardButton = () => (
+ <>
+ {!VALIDATED && CONFIGURED && (
+ setProviderConfig(config)}
+ variant="outline-primary"
+ data-testid="existing-sso-config-card-configure-button"
+ >
+ Configure
+
+ )}
+ {VALIDATED && CONFIGURED && !ENABLED && (
+ onEnableClick(config)}
+ variant="outline-primary"
+ data-testid="existing-sso-config-card-enable-button"
+ >
+ Enable
+
+ )}
+ >
+ );
+
+ return (
+
+
+ {renderCardStatusIcon()}
+ {config.display_name}
+ {renderCardBadge()}
+ {renderCardButton()}
+
+ )}
+ subtitle={(
+
+ Last modified {convertToReadableDate(config.modified)}
+
+ )}
+ actions={(!SUBMITTED || CONFIGURED) && (
+
+
+
+ {VALIDATED && (
+ setProviderConfig(config)}
+ >
+ Configure
+
+ )}
+ {(!ENABLED || !VALIDATED) && (
+ onDeleteClick(config)}
+ >
+ Delete
+
+ )}
+ {ENABLED && VALIDATED && (
+ onDisableClick(config)}
+ >
+ Disable
+
+ )}
+
+
+ )}
+ />
+
+ );
+};
+
+NewSSOConfigCard.propTypes = {
+ config: PropTypes.shape({
+ uuid: PropTypes.string,
+ display_name: PropTypes.string,
+ active: PropTypes.bool,
+ modified: PropTypes.string,
+ validated_at: PropTypes.string,
+ configured_at: PropTypes.string,
+ submitted_at: PropTypes.string,
+ }).isRequired,
+ setLoading: PropTypes.func.isRequired,
+ setRefreshBool: PropTypes.func.isRequired,
+ refreshBool: PropTypes.bool.isRequired,
+};
+
+export default NewSSOConfigCard;
diff --git a/src/components/settings/SettingsSSOTab/hooks.js b/src/components/settings/SettingsSSOTab/hooks.js
index 04a2002af8..4069de6804 100644
--- a/src/components/settings/SettingsSSOTab/hooks.js
+++ b/src/components/settings/SettingsSSOTab/hooks.js
@@ -10,6 +10,7 @@ import {
updateIdpDirtyState,
} from './data/actions';
import { updateSamlProviderData, deleteSamlProviderData } from './utils';
+import { features } from '../../../config';
const useIdpState = () => {
const {
@@ -179,23 +180,43 @@ const useExistingSSOConfigs = (enterpriseUuid, refreshBool) => {
const [error, setError] = useState(null);
useEffect(() => {
+ const { AUTH0_SELF_SERVICE_INTEGRATION } = features;
if (enterpriseUuid) {
- const fetchConfig = async () => {
- const response = await LmsApiService.getProviderConfig(enterpriseUuid);
- return response.data.results;
- };
- fetchConfig().then(configs => {
- setSsoConfigs(configs);
- setLoading(false);
- }).catch(err => {
- setLoading(false);
- if (err.customAttributes?.httpErrorStatus !== 404) {
- // nothing found is okay for this fetcher.
- setError(err);
- } else {
- setSsoConfigs([]);
- }
- });
+ if (!AUTH0_SELF_SERVICE_INTEGRATION) {
+ const fetchConfig = async () => {
+ const response = await LmsApiService.getProviderConfig(enterpriseUuid);
+ return response.data.results;
+ };
+ fetchConfig().then(configs => {
+ setSsoConfigs(configs);
+ setLoading(false);
+ }).catch(err => {
+ setLoading(false);
+ if (err.customAttributes?.httpErrorStatus !== 404) {
+ // nothing found is okay for this fetcher.
+ setError(err);
+ } else {
+ setSsoConfigs([]);
+ }
+ });
+ } else {
+ const fetchConfig = async () => {
+ const response = await LmsApiService.listEnterpriseSsoOrchestrationRecords(enterpriseUuid);
+ return response.data;
+ };
+ fetchConfig().then(orchestratorConfigs => {
+ setSsoConfigs(orchestratorConfigs);
+ setLoading(false);
+ }).catch(err => {
+ setLoading(false);
+ if (err.customAttributes?.httpErrorStatus !== 404) {
+ // nothing found is okay for this fetcher.
+ setError(err);
+ } else {
+ setSsoConfigs([]);
+ }
+ });
+ }
}
}, [enterpriseUuid, refreshBool]);
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index ff17bfe254..b1181c6817 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -1,15 +1,18 @@
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
- Alert, Hyperlink, Toast, Skeleton,
+ Alert, ActionRow, Button, Hyperlink, ModalDialog, Toast, Skeleton, useToggle,
} from '@edx/paragon';
-import { WarningFilled } from '@edx/paragon/icons';
+import { Add, WarningFilled } from '@edx/paragon/icons';
import { HELP_CENTER_SAML_LINK } from '../data/constants';
import { useExistingSSOConfigs, useExistingProviderData } from './hooks';
import NoSSOCard from './NoSSOCard';
import ExistingSSOConfigs from './ExistingSSOConfigs';
+import NewExistingSSOConfigs from './NewExistingSSOConfigs';
import NewSSOConfigForm from './NewSSOConfigForm';
import { SSOConfigContext, SSOConfigContextProvider } from './SSOConfigContext';
+import LmsApiService from '../../../data/services/LmsApiService';
+import { features } from '../../../config';
const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const {
@@ -21,22 +24,133 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const [existingProviderData, pdError, pdIsLoading] = useExistingProviderData(enterpriseId, refreshBool);
const [showNewSSOForm, setShowNewSSOForm] = useState(false);
const [showNoSSOCard, setShowNoSSOCard] = useState(false);
+ const { AUTH0_SELF_SERVICE_INTEGRATION } = features;
+ const [isOpen, open, close] = useToggle(false);
- useEffect(() => {
- let validConfigExists = false;
- existingConfigs.forEach(config => {
- if (config.was_valid_at) {
- validConfigExists = true;
- }
+ const newConfigurationButtonOnClick = async () => {
+ Promise.all(existingConfigs.map(config => LmsApiService.updateEnterpriseSsoOrchestrationRecord(
+ { active: false, is_removed: true },
+ config.uuid,
+ ))).then(() => {
+ setRefreshBool(!refreshBool);
+ close();
});
+ };
+
+ useEffect(() => {
+ if (AUTH0_SELF_SERVICE_INTEGRATION) {
+ setHasSSOConfig(existingConfigs.some(config => config.validated_at));
+ } else {
+ setHasSSOConfig(existingConfigs.some(config => config.was_valid_at));
+ }
if (!existingConfigs || existingConfigs?.length < 1) {
setShowNoSSOCard(true);
} else {
setShowNoSSOCard(false);
}
- setHasSSOConfig(validConfigExists);
- }, [existingConfigs, setHasSSOConfig]);
+ }, [AUTH0_SELF_SERVICE_INTEGRATION, existingConfigs, setHasSSOConfig]);
+ if (AUTH0_SELF_SERVICE_INTEGRATION) {
+ return (
+
+
+
+
+ Create new SSO configuration?
+
+
+
+
+ Only one SSO integration is supported at a time.
+
+ To continue updating and editing your SSO integration, select "Cancel" and then
+ "Configure" on the integration card. Creating a new SSO configuration will overwrite and delete
+ your existing SSO configuration.
+
+
+
+
+
+ Cancel
+
+
+ Create new SSO
+
+
+
+
+
+
Single Sign-On (SSO) Integrations
+
+ {existingConfigs?.length > 0 && (providerConfig === null) && (
+
+ New
+
+ )}
+
+ Help Center: Single Sign-On
+
+
+
+ {(!isLoading || !pdIsLoading) && (
+
+ {/* providerConfig represents the currently selected config to edit/create, if there are
+ existing configs but no providerConfig then we can safely render the listings page */}
+ {existingConfigs?.length > 0 && (providerConfig === null) && (
+
+ )}
+ {/* Nothing found so guide user to creation/edit form */}
+ {showNoSSOCard &&
}
+ {/* Since we found a selected providerConfig we know we are in editing mode and can safely
+ render the create/edit form */}
+ {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && (
)}
+ {error && (
+
+ An error occurred loading the SAML configs: {error?.message}
+
+ )}
+ {pdError && (
+
+ An error occurred loading the SAML data: {pdError?.message}
+
+ )}
+ {infoMessage && (
+
setInfoMessage(null)}
+ show={infoMessage.length > 0}
+ >
+ {infoMessage}
+
+ )}
+
+ )}
+ {(isLoading || pdIsLoading) &&
}
+
+ );
+ }
return (
diff --git a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
new file mode 100644
index 0000000000..cd0b7dfdd1
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
@@ -0,0 +1,245 @@
+import React from 'react';
+import '@testing-library/jest-dom/extend-expect';
+import {
+ QueryClient,
+ QueryClientProvider,
+} from '@tanstack/react-query';
+import userEvent from '@testing-library/user-event';
+import {
+ act,
+ render,
+ screen,
+ waitFor,
+} from '@testing-library/react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+
+import { Provider } from 'react-redux';
+import { getMockStore, enterpriseId } from '../testutils';
+import { features } from '../../../../config';
+import NewExistingSSOConfigs from '../NewExistingSSOConfigs';
+import { SSOConfigContext, SSO_INITIAL_STATE } from '../SSOConfigContext';
+import LmsApiService from '../../../../data/services/LmsApiService';
+
+const queryClient = new QueryClient({
+ queries: {
+ retry: true, // optional: you may disable automatic query retries for all queries or on a per-query basis.
+ },
+});
+
+jest.mock('../../utils');
+jest.mock('../../../../data/services/LmsApiService');
+const mockSetRefreshBool = jest.fn();
+
+const initialStore = {
+ portalConfiguration: {
+ enterpriseId,
+ enterpriseSlug: 'sluggy',
+ enterpriseName: 'sluggyent',
+ contactEmail: 'foobar',
+ },
+};
+const store = getMockStore({ contactEmail: 'foobar', ...initialStore });
+const inactiveConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: false,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2022-05-12T19:51:25Z',
+ validated_at: '2022-06-12T19:51:25Z',
+ submitted_at: '2022-04-12T19:51:25Z',
+ },
+];
+const activeConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: true,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2022-05-12T19:51:25Z',
+ validated_at: '2022-06-12T19:51:25Z',
+ submitted_at: '2022-04-12T19:51:25Z',
+ },
+];
+const unvalidatedConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: true,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2022-04-12T19:51:25Z',
+ validated_at: null,
+ submitted_at: '2022-04-12T19:51:25Z',
+ },
+];
+const inProgressConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: false,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2021-04-12T19:51:25Z',
+ validated_at: null,
+ submitted_at: '2022-04-12T19:51:25Z',
+ },
+];
+const notConfiguredConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: false,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: null,
+ validated_at: null,
+ submitted_at: '2022-04-12T19:51:25Z',
+ },
+];
+
+jest.mock('../data/actions');
+jest.mock('../../utils');
+const entryType = 'direct';
+const metadataURL = 'https://foobar.com';
+const entityID = 'foobar';
+const publicKey = 'abc123';
+const ssoUrl = 'https://foobar.com';
+const mockCreateOrUpdateIdpRecord = jest.fn();
+const mockHandleEntityIDUpdate = jest.fn();
+const mockHandleMetadataEntryTypeUpdate = jest.fn();
+jest.mock('../hooks', () => {
+ const originalModule = jest.requireActual('../hooks');
+ return {
+ ...originalModule,
+ useIdpState: () => ({
+ entryType,
+ metadataURL,
+ entityID,
+ publicKey,
+ ssoUrl,
+ createOrUpdateIdpRecord: mockCreateOrUpdateIdpRecord,
+ handleEntityIDUpdate: mockHandleEntityIDUpdate,
+ handleMetadataEntryTypeUpdate: mockHandleMetadataEntryTypeUpdate,
+ }),
+ useExistingSSOConfigs: () => [[{ hehe: 'haha' }], null, true],
+ };
+});
+
+const mockSetProviderConfig = jest.fn();
+const contextValue = {
+ ...SSO_INITIAL_STATE,
+ setCurrentError: jest.fn(),
+ currentError: null,
+ dispatchSsoState: jest.fn(),
+ ssoState: {
+ idp: {
+ metadataURL: '',
+ entityID: '',
+ entryType: '',
+ isDirty: false,
+ },
+ serviceprovider: {
+ isSPConfigured: false,
+ },
+ refreshBool: false,
+ providerConfig: {
+ id: 1337,
+ },
+ },
+ setProviderConfig: mockSetProviderConfig,
+ setRefreshBool: jest.fn(),
+};
+
+const setupNewExistingSSOConfigs = (configs) => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = true;
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('New Existing SSO Configs tests', () => {
+ afterEach(() => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = false;
+ jest.clearAllMocks();
+ });
+ test('checks and sets in progress configs', async () => {
+ setupNewExistingSSOConfigs(inProgressConfig);
+ expect(
+ screen.queryByText(
+ 'Your SSO Integration is in progress',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('checks and sets not configured configs', async () => {
+ setupNewExistingSSOConfigs(notConfiguredConfig);
+ expect(
+ screen.queryByText(
+ 'Your SSO Integration is in progress',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('checks and sets validated configs', async () => {
+ setupNewExistingSSOConfigs(activeConfig);
+ expect(
+ screen.queryByText(
+ 'Your SSO integration is live!',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('checks and sets un-validated configs', async () => {
+ setupNewExistingSSOConfigs(unvalidatedConfig);
+ expect(
+ screen.queryByText(
+ 'You need to test your SSO connection',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('polls for finished configs', async () => {
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockImplementation(() => Promise.resolve({
+ data: [{
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: true,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2022-05-12T19:51:25Z',
+ validated_at: '2022-06-12T19:51:25Z',
+ submitted_at: '2022-04-12T19:51:25Z',
+ }],
+ }));
+ setupNewExistingSSOConfigs(inProgressConfig);
+ expect(
+ screen.queryByText(
+ 'Your SSO Integration is in progress',
+ ),
+ ).toBeInTheDocument();
+ await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
+ expect(mockSetRefreshBool).toHaveBeenCalledTimes(2);
+ });
+ test('enabling config sets loading and renders skeleton', async () => {
+ const spy = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
+ spy.mockImplementation(() => Promise.resolve({}));
+ setupNewExistingSSOConfigs(inactiveConfig);
+ const button = screen.getByTestId('existing-sso-config-card-enable-button');
+ act(() => {
+ userEvent.click(button);
+ });
+ expect(spy).toBeCalledTimes(1);
+ await waitFor(() => expect(
+ screen.queryByTestId(
+ 'sso-self-service-skeleton',
+ ),
+ ).toBeInTheDocument());
+ });
+});
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
new file mode 100644
index 0000000000..c62c7972ac
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
@@ -0,0 +1,165 @@
+import React from 'react';
+import '@testing-library/jest-dom/extend-expect';
+import userEvent from '@testing-library/user-event';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { Provider } from 'react-redux';
+import { render, screen, waitFor } from '@testing-library/react';
+import { SSOConfigContext, SSO_INITIAL_STATE } from '../SSOConfigContext';
+import { getMockStore, initialStore } from '../testutils';
+import NewSSOConfigAlerts from '../NewSSOConfigAlerts';
+
+const store = getMockStore({ contactEmail: 'foobar', ...initialStore });
+const mockSetProviderConfig = jest.fn();
+const contextValue = {
+ ...SSO_INITIAL_STATE,
+ setCurrentError: jest.fn(),
+ currentError: null,
+ dispatchSsoState: jest.fn(),
+ ssoState: {
+ idp: {
+ metadataURL: '',
+ entityID: '',
+ entryType: '',
+ isDirty: false,
+ },
+ serviceprovider: {
+ isSPConfigured: false,
+ },
+ refreshBool: false,
+ providerConfig: {
+ id: 1337,
+ },
+ },
+ setProviderConfig: mockSetProviderConfig,
+ setRefreshBool: jest.fn(),
+};
+
+describe('New SSO Config Alerts Tests', () => {
+ test('displays inProgress alert properly', async () => {
+ render(
+
+
+
+ ,
+
+
+ ,
+ );
+ expect(
+ screen.queryByText(
+ 'Your SSO Integration is in progress',
+ ),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByText(
+ 'You need to test your SSO connection',
+ ),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByText(
+ 'Your SSO integration is live!',
+ ),
+ ).not.toBeInTheDocument();
+ });
+ test('inProgress alert accounts for if configured before', () => {
+ render(
+
+
+
+ ,
+
+
+ ,
+ );
+ expect(
+ screen.getByText(
+ 'five minutes',
+ { exact: false },
+ ),
+ ).toBeInTheDocument();
+ });
+ test('displays untested alert properly', () => {
+ render(
+
+
+
+ ,
+
+
+ ,
+ );
+ expect(
+ screen.queryByText(
+ 'You need to test your SSO connection',
+ ),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByText(
+ 'Your SSO integration is live!',
+ ),
+ ).not.toBeInTheDocument();
+ });
+ test('displays live alert properly', () => {
+ render(
+
+
+
+ ,
+
+
+ ,
+ );
+ expect(
+ screen.queryByText(
+ 'Your SSO integration is live!',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('calls closeAlerts prop on close', async () => {
+ const mockCloseAlerts = jest.fn();
+ render(
+
+
+
+ ,
+
+
+ ,
+ );
+ await waitFor(() => {
+ userEvent.click(screen.getByText('Dismiss'));
+ }, []).then(() => {
+ expect(mockCloseAlerts).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
new file mode 100644
index 0000000000..a953ecb752
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
@@ -0,0 +1,222 @@
+import React from 'react';
+import '@testing-library/jest-dom/extend-expect';
+import userEvent from '@testing-library/user-event';
+import { act, render, screen } from '@testing-library/react';
+import NewSSOConfigCard from '../NewSSOConfigCard';
+import LmsApiService from '../../../../data/services/LmsApiService';
+
+describe('New SSO Config Card Tests', () => {
+ test('displays enabled and validated status icon properly', async () => {
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-enabled-icon',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('displays not validated status icon properly', async () => {
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-not-validated-icon',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('displays not validated status icon properly', async () => {
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-not-active-icon',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('displays badges properly', async () => {
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-badge-in-progress',
+ ),
+ ).toBeInTheDocument();
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-badge-disabled',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('displays configure button properly', async () => {
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-configure-button',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('displays enable button properly', async () => {
+ render(
+
,
+ );
+ expect(
+ screen.getByTestId(
+ 'existing-sso-config-card-enable-button',
+ ),
+ ).toBeInTheDocument();
+ });
+ test('handles kebob Delete dropdown option', async () => {
+ const spy = jest.spyOn(LmsApiService, 'deleteEnterpriseSsoOrchestrationRecord');
+ spy.mockImplementation(() => Promise.resolve({}));
+ render(
+
,
+ );
+ act(() => {
+ userEvent.click(screen.getByTestId('existing-sso-config-card-dropdown'));
+ });
+ act(() => {
+ userEvent.click(screen.getByTestId('existing-sso-config-delete-dropdown'));
+ });
+ expect(spy).toBeCalledTimes(1);
+ });
+ test('handles kebob Disable dropdown option', async () => {
+ const spy = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
+ spy.mockImplementation(() => Promise.resolve({}));
+ render(
+
,
+ );
+ act(() => {
+ userEvent.click(screen.getByTestId('existing-sso-config-card-dropdown'));
+ });
+ act(() => {
+ userEvent.click(screen.getByTestId('existing-sso-config-disable-dropdown'));
+ });
+ expect(spy).toBeCalledTimes(1);
+ });
+});
diff --git a/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx b/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx
index 4a05d2b026..a46e4697d8 100644
--- a/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx
@@ -1,12 +1,18 @@
import {
act, render, screen, waitFor,
} from '@testing-library/react';
+import {
+ QueryClient,
+ QueryClientProvider,
+} from '@tanstack/react-query';
import '@testing-library/jest-dom/extend-expect';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Provider } from 'react-redux';
import { HELP_CENTER_SAML_LINK } from '../../data/constants';
+import { features } from '../../../../config';
import SettingsSSOTab from '..';
import LmsApiService from '../../../../data/services/LmsApiService';
@@ -19,14 +25,24 @@ const initialStore = {
enterpriseId,
enterpriseSlug: 'sluggy',
enterpriseName: 'sluggyent',
+ contactEmail: 'foobar',
},
};
+const queryClient = new QueryClient({
+ queries: {
+ retry: true, // optional: you may disable automatic query retries for all queries or on a per-query basis.
+ },
+});
const mockStore = configureMockStore([thunk]);
const getMockStore = aStore => mockStore(aStore);
const store = getMockStore({ ...initialStore });
describe('SAML Config Tab', () => {
+ afterEach(() => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = false;
+ jest.clearAllMocks();
+ });
test('renders base page with correct text and help center link', async () => {
const aResult = () => Promise.resolve(1);
LmsApiService.getProviderConfig.mockImplementation(() => (
@@ -57,7 +73,7 @@ describe('SAML Config Tab', () => {
() => expect(mockSetHasSSOConfig).toBeCalledWith(false),
);
});
- test('page sets has valid sso config with valid configs ', async () => {
+ test('page sets has valid sso config with valid configs', async () => {
LmsApiService.getProviderConfig.mockImplementation(() => (
{ data: { results: [{ was_valid_at: '10/10/22' }] } }
));
@@ -70,4 +86,31 @@ describe('SAML Config Tab', () => {
() => expect(mockSetHasSSOConfig).toBeCalledWith(true),
);
});
+ test('page renders new sso self service tool properly', async () => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = true;
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockImplementation(() => Promise.resolve({
+ data: [{
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: true,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2022-05-12T19:51:25Z',
+ validated_at: '2022-06-12T19:51:25Z',
+ submitted_at: '2022-04-12T19:51:25Z',
+ }],
+ }));
+ await waitFor(() => render(
+
+
+
+
+ ,
+
+ ,
+ ));
+ expect(screen.queryByText(
+ 'Great news! Your test was successful and your new SSO integration is live and ready to use.',
+ )).toBeInTheDocument();
+ });
});
diff --git a/src/components/settings/SettingsTabs.jsx b/src/components/settings/SettingsTabs.jsx
index 8f471233c3..729ac106bc 100644
--- a/src/components/settings/SettingsTabs.jsx
+++ b/src/components/settings/SettingsTabs.jsx
@@ -1,4 +1,8 @@
import React, { useState, useMemo } from 'react';
+import {
+ QueryClient,
+ QueryClientProvider,
+} from '@tanstack/react-query';
import {
Container,
Tabs,
@@ -27,6 +31,12 @@ import SettingsApiCredentialsTab from './SettingsApiCredentialsTab';
import { features } from '../../config';
import { updatePortalConfigurationEvent } from '../../data/actions/portalConfiguration';
+const queryClient = new QueryClient({
+ queries: {
+ retry: true, // optional: you may disable automatic query retries for all queries or on a per-query basis.
+ },
+});
+
const SettingsTabs = ({
enterpriseId,
enterpriseSlug,
@@ -80,10 +90,12 @@ const SettingsTabs = ({
eventKey={SETTINGS_TABS_VALUES.sso}
title={SETTINGS_TAB_LABELS.sso}
>
-
+
+
+
,
);
}
diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js
index 18f2cca502..b3c32f662d 100644
--- a/src/data/services/LmsApiService.js
+++ b/src/data/services/LmsApiService.js
@@ -46,7 +46,7 @@ class LmsApiService {
return LmsApiService.apiClient().get(enterpriseSsoOrchestrationFetchUrl);
}
- static listEnterpriseSsoOrchestration(enterpriseCustomerUuid) {
+ static listEnterpriseSsoOrchestrationRecords(enterpriseCustomerUuid) {
const enterpriseSsoOrchestrationListUrl = `${LmsApiService.enterpriseSsoOrchestrationUrl}`;
if (enterpriseCustomerUuid) {
return LmsApiService.apiClient().get(`${enterpriseSsoOrchestrationListUrl}?enterprise_customer=${enterpriseCustomerUuid}`);
From 03e4c8dbe51194b795a3a3dedc21cad3cddaf85b Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Tue, 17 Oct 2023 18:44:10 +0000
Subject: [PATCH 041/124] feat: putting live sso config alert behind a cookie
---
.../SettingsSSOTab/NewSSOConfigAlerts.jsx | 142 ++++++++++--------
.../SettingsSSOTab/NewSSOConfigCard.jsx | 29 ++--
.../tests/NewSSOConfigAlerts.test.jsx | 48 +++++-
.../tests/NewSSOConfigCard.test.jsx | 12 +-
4 files changed, 149 insertions(+), 82 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
index 01fd8e047e..26f1e24c05 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
@@ -5,6 +5,11 @@ import {
CheckCircle, Warning,
} from '@edx/paragon/icons';
import { Alert } from '@edx/paragon';
+import Cookies from 'universal-cookie';
+
+export const SSO_SETUP_COMPLETION_COOKIE_NAME = 'dismissed-sso-completion-alert';
+const SSO_ALERT_OVERRIDE_PARAM = 'sso_alert_override';
+const ssoCookies = new Cookies();
const NewSSOConfigAlerts = ({
inProgressConfigs,
@@ -13,66 +18,83 @@ const NewSSOConfigAlerts = ({
notConfigured,
contactEmail,
closeAlerts,
-}) => (
- <>
- {inProgressConfigs.length >= 1 && (
-
- Your SSO Integration is in progress
-
- edX is configuring your SSO. This step takes approximately{' '}
- {notConfigured.length > 0 ? `five minutes. You will receive an email at ${contactEmail} when the configuration is complete` : 'fifteen seconds'}.
-
-
- )}
- {untestedConfigs.length >= 1 && inProgressConfigs.length === 0 && (
-
- You need to test your SSO connection
-
- Your SSO configuration has completed,
- and you should have received an email with the following instructions:
-
- 1. Copy the URL for your learner Portal dashboard below:
-
- http://courses.edx.org/dashboard?tpa_hint=saml-bestrun-hana
-
- 2: Launch a new incognito or private window and paste the copied URL into the URL bar to load your
- learner Portal dashboard.
-
- 3: When prompted, enter login credentials supported by your IDP to test your connection to edX.
-
- Return to this window after completing the testing instructions.
- This window will automatically update when a successful test is detected.
-
-
- )}
- {liveConfigs.length >= 1 && inProgressConfigs.length === 0 && untestedConfigs.length === 0 && (
-
- Your SSO integration is live!
-
- Great news! Your test was successful and your new SSO integration is live and ready to use.
-
-
- )}
- >
-);
+}) => {
+ const dismissSetupCompleteAlert = () => {
+ ssoCookies.set(
+ SSO_SETUP_COMPLETION_COOKIE_NAME,
+ true,
+ { sameSite: 'strict' },
+ );
+ closeAlerts();
+ };
+
+ const searchParams = new URLSearchParams(window.location.search);
+ const dismissedSSOSetupCompletionCookie = ssoCookies.get(SSO_SETUP_COMPLETION_COOKIE_NAME) === 'true';
+ const hideSSOLiveAlert = dismissedSSOSetupCompletionCookie && !searchParams.get(SSO_ALERT_OVERRIDE_PARAM);
+ return (
+ <>
+ {inProgressConfigs.length >= 1 && (
+
+ Your SSO Integration is in progress
+
+ edX is configuring your SSO. This step takes approximately{' '}
+ {notConfigured.length > 0 ? `five minutes. You will receive an email at ${contactEmail} when the configuration is complete` : 'fifteen seconds'}.
+
+
+ )}
+ {untestedConfigs.length >= 1 && inProgressConfigs.length === 0 && (
+
+ You need to test your SSO connection
+
+ Your SSO configuration has completed,
+ and you should have received an email with the following instructions:
+
+ 1. Copy the URL for your learner Portal dashboard below:
+
+ http://courses.edx.org/dashboard?tpa_hint=saml-bestrun-hana
+
+ 2: Launch a new incognito or private window and paste the copied URL into the URL bar to load your
+ learner Portal dashboard.
+
+ 3: When prompted, enter login credentials supported by your IDP to test your connection to edX.
+
+ Return to this window after completing the testing instructions.
+ This window will automatically update when a successful test is detected.
+
+
+ )}
+ {liveConfigs.length >= 1 && (
+ inProgressConfigs.length === 0) && (
+ untestedConfigs.length === 0) && (
+ !hideSSOLiveAlert) && (
+
+ Your SSO integration is live!
+
+ Great news! Your test was successful and your new SSO integration is live and ready to use.
+
+
+ )}
+ >
+ );
+};
NewSSOConfigAlerts.propTypes = {
inProgressConfigs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
index 9e83ef4b21..e7f94d0239 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
@@ -49,9 +49,16 @@ const NewSSOConfigCard = ({
});
};
+ const renderKeyOffIcon = (dataTestId) => (
+
+ );
+
const renderCardStatusIcon = () => (
<>
- {VALIDATED && ENABLED && (
+ {VALIDATED && ENABLED && CONFIGURED && (
)}
- {!VALIDATED && (
+ {!VALIDATED && CONFIGURED && (
This integration has not been validated. Please follow the testing instructions to validate your integration.
- )}
+ }
>
-
+ {renderKeyOffIcon('existing-sso-config-card-off-not-validated-icon')}
)}
- {VALIDATED && !ENABLED && (
-
+ {(!ENABLED || !CONFIGURED) && (
+ <>
+ {renderKeyOffIcon('existing-sso-config-card-off-icon')}
+ >
)}
>
);
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
index c62c7972ac..47c79f90fe 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
@@ -5,10 +5,19 @@ import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Provider } from 'react-redux';
import { render, screen, waitFor } from '@testing-library/react';
import { SSOConfigContext, SSO_INITIAL_STATE } from '../SSOConfigContext';
-import { getMockStore, initialStore } from '../testutils';
-import NewSSOConfigAlerts from '../NewSSOConfigAlerts';
+import { getMockStore } from '../testutils';
+import NewSSOConfigAlerts, { SSO_SETUP_COMPLETION_COOKIE_NAME } from '../NewSSOConfigAlerts';
-const store = getMockStore({ contactEmail: 'foobar', ...initialStore });
+const enterpriseId = 'an-id-1';
+const initialStore = {
+ portalConfiguration: {
+ enterpriseId,
+ enterpriseSlug: 'sluggy',
+ enterpriseName: 'sluggyent',
+ contactEmail: 'foobar',
+ },
+};
+const store = getMockStore({ ...initialStore });
const mockSetProviderConfig = jest.fn();
const contextValue = {
...SSO_INITIAL_STATE,
@@ -35,6 +44,9 @@ const contextValue = {
};
describe('New SSO Config Alerts Tests', () => {
+ afterEach(() => {
+ jest.resetAllMocks();
+ });
test('displays inProgress alert properly', async () => {
render(
@@ -118,6 +130,10 @@ describe('New SSO Config Alerts Tests', () => {
).not.toBeInTheDocument();
});
test('displays live alert properly', () => {
+ Object.defineProperty(window.document, 'cookie', {
+ writable: true,
+ value: `${SSO_SETUP_COMPLETION_COOKIE_NAME}=false`,
+ });
render(
@@ -162,4 +178,30 @@ describe('New SSO Config Alerts Tests', () => {
expect(mockCloseAlerts).toHaveBeenCalled();
});
});
+ test('hides live alert properly after dismissing', () => {
+ Object.defineProperty(window.document, 'cookie', {
+ writable: true,
+ value: `${SSO_SETUP_COMPLETION_COOKIE_NAME}=true`,
+ });
+ render(
+
+
+
+ ,
+
+
+ ,
+ );
+ expect(
+ screen.queryByText(
+ 'Your SSO integration is live!',
+ ),
+ ).not.toBeInTheDocument();
+ });
});
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
index a953ecb752..07398e464a 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
@@ -14,8 +14,8 @@ describe('New SSO Config Card Tests', () => {
uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
active: true,
modified: '2021-08-05T15:00:00Z',
- validated_at: '2021-08-05T15:00:00Z',
- configured_at: '2021-08-05T15:00:00Z',
+ validated_at: '2021-08-07T15:00:00Z',
+ configured_at: '2021-08-06T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
setLoading={jest.fn()}
@@ -39,7 +39,7 @@ describe('New SSO Config Card Tests', () => {
modified: '2021-08-05T15:00:00Z',
validated_at: null,
configured_at: '2021-08-05T15:00:00Z',
- submitted_at: '2021-08-05T15:00:00Z',
+ submitted_at: '2021-08-04T15:00:00Z',
}}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
@@ -48,11 +48,11 @@ describe('New SSO Config Card Tests', () => {
);
expect(
screen.getByTestId(
- 'existing-sso-config-card-not-validated-icon',
+ 'existing-sso-config-card-off-not-validated-icon',
),
).toBeInTheDocument();
});
- test('displays not validated status icon properly', async () => {
+ test('displays key off icon status icon properly', async () => {
render(
{
);
expect(
screen.getByTestId(
- 'existing-sso-config-card-not-active-icon',
+ 'existing-sso-config-card-off-icon',
),
).toBeInTheDocument();
});
From ba7c4a89b4a08a56975cca24234edaa1e9cc439b Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 19 Oct 2023 11:55:38 -0400
Subject: [PATCH 042/124] feat: add assigned table to LCM activity tab (#1054)
---
.env.development | 2 +-
package-lock.json | 100 ++++++-
package.json | 5 +-
.../AIAnalyticsSummary.test.jsx.snap | 12 +-
.../Admin/__snapshots__/Admin.test.jsx.snap | 206 +++++++--------
src/components/App/index.jsx | 135 ++++++----
.../ManageCodesTab.test.jsx.snap | 33 ++-
.../CodeSearchResults.test.jsx.snap | 26 +-
.../CompletedLearnersTable.test.jsx.snap | 2 +-
.../Coupon/__snapshots__/Coupon.test.jsx.snap | 8 +-
...rnersForInactiveCoursesTable.test.jsx.snap | 6 +-
.../EnrolledLearnersTable.test.jsx.snap | 2 +-
.../EnrollmentsTable.test.jsx.snap | 2 +-
.../__snapshots__/ErrorPage.test.jsx.snap | 20 +-
.../__snapshots__/ForbiddenPage.test.jsx.snap | 18 +-
.../LearnerActivityTable.test.jsx.snap | 14 +-
.../__snapshots__/NumberCard.test.jsx.snap | 6 +-
.../PastWeekPassedLearnersTable.test.jsx.snap | 4 +-
.../ReduxFormCheckbox.test.jsx.snap | 2 +
.../RegisteredLearnersTable.test.jsx.snap | 2 +-
.../__snapshots__/SearchBar.test.jsx.snap | 2 +-
src/components/Sidebar/index.jsx | 18 +-
...ubsidyRequestManagementTable.test.jsx.snap | 4 +-
.../AssignmentDetailsTableCell.jsx | 47 ++++
.../BudgetAssignmentsTable.jsx | 63 +++++
.../BudgetDetailActivityTabContents.jsx | 63 +++--
.../BudgetDetailAssignments.jsx | 38 +++
.../BudgetDetailPage.jsx | 79 ++----
.../BudgetDetailPageHeader.jsx | 50 ++++
.../BudgetDetailPageWrapper.jsx | 31 +++
.../BudgetDetailRedemptions.jsx | 57 ++++
.../BudgetDetailTabsAndRoutes.jsx | 30 ++-
...tate.jsx => CustomDataTableEmptyState.jsx} | 4 +-
.../EmailAddressTableCell.jsx | 71 +++--
.../LearnerCreditAllocationTable.jsx | 128 ++++-----
.../SpendTableEnrollmentDetails.jsx | 14 +-
.../data/constants.js | 5 +-
.../data/hooks/hooks.js | 13 -
.../data/hooks/index.js | 3 +
.../data/hooks/useBudgetContentAssignments.js | 65 +++++
.../hooks/useBudgetContentAssignments.test.js | 131 ++++++++++
.../data/hooks/useBudgetDetailTabs.jsx | 5 +-
.../data/hooks/useBudgetId.js | 25 ++
.../useOfferRedemptions.test.js} | 66 +----
.../data/hooks/useOfferSummary.test.js | 67 +++++
.../data/hooks/useSubsidyAccessPolicy.js | 32 +++
.../hooks/useSubsidyAccessPolicy.test.jsx | 121 +++++++++
.../learner-credit-management/data/utils.js | 3 +-
.../search/CatalogSearch.jsx | 2 +-
.../search/CatalogSearchResults.jsx | 5 -
.../tests/BudgetDetailPage.test.jsx | 247 ++++++++++++------
.../tests/EmailAddressTableCell.test.jsx | 77 +++++-
.../tests/MultipleBudgetsPage.test.jsx | 5 +-
src/components/settings/SettingsTabs.jsx | 21 +-
src/components/test/testUtils.jsx | 2 +-
.../__snapshots__/Sidebar.test.jsx.snap | 52 ++--
src/data/hooks.js | 15 +-
.../services/EnterpriseAccessApiService.js | 25 ++
.../tests/EnterpriseAccessApiService.test.js | 21 ++
59 files changed, 1627 insertions(+), 685 deletions(-)
create mode 100644 src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
create mode 100644 src/components/learner-credit-management/BudgetAssignmentsTable.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailAssignments.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailPageHeader.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailRedemptions.jsx
rename src/components/learner-credit-management/{SpendTableEmptyState.jsx => CustomDataTableEmptyState.jsx} (75%)
delete mode 100644 src/components/learner-credit-management/data/hooks/hooks.js
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetId.js
rename src/components/learner-credit-management/data/{tests/hooks.test.js => hooks/useOfferRedemptions.test.js} (60%)
create mode 100644 src/components/learner-credit-management/data/hooks/useOfferSummary.test.js
create mode 100644 src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
create mode 100644 src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
diff --git a/.env.development b/.env.development
index 8a4494712a..92e64b6562 100644
--- a/.env.development
+++ b/.env.development
@@ -41,6 +41,7 @@ FEATURE_SETTINGS_PAGE_APPEARANCE_TAB='true'
FEATURE_LEARNER_CREDIT_MANAGEMENT='true'
FEATURE_CONTENT_HIGHLIGHTS='true'
FEATURE_API_CREDENTIALS_TAB='true'
+FEATURE_SSO_SETTINGS_TAB='true'
HOTJAR_APP_ID=''
HOTJAR_VERSION='6'
HOTJAR_DEBUG=''
@@ -52,4 +53,3 @@ USE_API_CACHE='true'
SUBSCRIPTION_LPR='true'
PLOTLY_SERVER_URL='http://localhost:8050'
AUTH0_SELF_SERVICE_INTEGRATION='true'
-FEATURE_SSO_SETTINGS_TAB='true'
diff --git a/package-lock.json b/package-lock.json
index fe42c17d03..d37f4b5d06 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,8 +16,9 @@
"@edx/frontend-enterprise-logistration": "3.2.0",
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
- "@edx/paragon": "20.39.2",
- "@tanstack/react-query": "^4.35.7",
+ "@edx/paragon": "20.46.3",
+ "@tanstack/react-query": "4.36.1",
+ "@tanstack/react-query-devtools": "4.36.1",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
@@ -3594,9 +3595,9 @@
}
},
"node_modules/@edx/paragon": {
- "version": "20.39.2",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.39.2.tgz",
- "integrity": "sha512-SvJskMG+hjRAoteR+dhjXIFAAgMk4IgnDA4gvBomxOk/D+zV+E3mebEoslX2Qx+krRLSwmfQLWhWYn4qlps/5w==",
+ "version": "20.46.3",
+ "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.46.3.tgz",
+ "integrity": "sha512-cHxoxoOREVFbBqW9IRAtlIAQo1lcF9JJXkLoEw1Vam6oetKSa5Mc0SL5kykbV+1iRPP7kS8A0Csf5nRr0oolLQ==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
@@ -5554,21 +5555,36 @@
"url": "https://github.com/sponsors/gregberge"
}
},
+ "node_modules/@tanstack/match-sorter-utils": {
+ "version": "8.8.4",
+ "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz",
+ "integrity": "sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==",
+ "dependencies": {
+ "remove-accents": "0.4.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kentcdodds"
+ }
+ },
"node_modules/@tanstack/query-core": {
- "version": "4.35.7",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.35.7.tgz",
- "integrity": "sha512-PgDJtX75ubFS0WCYFM7DqEoJ4QbxU3S5OH3gJSI40xr7UVVax3/J4CM3XUMOTs+EOT5YGEfssi3tfRVGte4DEw==",
+ "version": "4.36.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
+ "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/react-query": {
- "version": "4.35.7",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.35.7.tgz",
- "integrity": "sha512-0MankquP/6EOM2ATfEov6ViiKemey5uTbjGlFMX1xGotwNaqC76YKDMJdHumZupPbZcZPWAeoPGEHQmVKIKoOQ==",
+ "version": "4.36.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz",
+ "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==",
"dependencies": {
- "@tanstack/query-core": "4.35.7",
+ "@tanstack/query-core": "4.36.1",
"use-sync-external-store": "^1.2.0"
},
"funding": {
@@ -5589,6 +5605,25 @@
}
}
},
+ "node_modules/@tanstack/react-query-devtools": {
+ "version": "4.36.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.36.1.tgz",
+ "integrity": "sha512-WYku83CKP3OevnYSG8Y/QO9g0rT75v1om5IvcWUwiUZJ4LanYGLVCZ8TdFG5jfsq4Ej/lu2wwDAULEUnRIMBSw==",
+ "dependencies": {
+ "@tanstack/match-sorter-utils": "^8.7.0",
+ "superjson": "^1.10.0",
+ "use-sync-external-store": "^1.2.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "@tanstack/react-query": "^4.36.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz",
@@ -8961,6 +8996,20 @@
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
"integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="
},
+ "node_modules/copy-anything": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
+ "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+ "dependencies": {
+ "is-what": "^4.1.8"
+ },
+ "engines": {
+ "node": ">=12.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
"node_modules/copy-descriptor": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@@ -13701,6 +13750,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-what": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.15.tgz",
+ "integrity": "sha512-uKua1wfy3Yt+YqsD6mTUEa2zSi3G1oPlqTflgaPJ7z63vUGN5pxFpnQfeSLMFnJDEsdvOtkp1rUWkYjB4YfhgA==",
+ "engines": {
+ "node": ">=12.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
"node_modules/is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@@ -19779,6 +19839,11 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/remove-accents": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
+ "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA=="
+ },
"node_modules/remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -21695,6 +21760,17 @@
"node": ">= 0.12"
}
},
+ "node_modules/superjson": {
+ "version": "1.13.3",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz",
+ "integrity": "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==",
+ "dependencies": {
+ "copy-anything": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
diff --git a/package.json b/package.json
index fbd1b8ffac..324366e66f 100644
--- a/package.json
+++ b/package.json
@@ -32,8 +32,9 @@
"@edx/frontend-enterprise-logistration": "3.2.0",
"@edx/frontend-enterprise-utils": "3.2.0",
"@edx/frontend-platform": "4.0.1",
- "@edx/paragon": "20.39.2",
- "@tanstack/react-query": "^4.35.7",
+ "@edx/paragon": "20.46.3",
+ "@tanstack/react-query": "4.36.1",
+ "@tanstack/react-query-devtools": "4.36.1",
"algoliasearch": "4.8.3",
"axios-mock-adapter": "1.19.0",
"classnames": "2.2.6",
diff --git a/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap b/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
index 73d186b0e3..ff111de353 100644
--- a/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/AIAnalyticsSummary.test.jsx.snap
@@ -21,7 +21,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -43,7 +43,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -75,7 +75,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -97,7 +97,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -129,7 +129,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -151,7 +151,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
index be393984b1..c74ac78cdd 100644
--- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap
+++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap
@@ -246,7 +246,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -297,7 +297,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -367,7 +367,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -418,7 +418,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -555,7 +555,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -657,7 +657,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -708,7 +708,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -821,7 +821,7 @@ exports[` renders correctly with dashboard analytics data renders # cou
xmlns="http://www.w3.org/2000/svg"
>
@@ -966,7 +966,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1017,7 +1017,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1087,7 +1087,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1138,7 +1138,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1275,7 +1275,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1377,7 +1377,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1428,7 +1428,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1541,7 +1541,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1686,7 +1686,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1737,7 +1737,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1807,7 +1807,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1858,7 +1858,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -1995,7 +1995,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -2097,7 +2097,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -2148,7 +2148,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -2261,7 +2261,7 @@ exports[` renders correctly with dashboard analytics data renders # of
xmlns="http://www.w3.org/2000/svg"
>
@@ -2410,7 +2410,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -2461,7 +2461,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -2531,7 +2531,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -2582,7 +2582,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -2719,7 +2719,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -2821,7 +2821,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -2872,7 +2872,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -3087,7 +3087,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -3175,7 +3175,7 @@ exports[` renders correctly with dashboard analytics data renders colla
xmlns="http://www.w3.org/2000/svg"
>
@@ -3277,7 +3277,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3328,7 +3328,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3398,7 +3398,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3449,7 +3449,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3586,7 +3586,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3688,7 +3688,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3739,7 +3739,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -3954,7 +3954,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -4042,7 +4042,7 @@ exports[` renders correctly with dashboard analytics data renders full
xmlns="http://www.w3.org/2000/svg"
>
@@ -4144,7 +4144,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4195,7 +4195,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4265,7 +4265,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4316,7 +4316,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4453,7 +4453,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4555,7 +4555,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4606,7 +4606,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4719,7 +4719,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4868,7 +4868,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4919,7 +4919,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -4989,7 +4989,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -5040,7 +5040,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -5177,7 +5177,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -5279,7 +5279,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -5330,7 +5330,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -5443,7 +5443,7 @@ exports[` renders correctly with dashboard analytics data renders inact
xmlns="http://www.w3.org/2000/svg"
>
@@ -5592,7 +5592,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -5643,7 +5643,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -5713,7 +5713,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -5764,7 +5764,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -5901,7 +5901,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -6003,7 +6003,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -6054,7 +6054,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -6167,7 +6167,7 @@ exports[` renders correctly with dashboard analytics data renders learn
xmlns="http://www.w3.org/2000/svg"
>
@@ -6316,7 +6316,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6367,7 +6367,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6437,7 +6437,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6488,7 +6488,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6625,7 +6625,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6727,7 +6727,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6778,7 +6778,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -6891,7 +6891,7 @@ exports[` renders correctly with dashboard analytics data renders regis
xmlns="http://www.w3.org/2000/svg"
>
@@ -7036,7 +7036,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7087,7 +7087,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7157,7 +7157,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7208,7 +7208,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7345,7 +7345,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7447,7 +7447,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7498,7 +7498,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7611,7 +7611,7 @@ exports[` renders correctly with dashboard analytics data renders top a
xmlns="http://www.w3.org/2000/svg"
>
@@ -7743,7 +7743,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -7765,7 +7765,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -7809,7 +7809,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -7860,7 +7860,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -7930,7 +7930,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -7981,7 +7981,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -8118,7 +8118,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -8220,7 +8220,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -8271,7 +8271,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -8486,7 +8486,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -8574,7 +8574,7 @@ exports[` renders correctly with dashboard insights data renders dashbo
xmlns="http://www.w3.org/2000/svg"
>
@@ -8665,7 +8665,7 @@ exports[` renders correctly with error state 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9045,7 +9045,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9096,7 +9096,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9166,7 +9166,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9217,7 +9217,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9354,7 +9354,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9456,7 +9456,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9507,7 +9507,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9722,7 +9722,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -9810,7 +9810,7 @@ exports[` renders correctly with no dashboard insights data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/App/index.jsx b/src/components/App/index.jsx
index 861bd7d056..5915c030a4 100644
--- a/src/components/App/index.jsx
+++ b/src/components/App/index.jsx
@@ -1,6 +1,11 @@
import React, { useEffect, useMemo } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import { Helmet } from 'react-helmet';
+import {
+ QueryClient,
+ QueryClientProvider,
+} from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { initializeHotjar } from '@edx/frontend-enterprise-hotjar';
import { AuthenticatedPageRoute, PageRoute, AppProvider } from '@edx/frontend-platform/react';
import { logError } from '@edx/frontend-platform/logging';
@@ -19,6 +24,19 @@ import { SystemWideWarningBanner } from '../system-wide-banner';
import store from '../../data/store';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // Specifying a longer `staleTime` of 20 seconds means queries will not refetch their data
+ // as often; mitigates making duplicate queries when within the `staleTime` window, instead
+ // relying on the cached data until the `staleTime` window has exceeded. This may be modified
+ // per-query, as needed, if certain queries expect to be more up-to-date than others. Allows
+ // `useQuery` to be used as a state manager.
+ staleTime: 1000 * 20,
+ },
+ },
+});
+
const AppWrapper = () => {
const apiClient = getAuthenticatedHttpClient();
const config = getConfig();
@@ -55,64 +73,67 @@ const AppWrapper = () => {
}, [config]);
return (
-
-
- {isMaintenanceAlertOpen && (
-
- {config.MAINTENANCE_ALERT_MESSAGE}
-
- )}
-
-
- }
- authenticatedAPIClient={apiClient}
- redirect={`${process.env.BASE_URL}/enterprises`}
- />
-
-
- (
-
-
-
-
- )}
- />
- }
- authenticatedAPIClient={apiClient}
- redirect={process.env.BASE_URL}
+
+
+
+
-
-
-
-
+ {isMaintenanceAlertOpen && (
+
+ {config.MAINTENANCE_ALERT_MESSAGE}
+
+ )}
+
+
+ }
+ authenticatedAPIClient={apiClient}
+ redirect={`${process.env.BASE_URL}/enterprises`}
+ />
+
+
+ (
+
+
+
+
+ )}
+ />
+ }
+ authenticatedAPIClient={apiClient}
+ redirect={process.env.BASE_URL}
+ />
+
+
+
+
+
);
};
diff --git a/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap b/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap
index 13de3d3b8a..b66b03dfe0 100644
--- a/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap
+++ b/src/components/CodeManagement/tests/__snapshots__/ManageCodesTab.test.jsx.snap
@@ -67,7 +67,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -105,7 +105,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -180,12 +180,9 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
-
@@ -271,7 +268,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -309,7 +306,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -384,7 +381,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -480,7 +477,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -518,7 +515,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -657,7 +654,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -695,7 +692,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -862,7 +859,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -993,7 +990,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -1058,7 +1055,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
@@ -1094,7 +1091,7 @@ Array [
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
index eb08be87a9..61f894cf87 100644
--- a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
+++ b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap
@@ -59,7 +59,7 @@ exports[` basic rendering should render empty table data 1`
xmlns="http://www.w3.org/2000/svg"
>
@@ -86,7 +86,7 @@ exports[` basic rendering should render empty table data 1`
xmlns="http://www.w3.org/2000/svg"
>
@@ -165,7 +165,7 @@ exports[` basic rendering should render error 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -192,7 +192,7 @@ exports[` basic rendering should render error 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -279,7 +279,7 @@ exports[` basic rendering should render loading 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -650,7 +650,7 @@ exports[` basic rendering should render table data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -754,7 +754,7 @@ exports[` basic rendering should render table data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -857,7 +857,7 @@ exports[` basic rendering should render table data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -979,7 +979,7 @@ exports[` basic rendering should render table data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -1015,7 +1015,7 @@ exports[` basic rendering should render table data 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -1092,7 +1092,7 @@ exports[` basic rendering should render table data when sea
xmlns="http://www.w3.org/2000/svg"
>
@@ -1283,7 +1283,7 @@ exports[` basic rendering should render table data when sea
xmlns="http://www.w3.org/2000/svg"
>
@@ -1319,7 +1319,7 @@ exports[` basic rendering should render table data when sea
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap b/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap
index 41fbb7a365..722c7b349f 100644
--- a/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap
+++ b/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap
@@ -19,7 +19,7 @@ exports[`CompletedLearnersTable renders empty state correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap b/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap
index 434813392e..a77defc1e3 100644
--- a/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap
+++ b/src/components/Coupon/__snapshots__/Coupon.test.jsx.snap
@@ -111,7 +111,7 @@ exports[` renders correctly with error state 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -135,7 +135,7 @@ exports[` renders correctly with error state 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -269,7 +269,7 @@ exports[` renders correctly with max uses 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -398,7 +398,7 @@ exports[` renders correctly without max uses 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap b/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap
index ea62dd2bae..415407b4ae 100644
--- a/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap
+++ b/src/components/EnrolledLearnersForInactiveCoursesTable/__snapshots__/EnrolledLearnersForInactiveCoursesTable.test.jsx.snap
@@ -19,7 +19,7 @@ exports[`EnrolledLearnersForInactiveCoursesTable renders empty state correctly 1
xmlns="http://www.w3.org/2000/svg"
>
@@ -315,7 +315,7 @@ exports[`EnrolledLearnersForInactiveCoursesTable renders enrolled learners for i
xmlns="http://www.w3.org/2000/svg"
>
@@ -351,7 +351,7 @@ exports[`EnrolledLearnersForInactiveCoursesTable renders enrolled learners for i
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap
index 301e26406f..829f004573 100644
--- a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap
+++ b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap
@@ -19,7 +19,7 @@ exports[`EnrolledLearnersTable renders empty state correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap b/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap
index 589f3003a1..5f9d166431 100644
--- a/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap
+++ b/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap
@@ -19,7 +19,7 @@ exports[`EnrollmentsTable renders empty state correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/ErrorPage/__snapshots__/ErrorPage.test.jsx.snap b/src/components/ErrorPage/__snapshots__/ErrorPage.test.jsx.snap
index 46e31f69b3..7572adf922 100644
--- a/src/components/ErrorPage/__snapshots__/ErrorPage.test.jsx.snap
+++ b/src/components/ErrorPage/__snapshots__/ErrorPage.test.jsx.snap
@@ -31,7 +31,7 @@ exports[` renders correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -84,18 +84,14 @@ exports[` renders correctly for 403 errors 1`] = `
For assistance, please contact the edX Customer Success team at
-
-
- customersuccess@edx.org
-
-
+ customersuccess@edx.org
+
.
diff --git a/src/components/ForbiddenPage/__snapshots__/ForbiddenPage.test.jsx.snap b/src/components/ForbiddenPage/__snapshots__/ForbiddenPage.test.jsx.snap
index f0602540eb..de29dfe42b 100644
--- a/src/components/ForbiddenPage/__snapshots__/ForbiddenPage.test.jsx.snap
+++ b/src/components/ForbiddenPage/__snapshots__/ForbiddenPage.test.jsx.snap
@@ -21,18 +21,14 @@ exports[`
renders correctly 1`] = `
For assistance, please contact the edX Customer Success team at
-
-
- customersuccess@edx.org
-
-
+ customersuccess@edx.org
+
.
diff --git a/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap b/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap
index 37df5114a7..045d0d6f0e 100644
--- a/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap
+++ b/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap
@@ -437,7 +437,7 @@ exports[`LearnerActivityTable renders active learners table correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -473,7 +473,7 @@ exports[`LearnerActivityTable renders active learners table correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -507,7 +507,7 @@ exports[`LearnerActivityTable renders empty state correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -923,7 +923,7 @@ exports[`LearnerActivityTable renders inactive past month learners table correct
xmlns="http://www.w3.org/2000/svg"
>
@@ -959,7 +959,7 @@ exports[`LearnerActivityTable renders inactive past month learners table correct
xmlns="http://www.w3.org/2000/svg"
>
@@ -1373,7 +1373,7 @@ exports[`LearnerActivityTable renders inactive past week learners table correctl
xmlns="http://www.w3.org/2000/svg"
>
@@ -1409,7 +1409,7 @@ exports[`LearnerActivityTable renders inactive past week learners table correctl
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap b/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap
index 415a2c1b3e..7586dfc531 100644
--- a/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap
+++ b/src/components/NumberCard/__snapshots__/NumberCard.test.jsx.snap
@@ -30,7 +30,7 @@ exports[`
renders correctly with detail actions 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -81,7 +81,7 @@ exports[`
renders correctly with detail actions 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -166,7 +166,7 @@ exports[`
renders correctly without detail actions 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap b/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap
index 5bd0b51726..1b48824ca2 100644
--- a/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap
+++ b/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap
@@ -209,7 +209,7 @@ exports[`PastWeekPassedLearnersTable renders table correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -245,7 +245,7 @@ exports[`PastWeekPassedLearnersTable renders table correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap b/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap
index 89d00e3afa..e3c7a71ec3 100644
--- a/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap
+++ b/src/components/ReduxFormCheckbox/__snapshots__/ReduxFormCheckbox.test.jsx.snap
@@ -11,6 +11,7 @@ exports[`
renders checked correctly 1`] = `
checked={true}
className="pgn__form-checkbox-input"
defaultValue={false}
+ disabled={false}
id="id"
type="checkbox"
/>
@@ -35,6 +36,7 @@ exports[`
renders unchecked correctly 1`] = `
checked={false}
className="pgn__form-checkbox-input"
defaultValue={false}
+ disabled={false}
id="id"
type="checkbox"
/>
diff --git a/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap b/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap
index c66304e864..a371442a53 100644
--- a/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap
+++ b/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap
@@ -19,7 +19,7 @@ exports[`RegisteredLearnersTable renders empty state correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/SearchBar/__snapshots__/SearchBar.test.jsx.snap b/src/components/SearchBar/__snapshots__/SearchBar.test.jsx.snap
index 493225f51d..d93f96f39b 100644
--- a/src/components/SearchBar/__snapshots__/SearchBar.test.jsx.snap
+++ b/src/components/SearchBar/__snapshots__/SearchBar.test.jsx.snap
@@ -51,7 +51,7 @@ exports[`
renders correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx
index 5a78bf11e8..a6cfd936d1 100644
--- a/src/components/Sidebar/index.jsx
+++ b/src/components/Sidebar/index.jsx
@@ -70,25 +70,25 @@ const Sidebar = ({
{
title: 'Learner Progress Report',
to: `${baseUrl}/admin/${ROUTE_NAMES.learners}`,
- icon:
,
+ icon:
,
},
{
title: 'Analytics',
to: `${baseUrl}/admin/${ROUTE_NAMES.analytics}`,
- icon:
,
+ icon:
,
hidden: !features.ANALYTICS || !enableAnalyticsScreen,
},
{
title: 'Code Management',
to: `${baseUrl}/admin/${ROUTE_NAMES.codeManagement}`,
- icon:
,
+ icon:
,
hidden: !features.CODE_MANAGEMENT || !enableCodeManagementScreen,
notification: !!subsidyRequestsCounts.couponCodes,
},
{
title: 'Subscription Management',
to: `${baseUrl}/admin/${ROUTE_NAMES.subscriptionManagement}`,
- icon:
,
+ icon:
,
hidden: !enableSubscriptionManagementScreen,
notification: !!subsidyRequestsCounts.subscriptionLicenses,
},
@@ -96,33 +96,33 @@ const Sidebar = ({
title: 'Learner Credit Management',
id: TOUR_TARGETS.LEARNER_CREDIT,
to: `${baseUrl}/admin/${ROUTE_NAMES.learnerCredit}`,
- icon:
,
+ icon:
,
hidden: !canManageLearnerCredit,
},
{
title: 'Highlights',
id: TOUR_TARGETS.CONTENT_HIGHLIGHTS,
to: `${baseUrl}/admin/${ROUTE_NAMES.contentHighlights}`,
- icon:
,
+ icon:
,
hidden: !FEATURE_CONTENT_HIGHLIGHTS || !enterpriseCuration?.isHighlightFeatureActive,
},
{
title: 'Reporting Configurations',
to: `${baseUrl}/admin/${ROUTE_NAMES.reporting}`,
- icon:
,
+ icon:
,
hidden: !features.REPORTING_CONFIGURATIONS || !enableReportingConfigScreen,
},
{
title: 'Settings',
id: TOUR_TARGETS.SETTINGS_SIDEBAR,
to: `${baseUrl}/admin/${ROUTE_NAMES.settings}`,
- icon:
,
+ icon:
,
},
// NOTE: keep "Support" link the last nav item
{
title: 'Support',
to: configuration.ENTERPRISE_SUPPORT_URL,
- icon:
,
+ icon:
,
hidden: !features.SUPPORT,
external: true,
},
diff --git a/src/components/SubsidyRequestManagementTable/tests/__snapshots__/SubsidyRequestManagementTable.test.jsx.snap b/src/components/SubsidyRequestManagementTable/tests/__snapshots__/SubsidyRequestManagementTable.test.jsx.snap
index 110de1903e..888fba9fb4 100644
--- a/src/components/SubsidyRequestManagementTable/tests/__snapshots__/SubsidyRequestManagementTable.test.jsx.snap
+++ b/src/components/SubsidyRequestManagementTable/tests/__snapshots__/SubsidyRequestManagementTable.test.jsx.snap
@@ -417,7 +417,7 @@ exports[`SubsidyRequestManagementTable renders data in a table as expected 1`] =
xmlns="http://www.w3.org/2000/svg"
>
@@ -452,7 +452,7 @@ exports[`SubsidyRequestManagementTable renders data in a table as expected 1`] =
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
new file mode 100644
index 0000000000..92826cb93f
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { Hyperlink } from '@edx/paragon';
+
+import { configuration } from '../../config';
+import EmailAddressTableCell from './EmailAddressTableCell';
+
+const AssignmentDetailsTableCell = ({ row, enterpriseSlug }) => {
+ const { ENTERPRISE_LEARNER_PORTAL_URL } = configuration;
+ return (
+ <>
+
+
+
+ View course
+
+
+ >
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+AssignmentDetailsTableCell.propTypes = {
+ row: PropTypes.shape({
+ original: PropTypes.shape({
+ uuid: PropTypes.string.isRequired,
+ learnerEmail: PropTypes.string.isRequired,
+ contentKey: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+ enterpriseSlug: PropTypes.string,
+};
+
+export default connect(mapStateToProps)(AssignmentDetailsTableCell);
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
new file mode 100644
index 0000000000..5b2b94b5b0
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { DataTable } from '@edx/paragon';
+
+import TableTextFilter from './TableTextFilter';
+import CustomDataTableEmptyState from './CustomDataTableEmptyState';
+import AssignmentDetailsTableCell from './AssignmentDetailsTableCell';
+import { DEFAULT_PAGE, PAGE_SIZE, formatPrice } from './data';
+
+const FilterStatus = (rest) =>
;
+
+const BudgetAssignmentsTable = ({
+ isLoading,
+ tableData,
+ fetchTableData,
+}) => (
+
`-${formatPrice(row.original.contentQuantity / 100, { maximumFractionDigits: 0 })}`,
+ disableFilters: true,
+ },
+ ]}
+ initialTableOptions={{
+ getRowId: row => row?.uuid?.toString(),
+ }}
+ initialState={{
+ pageSize: PAGE_SIZE,
+ pageIndex: DEFAULT_PAGE,
+ sortBy: [],
+ filters: [],
+ }}
+ fetchData={fetchTableData}
+ data={tableData.results}
+ itemCount={tableData.count}
+ pageCount={tableData.numPages}
+ EmptyTableComponent={CustomDataTableEmptyState}
+ />
+);
+
+BudgetAssignmentsTable.propTypes = {
+ isLoading: PropTypes.bool.isRequired,
+ tableData: PropTypes.shape().isRequired,
+ fetchTableData: PropTypes.func.isRequired,
+};
+
+export default BudgetAssignmentsTable;
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index a593012a63..28a152fce7 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -1,47 +1,70 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import { useParams } from 'react-router-dom';
+import { Stack } from '@edx/paragon';
-import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import { useOfferRedemptions, isUUID } from './data';
+import BudgetDetailRedemptions from './BudgetDetailRedemptions';
+import BudgetDetailAssignments from './BudgetDetailAssignments';
+import {
+ useOfferRedemptions,
+ useBudgetContentAssignments,
+ useBudgetId,
+ useSubsidyAccessPolicy,
+} from './data';
const BudgetDetailActivityTabContents = ({
enterpriseUUID,
- enterpriseSlug,
- enableLearnerPortal,
+ enterpriseFeatures,
}) => {
- const { budgetId } = useParams();
- const enterpriseOfferId = isUUID(budgetId) ? null : budgetId;
- const subsidyAccessPolicyId = isUUID(budgetId) ? budgetId : null;
+ const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
+ const {
+ data: subsidyAccessPolicy,
+ } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
+ const isTopDownAssignmentEnabled = enterpriseFeatures?.topDownAssignmentRealTimeLcm;
+
const {
isLoading: isLoadingOfferRedemptions,
offerRedemptions,
fetchOfferRedemptions,
} = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
+ const {
+ isLoading: isLoadingContentAssignments,
+ contentAssignments,
+ fetchContentAssignments,
+ } = useBudgetContentAssignments({
+ assignmentConfigurationUUID: subsidyAccessPolicy?.assignmentConfiguration?.uuid,
+ isEnabled: subsidyAccessPolicy?.isAssignable && isTopDownAssignmentEnabled,
+ });
+
return (
-
+
+
+
+
);
};
const mapStateToProps = state => ({
enterpriseUUID: state.portalConfiguration.enterpriseId,
- enterpriseSlug: state.portalConfiguration.enterpriseSlug,
- enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
});
BudgetDetailActivityTabContents.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
- enterpriseSlug: PropTypes.string.isRequired,
- enableLearnerPortal: PropTypes.bool.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }),
};
export default connect(mapStateToProps)(BudgetDetailActivityTabContents);
diff --git a/src/components/learner-credit-management/BudgetDetailAssignments.jsx b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
new file mode 100644
index 0000000000..f4ba6eb8f2
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import BudgetAssignmentsTable from './BudgetAssignmentsTable';
+
+const BudgetDetailAssignments = ({
+ isEnabled,
+ isLoading,
+ tableData,
+ fetchTableData,
+}) => {
+ if (!isEnabled) {
+ return null;
+ }
+
+ return (
+
+ Assigned
+
+ Assigned activity earmarks funds in your budget so you can't overspend. For funds to move
+ from assigned to spent, your learners must complete enrollment.
+
+
+
+ );
+};
+
+BudgetDetailAssignments.propTypes = {
+ isEnabled: PropTypes.bool.isRequired,
+ isLoading: PropTypes.bool.isRequired,
+ tableData: PropTypes.shape().isRequired,
+ fetchTableData: PropTypes.func.isRequired,
+};
+
+export default BudgetDetailAssignments;
diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx
index 29b83b4058..1ad1ff56cc 100644
--- a/src/components/learner-credit-management/BudgetDetailPage.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPage.jsx
@@ -1,61 +1,38 @@
-import React, { useContext } from 'react';
-import PropTypes from 'prop-types';
-import {
- Row,
- Col,
- Breadcrumb,
- Container,
-} from '@edx/paragon';
-import { connect } from 'react-redux';
-import { Helmet } from 'react-helmet';
-import { Link } from 'react-router-dom';
-import Hero from '../Hero';
+import React from 'react';
+import { Skeleton, Stack } from '@edx/paragon';
-import LoadingMessage from '../LoadingMessage';
-import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
-import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import { useBudgetId, useSubsidyAccessPolicy } from './data';
import BudgetDetailTabsAndRoutes from './BudgetDetailTabsAndRoutes';
+import BudgetDetailPageWrapper from './BudgetDetailPageWrapper';
+import BudgetDetailPageHeader from './BudgetDetailPageHeader';
-const PAGE_TITLE = 'Learner Credit Management';
+const BudgetDetailPage = () => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const {
+ isInitialLoading: isInitialLoadingSubsidyAccessPolicy,
+ data: subsidyAccessPolicy,
+ } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
-const BudgetDetailPage = ({ enterpriseSlug }) => {
- const { isLoading } = useContext(EnterpriseSubsidiesContext);
- if (isLoading) {
- return ;
+ if (isInitialLoadingSubsidyAccessPolicy) {
+ return (
+
+
+
+
+
+ loading budget details
+
+ );
}
- const links = [
- {
- label: 'Budgets',
- to: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}`,
- },
- ];
+
return (
- <>
-
-
-
-
-
-
-
-
+
+
+
-
- >
+
+
);
};
-const mapStateToProps = state => ({
- enterpriseSlug: state.portalConfiguration.enterpriseSlug,
-});
-
-BudgetDetailPage.propTypes = {
- enterpriseSlug: PropTypes.string.isRequired,
-};
-
-export default connect(mapStateToProps)(BudgetDetailPage);
+export default BudgetDetailPage;
diff --git a/src/components/learner-credit-management/BudgetDetailPageHeader.jsx b/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
new file mode 100644
index 0000000000..ad84c6724e
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { Link } from 'react-router-dom';
+import {
+ Row, Col, Breadcrumb, Stack,
+} from '@edx/paragon';
+
+import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import { useBudgetId, useSubsidyAccessPolicy } from './data';
+
+const BudgetDetailPageHeader = ({ enterpriseSlug }) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
+ return (
+
+
+
+
+
+
+ {budgetDisplayName && (
+
+
+ {budgetDisplayName}
+
+
+ )}
+
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+BudgetDetailPageHeader.propTypes = {
+ enterpriseSlug: PropTypes.string.isRequired,
+};
+
+export default connect(mapStateToProps)(BudgetDetailPageHeader);
diff --git a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
new file mode 100644
index 0000000000..1651094dd4
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Helmet } from 'react-helmet';
+import { Container } from '@edx/paragon';
+
+import Hero from '../Hero';
+
+const PAGE_TITLE = 'Learner Credit Management';
+
+const BudgetDetailPageWrapper = ({ subsidyAccessPolicy, children }) => {
+ // display name is an optional field, and may not be set for all budgets so fallback to "Overview"
+ // similar to the display name logic for budgets on the overview page route.
+ const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
+ const helmetPageTitle = budgetDisplayName ? `${budgetDisplayName} - ${PAGE_TITLE}` : PAGE_TITLE;
+ return (
+ <>
+
+
+
+ {children}
+
+ >
+ );
+};
+
+BudgetDetailPageWrapper.propTypes = {
+ children: PropTypes.node.isRequired,
+ subsidyAccessPolicy: PropTypes.shape(),
+};
+
+export default BudgetDetailPageWrapper;
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
new file mode 100644
index 0000000000..bccf80aa6c
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+
+import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
+
+const BudgetDetailRedemptions = ({
+ isLoading,
+ offerRedemptions,
+ fetchOfferRedemptions,
+ enterpriseUUID,
+ enterpriseSlug,
+ enableLearnerPortal,
+}) => (
+
+ Spent
+
+ Spent activity is driven by completed enrollments. Enrollment data is automatically updated every 12 hours.
+ Come back later to view more recent enrollments.
+
+
+
+);
+
+const mapStateToProps = state => ({
+ enterpriseUUID: state.portalConfiguration.enterpriseId,
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
+});
+
+BudgetDetailRedemptions.propTypes = {
+ enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseSlug: PropTypes.string.isRequired,
+ enableLearnerPortal: PropTypes.bool.isRequired,
+ isLoading: PropTypes.bool.isRequired,
+ offerRedemptions: PropTypes.shape({
+ results: PropTypes.arrayOf(PropTypes.shape({
+ userEmail: PropTypes.string,
+ courseTitle: PropTypes.string.isRequired,
+ courseListPrice: PropTypes.number.isRequired,
+ enrollmentDate: PropTypes.string.isRequired,
+ courseProductLine: PropTypes.string.isRequired,
+ })),
+ itemCount: PropTypes.number.isRequired,
+ pageCount: PropTypes.number.isRequired,
+ }).isRequired,
+ fetchOfferRedemptions: PropTypes.func.isRequired,
+};
+
+export default connect(mapStateToProps)(BudgetDetailRedemptions);
diff --git a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
index 9932cd4cdc..1d0ef0db7f 100644
--- a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
+++ b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
@@ -9,7 +9,7 @@ import {
BUDGET_DETAIL_ACTIVITY_TAB,
BUDGET_DETAIL_CATALOG_TAB,
} from './data/constants';
-import { useBudgetDetailTabs } from './data';
+import { useBudgetDetailTabs, useBudgetId, useSubsidyAccessPolicy } from './data';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
import NotFoundPage from '../NotFoundPage';
import EVENT_NAMES from '../../eventTracking';
@@ -18,17 +18,18 @@ import BudgetDetailCatalogTabContents from './BudgetDetailCatalogTabContents';
const DEFAULT_TAB = BUDGET_DETAIL_ACTIVITY_TAB;
-function isSupportedTabKey({ tabKey, enterpriseFeatures }) {
+function isSupportedTabKey({ tabKey, isBudgetAssignable, enterpriseFeatures }) {
const supportedTabs = [BUDGET_DETAIL_ACTIVITY_TAB];
- if (enterpriseFeatures.topDownAssignmentRealTimeLcm) {
+ if (enterpriseFeatures.topDownAssignmentRealTimeLcm && isBudgetAssignable) {
supportedTabs.push(BUDGET_DETAIL_CATALOG_TAB);
}
return supportedTabs.includes(tabKey);
}
-function getInitialTabKey(routeActiveTabKey, { enterpriseFeatures }) {
+function getInitialTabKey(routeActiveTabKey, { isBudgetAssignable, enterpriseFeatures }) {
const isValidTabKey = isSupportedTabKey({
tabKey: routeActiveTabKey,
+ isBudgetAssignable,
enterpriseFeatures,
});
if (!isValidTabKey) {
@@ -42,16 +43,27 @@ const BudgetDetailTabsAndRoutes = ({
enterpriseSlug,
enterpriseFeatures,
}) => {
+ const { activeTabKey: routeActiveTabKey } = useParams();
+ const { budgetId, subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const isBudgetAssignable = !!subsidyAccessPolicy?.isAssignable;
+
const history = useHistory();
- const { budgetId, activeTabKey: routeActiveTabKey } = useParams();
const [activeTabKey, setActiveTabKey] = useState(getInitialTabKey(
routeActiveTabKey,
- { enterpriseFeatures },
+ { enterpriseFeatures, isBudgetAssignable },
));
+ /**
+ * Ensure the active tab in the UI reflects the active tab in the URL.
+ */
useEffect(() => {
- setActiveTabKey(getInitialTabKey(routeActiveTabKey, { enterpriseFeatures }));
- }, [routeActiveTabKey, enterpriseFeatures]);
+ const initialTabKey = getInitialTabKey(
+ routeActiveTabKey,
+ { enterpriseFeatures, isBudgetAssignable },
+ );
+ setActiveTabKey(initialTabKey);
+ }, [routeActiveTabKey, enterpriseFeatures, isBudgetAssignable]);
const handleTabSelect = (nextActiveTabKey) => {
setActiveTabKey(nextActiveTabKey);
@@ -66,6 +78,7 @@ const BudgetDetailTabsAndRoutes = ({
const tabs = useBudgetDetailTabs({
activeTabKey,
+ isBudgetAssignable,
enterpriseFeatures,
ActivityTabElement: BudgetDetailActivityTabContents,
CatalogTabElement: BudgetDetailCatalogTabContents,
@@ -73,6 +86,7 @@ const BudgetDetailTabsAndRoutes = ({
if (!isSupportedTabKey({
tabKey: routeActiveTabKey || activeTabKey,
+ isBudgetAssignable,
enterpriseFeatures,
})) {
return ;
diff --git a/src/components/learner-credit-management/SpendTableEmptyState.jsx b/src/components/learner-credit-management/CustomDataTableEmptyState.jsx
similarity index 75%
rename from src/components/learner-credit-management/SpendTableEmptyState.jsx
rename to src/components/learner-credit-management/CustomDataTableEmptyState.jsx
index 20f5c9165a..962f68f383 100644
--- a/src/components/learner-credit-management/SpendTableEmptyState.jsx
+++ b/src/components/learner-credit-management/CustomDataTableEmptyState.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { DataTable, DataTableContext } from '@edx/paragon';
-const SpendTableEmptyState = () => {
+const CustomDataTableEmptyState = () => {
const { isLoading } = useContext(DataTableContext);
if (isLoading) {
return null;
@@ -9,4 +9,4 @@ const SpendTableEmptyState = () => {
return ;
};
-export default SpendTableEmptyState;
+export default CustomDataTableEmptyState;
diff --git a/src/components/learner-credit-management/EmailAddressTableCell.jsx b/src/components/learner-credit-management/EmailAddressTableCell.jsx
index a85d1094c8..d635932422 100644
--- a/src/components/learner-credit-management/EmailAddressTableCell.jsx
+++ b/src/components/learner-credit-management/EmailAddressTableCell.jsx
@@ -7,15 +7,51 @@ import {
import { InfoOutline } from '@edx/paragon/icons';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-const EmailAddressTableCell = ({ row, enterpriseUUID }) => {
- if (row.original.userEmail) {
- return {row.original.userEmail} ;
+/**
+ * Conditionally renders either the email address, when available, or a "Email hidden" message,
+ * including a popover that includes additional messaging about why the email is hidden.
+ *
+ * In the case an email is hidden, includes Segment events for when the popover is opened and closed. Note
+ * the row identifier passed as event metadata may be different depending on which table this component is
+ * used in and what data source is backing it, i.e.:
+ * - "Spent" table backed by analytics API uses the `enterpriseEnrollmentId`
+ * - "Spent" table backed by transactions API uses the `fulfillmentUUID`.
+ * - "Assigned" table uses the `ContentAssignment` UUID
+ *
+ * @param {string} tableId Unique identifier for the table this component is used in. Used to namespace
+ * the Segment events.
+ * @param {string} userEmail The email address to render, if available.
+ * @param {number} [enterpriseEnrollmentId] The enterprise enrollment ID, used by "Spent" table backed by analytics API.
+ * @param {string} [fulfillmentIdentifier] The UUID of a `LearnerCreditEnterpriseCourseEnrollment`, used by "Spent"
+ * table backed by transactions API.
+ * @param {string} [contentAssignmentUUID] The UUID of a content assignment, used by "Assigned" table.
+ * @param {string} enterpriseUUID The UUID of the enterprise, used for Segment events.
+ *
+ * @returns A React component that renders either an email address or a "Email hidden" message.
+ */
+const EmailAddressTableCell = ({
+ tableId,
+ userEmail,
+ enterpriseEnrollmentId,
+ contentAssignmentUUID,
+ fulfillmentIdentifier,
+ enterpriseUUID,
+}) => {
+ if (userEmail) {
+ return (
+
+ {userEmail}
+
+ );
}
return (
-
- Email hidden
+
+ Email hidden
@@ -28,19 +64,15 @@ const EmailAddressTableCell = ({ row, enterpriseUUID }) => {
onEntered={() => {
sendEnterpriseTrackEvent(
enterpriseUUID,
- 'edx.ui.enterprise.admin_portal.learner-credit-management.table.email-hidden-popover.opened',
- {
- enterpriseEnrollmentId: row.original.enterpriseEnrollmentId,
- },
+ `edx.ui.enterprise.admin_portal.learner-credit-management.${tableId}.email-hidden-popover.opened`,
+ { enterpriseEnrollmentId, fulfillmentIdentifier, contentAssignmentUUID },
);
}}
onExited={() => {
sendEnterpriseTrackEvent(
enterpriseUUID,
- 'edx.ui.enterprise.admin_portal.learner-credit-management.table.email-hidden-popover.dismissed',
- {
- enterpriseEnrollmentId: row.original.enterpriseEnrollmentId,
- },
+ `edx.ui.enterprise.admin_portal.learner-credit-management.${tableId}.email-hidden-popover.dismissed`,
+ { enterpriseEnrollmentId, fulfillmentIdentifier, contentAssignmentUUID },
);
}}
>
@@ -57,12 +89,11 @@ const EmailAddressTableCell = ({ row, enterpriseUUID }) => {
};
EmailAddressTableCell.propTypes = {
- row: PropTypes.shape({
- original: PropTypes.shape({
- userEmail: PropTypes.string,
- enterpriseEnrollmentId: PropTypes.number,
- }),
- }).isRequired,
+ tableId: PropTypes.string.isRequired,
+ userEmail: PropTypes.string, // may be undefined/null if email is hidden
+ enterpriseEnrollmentId: PropTypes.number,
+ contentAssignmentUUID: PropTypes.string,
+ fulfillmentIdentifier: PropTypes.string,
enterpriseUUID: PropTypes.string.isRequired,
};
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index b74e51cd25..e5f1309490 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -2,13 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import { DataTable } from '@edx/paragon';
+
import TableTextFilter from './TableTextFilter';
-import SpendTableEmptyState from './SpendTableEmptyState';
+import CustomDataTableEmptyState from './CustomDataTableEmptyState';
import SpendTableEnrollmentDetails from './SpendTableEnrollmentDetails';
import { getCourseProductLineText } from '../../utils';
-
-export const PAGE_SIZE = 20;
-export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
+import { PAGE_SIZE, DEFAULT_PAGE } from './data';
const FilterStatus = (rest) => ;
@@ -16,72 +15,61 @@ const LearnerCreditAllocationTable = ({
isLoading,
tableData,
fetchTableData,
-}) => {
- const defaultFilter = [];
- return (
- <>
- Spent
-
- Spent activity is driven by completed enrollments. Enrollment data is automatically updated every 12 hours.
- Come back later to view more recent enrollments.
-
- dayjs(row.values.enrollmentDate).format('MMM D, YYYY'),
- disableFilters: true,
- },
- {
- Header: 'Enrollment details',
- accessor: 'enrollmentDetails',
- Cell: SpendTableEnrollmentDetails,
- disableFilters: false,
- disableSortBy: true,
- },
- {
- Header: 'Amount',
- accessor: 'courseListPrice',
- Cell: ({ row }) => `$${row.values.courseListPrice}`,
- disableFilters: true,
- },
- {
- Header: 'Product',
- accessor: 'courseProductLine',
- Cell: ({ row }) => getCourseProductLineText(row.values.courseProductLine),
- disableFilters: true,
- },
- ]}
- initialTableOptions={{
- getRowId: row => row?.uuid?.toString(),
- }}
- initialState={{
- pageSize: PAGE_SIZE,
- pageIndex: DEFAULT_PAGE,
- sortBy: [
- { id: 'enrollmentDate', desc: true },
- ],
- filters: defaultFilter,
- }}
- fetchData={fetchTableData}
- data={tableData.results}
- itemCount={tableData.itemCount}
- pageCount={tableData.pageCount}
- EmptyTableComponent={SpendTableEmptyState}
- />
- >
- );
-};
+}) => (
+ dayjs(row.values.enrollmentDate).format('MMM D, YYYY'),
+ disableFilters: true,
+ },
+ {
+ Header: 'Enrollment details',
+ accessor: 'enrollmentDetails',
+ Cell: SpendTableEnrollmentDetails,
+ disableSortBy: true,
+ },
+ {
+ Header: 'Amount',
+ accessor: 'courseListPrice',
+ Cell: ({ row }) => `$${row.values.courseListPrice}`,
+ disableFilters: true,
+ },
+ {
+ Header: 'Product',
+ accessor: 'courseProductLine',
+ Cell: ({ row }) => getCourseProductLineText(row.values.courseProductLine),
+ disableFilters: true,
+ },
+ ]}
+ initialTableOptions={{
+ getRowId: row => row?.uuid?.toString(),
+ }}
+ initialState={{
+ pageSize: PAGE_SIZE,
+ pageIndex: DEFAULT_PAGE,
+ sortBy: [
+ { id: 'enrollmentDate', desc: true },
+ ],
+ filters: [],
+ }}
+ fetchData={fetchTableData}
+ data={tableData.results}
+ itemCount={tableData.itemCount}
+ pageCount={tableData.pageCount}
+ EmptyTableComponent={CustomDataTableEmptyState}
+ />
+);
LearnerCreditAllocationTable.propTypes = {
isLoading: PropTypes.bool.isRequired,
diff --git a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
index 227f917290..dafd44b88f 100644
--- a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
+++ b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
@@ -12,17 +12,24 @@ const SpendTableEnrollmentDetailsContents = ({
enterpriseSlug,
}) => (
<>
-
+
{enableLearnerPortal ? (
{row.original.courseTitle}
) : (
- {row.original.courseTitle}
+ {row.original.courseTitle}
)}
>
@@ -32,6 +39,9 @@ const rowPropType = PropTypes.shape({
original: PropTypes.shape({
courseKey: PropTypes.string.isRequired,
courseTitle: PropTypes.string.isRequired,
+ userEmail: PropTypes.string.isRequired,
+ enterpriseEnrollmentId: PropTypes.number,
+ fulfillmentIdentifier: PropTypes.string,
}).isRequired,
}).isRequired;
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 37f453bd4a..8db03bb0ce 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -32,5 +32,8 @@ export const LANGUAGE_REFINEMENT = 'language';
// Learning types
export const CONTENT_TYPE_COURSE = 'course';
export const EXEC_ED_TITLE = 'Executive Education';
-
export const EXEC_COURSE_TYPE = 'executive-education-2u';
+
+// Number of items to display per page in Budget Detail assignment/spend tables
+export const PAGE_SIZE = 25;
+export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
diff --git a/src/components/learner-credit-management/data/hooks/hooks.js b/src/components/learner-credit-management/data/hooks/hooks.js
deleted file mode 100644
index 306ad58fde..0000000000
--- a/src/components/learner-credit-management/data/hooks/hooks.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { useMemo, useState } from 'react';
-
-import { CONTENT_TYPE_COURSE } from '../constants';
-
-// eslint-disable-next-line import/prefer-default-export
-export const useSelectedCourse = () => {
- const [course, setCourse] = useState(null);
- const isCourse = useMemo(
- () => course?.contentType === CONTENT_TYPE_COURSE,
- [course],
- );
- return [course, setCourse, isCourse];
-};
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index c3bebd2b82..3b21c9be3f 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -1,3 +1,6 @@
export { default as useBudgetDetailTabs } from './useBudgetDetailTabs';
export { default as useOfferSummary } from './useOfferSummary';
export { default as useOfferRedemptions } from './useOfferRedemptions';
+export { default as useBudgetContentAssignments } from './useBudgetContentAssignments';
+export { default as useBudgetId } from './useBudgetId';
+export { default as useSubsidyAccessPolicy } from './useSubsidyAccessPolicy';
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
new file mode 100644
index 0000000000..45031d520e
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
@@ -0,0 +1,65 @@
+import { useCallback, useMemo, useState } from 'react';
+import debounce from 'lodash.debounce';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+
+const initialContentAssignmentsState = {
+ results: [],
+ count: 0,
+ numPages: 0,
+ currentPage: 1,
+};
+
+const applyFiltersToOptions = (filters, options) => {
+ if (!filters || filters.length === 0) {
+ return;
+ }
+ const searchQuery = filters.find(filter => filter.id === 'assignmentDetails')?.value;
+ if (searchQuery) {
+ Object.assign(options, { search: searchQuery });
+ }
+};
+
+const useBudgetContentAssignments = ({
+ assignmentConfigurationUUID,
+ isEnabled,
+}) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [contentAssignments, setContentAssignments] = useState(initialContentAssignmentsState);
+
+ const fetchContentAssignments = useCallback((args) => {
+ if (!isEnabled || !assignmentConfigurationUUID) {
+ setIsLoading(false);
+ return;
+ }
+ const getContentAssignments = async () => {
+ setIsLoading(true);
+ const options = {
+ page: args.pageIndex + 1, // `DataTable` uses zeo-indexed array
+ pageSize: args.pageSize,
+ };
+ applyFiltersToOptions(args.filters, options);
+ const assignmentsResponse = await EnterpriseAccessApiService.listContentAssignments(
+ assignmentConfigurationUUID,
+ options,
+ );
+ setContentAssignments(camelCaseObject(assignmentsResponse.data));
+ setIsLoading(false);
+ };
+ getContentAssignments();
+ }, [isEnabled, assignmentConfigurationUUID]);
+
+ const debouncedFetchContentAssigments = useMemo(
+ () => debounce(fetchContentAssignments, 300),
+ [fetchContentAssignments],
+ );
+
+ return {
+ isLoading,
+ contentAssignments,
+ fetchContentAssignments: debouncedFetchContentAssigments,
+ };
+};
+
+export default useBudgetContentAssignments;
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
new file mode 100644
index 0000000000..0ede9195bd
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
@@ -0,0 +1,131 @@
+import { renderHook } from '@testing-library/react-hooks';
+
+import useBudgetContentAssignments from './useBudgetContentAssignments'; // Import the hook
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+
+describe('useBudgetContentAssignments', () => {
+ it('does not call fetchContentAssignments if isEnabled is false', async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
+ assignmentConfigurationUUID: '123',
+ isEnabled: false,
+ }));
+ const { fetchContentAssignments } = result.current;
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockListContentAssignments.mockResolvedValue({
+ data: {
+ results: [],
+ count: 0,
+ numPages: 0,
+ currentPage: 1,
+ },
+ });
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ filters: [],
+ });
+
+ await waitForNextUpdate();
+
+ expect(mockListContentAssignments).not.toHaveBeenCalled();
+ });
+
+ it('should return the correct data', async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
+ assignmentConfigurationUUID: '123',
+ isEnabled: true,
+ }));
+ const { fetchContentAssignments } = result.current;
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockListContentAssignments.mockResolvedValue({
+ data: {
+ results: [
+ {
+ id: 1,
+ name: 'test',
+ },
+ ],
+ count: 1,
+ numPages: 1,
+ currentPage: 1,
+ },
+ });
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ filters: [],
+ });
+
+ await waitForNextUpdate();
+
+ expect(result.current.isLoading).toEqual(false);
+ expect(result.current.contentAssignments).toEqual({
+ results: [
+ {
+ id: 1,
+ name: 'test',
+ },
+ ],
+ count: 1,
+ numPages: 1,
+ currentPage: 1,
+ });
+ });
+
+ it.each([
+ {
+ filters: [
+ {
+ id: 'assignmentDetails',
+ value: 'test',
+ },
+ ],
+ hasSearchParam: true,
+ },
+ {
+ filters: [
+ {
+ id: 'other',
+ value: 'test',
+ },
+ ],
+ hasSearchParam: false,
+ },
+ ])('handles assignment details filter with search query parameter (%s)', async ({ filters, hasSearchParam }) => {
+ const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
+ assignmentConfigurationUUID: '123',
+ isEnabled: true,
+ }));
+ const { fetchContentAssignments } = result.current;
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockListContentAssignments.mockResolvedValue({
+ data: {
+ results: [
+ {
+ id: 1,
+ name: 'test',
+ },
+ ],
+ count: 1,
+ numPages: 1,
+ currentPage: 1,
+ },
+ });
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ filters,
+ });
+
+ await waitForNextUpdate();
+
+ expect(mockListContentAssignments).toHaveBeenCalledWith(
+ '123',
+ {
+ page: 1,
+ pageSize: 10,
+ search: hasSearchParam ? 'test' : undefined,
+ },
+ );
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx b/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx
index af8186a7b1..cb33ea6384 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailTabs.jsx
@@ -11,6 +11,7 @@ const TAB_CLASS_NAME = 'pt-4.5';
export const useBudgetDetailTabs = ({
activeTabKey,
+ isBudgetAssignable,
enterpriseFeatures,
ActivityTabElement,
CatalogTabElement,
@@ -29,7 +30,7 @@ export const useBudgetDetailTabs = ({
)}
,
);
- if (enterpriseFeatures.topDownAssignmentRealTimeLcm) {
+ if (enterpriseFeatures.topDownAssignmentRealTimeLcm && isBudgetAssignable) {
tabsArray.push(
{
+ const { budgetId } = useParams();
+ const enterpriseOfferId = isUUID(budgetId) ? null : budgetId;
+ const subsidyAccessPolicyId = isUUID(budgetId) ? budgetId : null;
+ return {
+ budgetId,
+ enterpriseOfferId,
+ subsidyAccessPolicyId,
+ };
+};
+
+export default useBudgetId;
diff --git a/src/components/learner-credit-management/data/tests/hooks.test.js b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js
similarity index 60%
rename from src/components/learner-credit-management/data/tests/hooks.test.js
rename to src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js
index 38ebaeaafd..cfe7affd2d 100644
--- a/src/components/learner-credit-management/data/tests/hooks.test.js
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js
@@ -1,34 +1,12 @@
import { act, renderHook } from '@testing-library/react-hooks/dom';
import { camelCaseObject } from '@edx/frontend-platform/utils';
-import {
- useOfferSummary,
- useOfferRedemptions,
-} from '../hooks';
+import useOfferRedemptions from './useOfferRedemptions';
import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
-jest.mock('@edx/frontend-platform/config', () => ({
- getConfig: jest.fn(() => ({
- FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
- })),
-}));
-jest.mock('../../../../data/services/EnterpriseDataApiService');
-
const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
const TEST_ENTERPRISE_OFFER_ID = 1;
-const mockOfferSummary = {
- offer_id: TEST_ENTERPRISE_OFFER_ID,
- status: 'Open',
- enterprise_customer_uuid: TEST_ENTERPRISE_UUID,
- amount_of_offer_spent: 200.00,
- max_discount: 5000.00,
- percent_of_offer_spent: 0.04,
- remaining_balance: 4800.00,
-};
-const mockEnterpriseOffer = {
- id: TEST_ENTERPRISE_OFFER_ID,
-};
const mockOfferEnrollments = [{
user_email: 'edx@example.com',
course_title: 'Test Course Title',
@@ -43,45 +21,11 @@ const mockOfferEnrollmentsResponse = {
results: mockOfferEnrollments,
};
-describe('useOfferSummary', () => {
- it('should handle null enterprise offer', async () => {
- const { result } = renderHook(() => useOfferSummary(TEST_ENTERPRISE_UUID));
-
- expect(result.current).toEqual({
- offerSummary: undefined,
- isLoading: false,
- });
- });
-
- it('should fetch summary data for enterprise offer', async () => {
- EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockResolvedValueOnce({ data: mockOfferSummary });
- const { result, waitForNextUpdate } = renderHook(() => useOfferSummary(TEST_ENTERPRISE_UUID, mockEnterpriseOffer));
-
- expect(result.current).toEqual({
- offerSummary: undefined,
- isLoading: true,
- });
-
- await waitForNextUpdate();
+const mockEnterpriseOffer = {
+ id: TEST_ENTERPRISE_OFFER_ID,
+};
- expect(EnterpriseDataApiService.fetchEnterpriseOfferSummary).toHaveBeenCalled();
- const expectedResult = {
- totalFunds: 5000,
- redeemedFunds: 200,
- redeemedFundsExecEd: NaN,
- redeemedFundsOcm: NaN,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerId: 1,
- budgetsSummary: [],
- offerType: undefined,
- };
- expect(result.current).toEqual({
- offerSummary: expectedResult,
- isLoading: false,
- });
- });
-});
+jest.mock('../../../../data/services/EnterpriseDataApiService');
describe('useOfferRedemptions', () => {
it('should fetch enrollment/redemptions metadata for enterprise offer', async () => {
diff --git a/src/components/learner-credit-management/data/hooks/useOfferSummary.test.js b/src/components/learner-credit-management/data/hooks/useOfferSummary.test.js
new file mode 100644
index 0000000000..352b104f6b
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useOfferSummary.test.js
@@ -0,0 +1,67 @@
+import { renderHook } from '@testing-library/react-hooks/dom';
+
+import useOfferSummary from './useOfferSummary';
+import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+
+jest.mock('@edx/frontend-platform/config', () => ({
+ getConfig: jest.fn(() => ({
+ FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
+ })),
+}));
+jest.mock('../../../../data/services/EnterpriseDataApiService');
+
+const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
+const TEST_ENTERPRISE_OFFER_ID = 1;
+
+const mockOfferSummary = {
+ offer_id: TEST_ENTERPRISE_OFFER_ID,
+ status: 'Open',
+ enterprise_customer_uuid: TEST_ENTERPRISE_UUID,
+ amount_of_offer_spent: 200.00,
+ max_discount: 5000.00,
+ percent_of_offer_spent: 0.04,
+ remaining_balance: 4800.00,
+};
+const mockEnterpriseOffer = {
+ id: TEST_ENTERPRISE_OFFER_ID,
+};
+
+describe('useOfferSummary', () => {
+ it('should handle null enterprise offer', async () => {
+ const { result } = renderHook(() => useOfferSummary(TEST_ENTERPRISE_UUID));
+
+ expect(result.current).toEqual({
+ offerSummary: undefined,
+ isLoading: false,
+ });
+ });
+
+ it('should fetch summary data for enterprise offer', async () => {
+ EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockResolvedValueOnce({ data: mockOfferSummary });
+ const { result, waitForNextUpdate } = renderHook(() => useOfferSummary(TEST_ENTERPRISE_UUID, mockEnterpriseOffer));
+
+ expect(result.current).toEqual({
+ offerSummary: undefined,
+ isLoading: true,
+ });
+
+ await waitForNextUpdate();
+
+ expect(EnterpriseDataApiService.fetchEnterpriseOfferSummary).toHaveBeenCalled();
+ const expectedResult = {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ redeemedFundsExecEd: NaN,
+ redeemedFundsOcm: NaN,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerId: 1,
+ budgetsSummary: [],
+ offerType: undefined,
+ };
+ expect(result.current).toEqual({
+ offerSummary: expectedResult,
+ isLoading: false,
+ });
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
new file mode 100644
index 0000000000..c36d72ffa0
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
@@ -0,0 +1,32 @@
+import { useQuery } from '@tanstack/react-query';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+
+const determineBudgetAssignability = (policyType) => {
+ const assignableSubsidyAccessPolicyTypes = ['AssignedLearnerCreditAccessPolicy'];
+ return assignableSubsidyAccessPolicyTypes.includes(policyType);
+};
+
+/**
+ * Retrieves a subsidy access policy by UUID from the API.
+ *
+ * @param {*} queryKey The queryKey from the associated `useQuery` call.
+ * @returns The subsidy access policy object, with the `isAssignable` property added.
+ */
+const getSubsidyAccessPolicy = async ({ queryKey }) => {
+ const subsidyAccessPolicyUUID = queryKey[2];
+ const response = await EnterpriseAccessApiService.retrieveSubsidyAccessPolicy(subsidyAccessPolicyUUID);
+ const subsidyAccessPolicy = camelCaseObject(response.data);
+ subsidyAccessPolicy.isAssignable = determineBudgetAssignability(subsidyAccessPolicy.policyType);
+ return subsidyAccessPolicy;
+};
+
+const useSubsidyAccessPolicy = (subsidyAccessPolicyId, { queryOptions } = {}) => useQuery({
+ queryKey: ['learner-credit-management', 'subsidy-access-policy', subsidyAccessPolicyId],
+ queryFn: getSubsidyAccessPolicy,
+ enabled: !!subsidyAccessPolicyId,
+ ...queryOptions,
+});
+
+export default useSubsidyAccessPolicy;
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
new file mode 100644
index 0000000000..4d67e12051
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
@@ -0,0 +1,121 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { renderHook } from '@testing-library/react-hooks';
+
+import useSubsidyAccessPolicy from './useSubsidyAccessPolicy'; // Import the hook
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+
+const mockSubsidyAccessPolicyUUID = '9af340a9-48de-4d94-976d-e2282b9eb7f3';
+
+// Mock the EnterpriseAccessApiService
+jest.mock('../../../../data/services/EnterpriseAccessApiService', () => ({
+ retrieveSubsidyAccessPolicy: jest.fn().mockResolvedValue({
+ data: {
+ uuid: '9af340a9-48de-4d94-976d-e2282b9eb7f3',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ // Other properties...
+ },
+ }),
+}));
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+});
+
+const wrapper = ({ children }) => (
+ {children}
+);
+
+describe('useSubsidyAccessPolicy', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it.each([
+ { isAssignable: true },
+ { isAssignable: false },
+ ])('should fetch and return subsidy access policy (%s)', async ({ isAssignable }) => {
+ // Mock the policy type in response based on isAssignable
+ jest.spyOn(EnterpriseAccessApiService, 'retrieveSubsidyAccessPolicy').mockResolvedValueOnce({
+ data: {
+ uuid: mockSubsidyAccessPolicyUUID,
+ policyType: isAssignable ? 'AssignedLearnerCreditAccessPolicy' : 'PerLearnerCreditSpendLimitAccessPolicy',
+ // Other properties...
+ },
+ });
+ const { result, waitForNextUpdate } = renderHook(
+ () => useSubsidyAccessPolicy(mockSubsidyAccessPolicyUUID),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.isLoading).toBe(false);
+ expect(result.current.isError).toBe(false);
+ expect(result.current.data).toEqual({
+ uuid: mockSubsidyAccessPolicyUUID,
+ policyType: isAssignable ? 'AssignedLearnerCreditAccessPolicy' : 'PerLearnerCreditSpendLimitAccessPolicy',
+ isAssignable,
+ // Other expected properties...
+ });
+ });
+
+ it('should handle errors gracefully', async () => {
+ // Mock an error response from the API
+ jest.spyOn(EnterpriseAccessApiService, 'retrieveSubsidyAccessPolicy').mockRejectedValueOnce(new Error('Mock API Error'));
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useSubsidyAccessPolicy(mockSubsidyAccessPolicyUUID),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.isLoading).toBe(false);
+ expect(result.current.isError).toBe(true);
+ expect(result.current.error.message).toBe('Mock API Error');
+ });
+
+ it.each([
+ {
+ subsidyAccessPolicyId: undefined,
+ expectedData: undefined,
+ },
+ {
+ subsidyAccessPolicyId: mockSubsidyAccessPolicyUUID,
+ expectedData: {
+ uuid: mockSubsidyAccessPolicyUUID,
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ isAssignable: true,
+ // Other expected properties...
+ },
+ },
+ ])('should enable/disable the query based on subsidyAccessPolicyId (%s)', async ({
+ subsidyAccessPolicyId,
+ expectedData,
+ }) => {
+ // Mock the policy type in response based on subsidyAccessPolicyId
+ jest.spyOn(EnterpriseAccessApiService, 'retrieveSubsidyAccessPolicy').mockResolvedValueOnce({
+ data: {
+ uuid: mockSubsidyAccessPolicyUUID,
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ // Other properties...
+ },
+ });
+ const { result, waitForNextUpdate } = renderHook(() => useSubsidyAccessPolicy(subsidyAccessPolicyId), { wrapper });
+
+ if (expectedData) {
+ await waitForNextUpdate();
+ expect(result.current.isLoading).toBe(false);
+ } else {
+ expect(result.current.isLoading).toBe(true);
+ }
+
+ expect(result.current.isInitialLoading).toBe(false);
+ expect(result.current.isError).toBe(false);
+ expect(result.current.data).toEqual(expectedData);
+ });
+});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 4705d62507..681c421759 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -143,10 +143,11 @@ export const getBudgetStatus = (startDateStr, endDateStr, currentDate = new Date
};
};
-export const formatPrice = (price) => {
+export const formatPrice = (price, options = {}) => {
const USDollar = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
+ ...options,
});
return USDollar.format(Math.abs(price));
};
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
index 43cc7aae8d..c19d87dc50 100644
--- a/src/components/learner-credit-management/search/CatalogSearch.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -21,7 +21,7 @@ const CatalogSearch = () => {
id="catalogs.enterpriseCatalogs.header"
defaultMessage="Budget associated catalog"
description="Search dialogue."
- tagName="h2"
+ tagName="h3"
/>
diff --git a/src/components/learner-credit-management/search/CatalogSearchResults.jsx b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
index 60c20e0262..5127568653 100644
--- a/src/components/learner-credit-management/search/CatalogSearchResults.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
@@ -112,7 +112,6 @@ BaseCatalogSearchResults.defaultProps = {
searchResults: { disjunctiveFacetsRefinements: [], nbHits: 0, hits: [] },
error: null,
paginationComponent: SearchPagination,
- row: null,
preview: false,
setNoContent: () => {},
};
@@ -134,14 +133,10 @@ BaseCatalogSearchResults.propTypes = {
error: PropTypes.shape({
message: PropTypes.string,
}),
-
searchState: PropTypes.shape({
page: PropTypes.number,
}).isRequired,
paginationComponent: PropTypes.func,
- // eslint-disable-next-line react/no-unused-prop-types
- row: PropTypes.string,
- contentType: PropTypes.string.isRequired,
preview: PropTypes.bool,
setNoContent: PropTypes.func,
};
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index b3a85b84bb..781a012634 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -1,9 +1,10 @@
import React from 'react';
import { useParams } from 'react-router-dom';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
-import { screen, waitFor } from '@testing-library/react';
+import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
@@ -11,8 +12,11 @@ import { renderWithRouter } from '@edx/frontend-enterprise-utils';
import { act } from 'react-dom/test-utils';
import BudgetDetailPage from '../BudgetDetailPage';
-import { useOfferSummary, useOfferRedemptions } from '../data';
-import { EXEC_ED_OFFER_TYPE } from '../data/constants';
+import {
+ useSubsidyAccessPolicy,
+ useOfferRedemptions,
+ useBudgetContentAssignments,
+} from '../data';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
jest.mock('react-router-dom', () => ({
@@ -25,13 +29,28 @@ jest.mock('react-router-dom', () => ({
jest.mock('../data', () => ({
...jest.requireActual('../data'),
- useOfferSummary: jest.fn(),
useOfferRedemptions: jest.fn(),
+ useBudgetContentAssignments: jest.fn(),
+ useSubsidyAccessPolicy: jest.fn(),
}));
-useOfferSummary.mockReturnValue({
+useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'test-budget-uuid',
+ policyType: 'PerLearnerSpendCreditAccessPolicy',
+ displayName: null,
+ isAssignable: false,
+ },
+});
+useBudgetContentAssignments.mockReturnValue({
isLoading: false,
- offerSummary: null,
+ contentAssignments: {
+ count: 0,
+ results: [],
+ numPages: 1,
+ },
+ fetchContentAssignments: jest.fn(),
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
@@ -61,19 +80,12 @@ const initialStoreState = {
const mockEnterpriseOfferId = '123';
const mockSubsidyAccessPolicyUUID = 'c17de32e-b80b-468f-b994-85e68fd32751';
-const mockOfferDisplayName = 'Test Enterprise Offer';
-const mockOfferSummary = {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerType: EXEC_ED_OFFER_TYPE,
-};
-
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
+const queryClient = new QueryClient();
+
const BudgetDetailPageWrapper = ({
initialState = initialStoreState,
enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
@@ -81,13 +93,15 @@ const BudgetDetailPageWrapper = ({
}) => {
const store = getMockStore({ ...initialState });
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
};
@@ -103,6 +117,29 @@ describe('
', () => {
});
});
+ it.each([
+ { displayName: null },
+ { displayName: 'Test Budget Display Name' },
+ ])('renders budget header data', ({ displayName }) => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ displayName,
+ },
+ });
+ const expectedDisplayName = displayName || 'Overview';
+ renderWithRouter(
);
+
+ // Hero
+ expect(screen.getByText('Learner Credit Management'));
+ // Breadcrumb
+ expect(screen.getByText(expectedDisplayName, { selector: 'li' }));
+ // Page heading
+ expect(screen.getByText(expectedDisplayName, { selector: 'h2' }));
+ });
+
it.each([
{
budgetId: mockEnterpriseOfferId,
@@ -116,20 +153,10 @@ describe('
', () => {
budgetId,
expectedUseOfferRedemptionsArgs,
}) => {
- const mockOffer = {
- id: budgetId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- };
useParams.mockReturnValue({
budgetId,
activeTabKey: 'activity',
});
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: mockOfferSummary,
- });
useOfferRedemptions.mockReturnValue({
isLoading: false,
offerRedemptions: {
@@ -139,45 +166,108 @@ describe('
', () => {
},
fetchOfferRedemptions: jest.fn(),
});
- renderWithRouter(
-
,
- );
+ renderWithRouter(
);
expect(useOfferRedemptions).toHaveBeenCalledTimes(1);
expect(useOfferRedemptions).toHaveBeenCalledWith(...expectedUseOfferRedemptionsArgs);
- // Hero
- expect(screen.getByText('Learner Credit Management'));
- // Breadcrumb
- expect(screen.getByText('Overview'));
// Activity tab exists and is active
expect(screen.getByText('Activity').getAttribute('aria-selected')).toBe('true');
- // Spend table is visible within Activity tab contents
- expect(screen.getByText('No results found'));
+ // Catalog tab does NOT exist since the budget is not assignable
+ expect(screen.queryByText('Catalog')).not.toBeInTheDocument();
+
+ // Spent table is visible within Activity tab contents
+ const spentSection = within(screen.getByText('Spent').closest('section'));
+ expect(spentSection.getByText('No results found')).toBeInTheDocument();
+ });
+
+ it('renders with empty assigned table and catalog tab available for assignable budgets', () => {
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ isAssignable: true,
+ },
+ });
+ renderWithRouter(
);
+
+ // Assigned table is visible within Activity tab contents
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ expect(assignedSection.getByText('No results found')).toBeInTheDocument();
+
// Catalog tab exists and is NOT active
expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('false');
});
- it('renders with catalog tab active on initial load', async () => {
+ it('renders with assigned table data', () => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ isAssignable: true,
+ },
+ });
+ const mockLearnerEmail = 'edx@example.com';
+ const mockCourseKey = 'edX+DemoX';
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ learnerEmail: mockLearnerEmail,
+ contentKey: mockCourseKey,
+ },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ });
+ renderWithRouter(
);
+
+ // Assigned table is visible within Activity tab contents
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ expect(assignedSection.queryByText('No results found')).not.toBeInTheDocument();
+ expect(assignedSection.getByText(mockLearnerEmail)).toBeInTheDocument();
+ const viewCourseCTA = assignedSection.getByText('View course', { selector: 'a' });
+ expect(viewCourseCTA).toBeInTheDocument();
+ expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
+ });
+
+ it('renders with catalog tab active on initial load for assignable budgets', async () => {
useParams.mockReturnValue({
- budgetId: '123',
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
activeTabKey: 'catalog',
});
- renderWithRouter(
-
,
- );
+ renderWithRouter(
);
+
// Catalog tab exists and is active
expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('true');
});
- it('hides catalog tab when enterpriseFeatures.topDownAssignmentRealTimeLcm is false', () => {
+ it('hides catalog tab when budget is not assignable', () => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ policyType: 'PerLearnerSpendCreditAccessPolicy',
+ isAssignable: false,
+ },
+ });
+ renderWithRouter(
);
+
+ // Catalog tab does NOT exist
+ expect(screen.queryByText('Catalog')).toBeFalsy();
+ });
+
+ it('hides catalog tab when enterpriseFeatures.topDownAssignmentRealTimeLcm', () => {
const initialState = {
portalConfiguration: {
...initialStoreState.portalConfiguration,
@@ -186,13 +276,8 @@ describe('
', () => {
},
},
};
- renderWithRouter(
-
,
- );
+ renderWithRouter(
);
+
// Catalog tab does NOT exist
expect(screen.queryByText('Catalog')).toBeFalsy();
});
@@ -202,12 +287,8 @@ describe('
', () => {
budgetId: '123',
activeTabKey: undefined,
});
- renderWithRouter(
-
,
- );
+ renderWithRouter(
);
+
// Activity tab exists and is active
expect(screen.getByText('Activity').getAttribute('aria-selected')).toBe('true');
});
@@ -217,23 +298,21 @@ describe('
', () => {
budgetId: '123',
activeTabKey: 'invalid',
});
- renderWithRouter(
-
,
- );
+ renderWithRouter(
);
expect(screen.getByText('404')).toBeInTheDocument();
expect(screen.getByText('something went wrong', { exact: false })).toBeInTheDocument();
});
it('handles user switching to catalog tab', async () => {
- renderWithRouter(
-
,
- );
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ isAssignable: true,
+ },
+ });
+ renderWithRouter(
);
const catalogTab = screen.getByText('Catalog');
await act(async () => {
@@ -245,7 +324,12 @@ describe('
', () => {
});
});
- it('displays loading message while loading data', () => {
+ it('displays loading message while loading subsidy access policy metadata from API', () => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: true,
+ data: undefined,
+ });
+
renderWithRouter(
', () => {
}}
/>,
);
- expect(screen.getByText('Loading'));
+
+ expect(screen.getByText('loading budget details')).toBeInTheDocument();
});
});
diff --git a/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx b/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
index 093363a737..71b7a44970 100644
--- a/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
+++ b/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
@@ -2,18 +2,30 @@ import React from 'react';
import {
screen,
render,
+ waitFor,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import '@testing-library/jest-dom/extend-expect';
import EmailAddressTableCell from '../EmailAddressTableCell';
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-utils'),
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
+
const mockStore = configureMockStore();
+const mockEnterpriseUUID = 'test-enterprise-uuid';
+const mockContentAssignmentUUID = 'test-content-assignment-uuid';
+const mockFulfillmentIdentifier = 'test-fulfillment-identifier';
+
const mockInitialState = {
portalConfiguration: {
- enterpriseId: 'test-enterprise',
+ enterpriseId: mockEnterpriseUUID,
},
};
@@ -27,26 +39,67 @@ const EmailAddressTableCellWrapper = ({
);
describe('
', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
it('with email is present, display it', () => {
const userEmail = 'edx@example.com';
- const row = {
- original: {
- userEmail,
- },
+ const props = {
+ tableId: 'spent',
+ userEmail,
};
- render(
);
+ render(
);
expect(screen.getByText(userEmail));
});
- it('without email present, show popover message', async () => {
- const row = {
- original: {
- userEmail: null,
- },
+ it.each([
+ { enterpriseEnrollmentId: 123 },
+ { fulfillmentIdentifier: mockFulfillmentIdentifier },
+ { contentAssignmentUUID: mockContentAssignmentUUID },
+ ])('without email present, show popover message (%s)', async ({
+ enterpriseEnrollmentId,
+ fulfillmentIdentifier,
+ contentAssignmentUUID,
+ }) => {
+ const props = {
+ tableId: 'spent',
+ userEmail: null,
+ enterpriseEnrollmentId,
+ fulfillmentIdentifier,
+ contentAssignmentUUID,
};
- render(
);
+ render(
);
expect(screen.getByText('Email hidden'));
userEvent.click(screen.getByLabelText('More details'));
+
+ // Verify onEntered Segment event is called when popover opens
expect(await screen.findByText('Learner data disabled', { exact: false }));
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledWith(
+ mockEnterpriseUUID,
+ 'edx.ui.enterprise.admin_portal.learner-credit-management.spent.email-hidden-popover.opened',
+ {
+ enterpriseEnrollmentId,
+ fulfillmentIdentifier,
+ contentAssignmentUUID,
+ },
+ );
+
+ // Verify onExited Segment event is called when popover is closed
+ userEvent.click(screen.getByLabelText('More details'));
+ await waitFor(() => {
+ expect(screen.queryByText('Learner data disabled', { exact: false })).not.toBeInTheDocument();
+ });
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledWith(
+ mockEnterpriseUUID,
+ 'edx.ui.enterprise.admin_portal.learner-credit-management.spent.email-hidden-popover.dismissed',
+ {
+ enterpriseEnrollmentId,
+ fulfillmentIdentifier,
+ contentAssignmentUUID,
+ },
+ );
});
});
diff --git a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
index 785da8899d..09d2f29192 100644
--- a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
+++ b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
@@ -14,10 +14,13 @@ import MultipleBudgetsPage from '../MultipleBudgetsPage';
const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
-const enterpriseId = 'test-enterprise';
+const enterpriseId = 'test-enterprise-uuid';
+const enterpriseSlug = 'test-enterprise-slug';
const initialStore = {
portalConfiguration: {
enterpriseId,
+ enterpriseSlug,
+ enableLearnerPortal: true,
},
};
const store = getMockStore({ ...initialStore });
diff --git a/src/components/settings/SettingsTabs.jsx b/src/components/settings/SettingsTabs.jsx
index 729ac106bc..f8a10c4cf4 100644
--- a/src/components/settings/SettingsTabs.jsx
+++ b/src/components/settings/SettingsTabs.jsx
@@ -1,8 +1,4 @@
import React, { useState, useMemo } from 'react';
-import {
- QueryClient,
- QueryClientProvider,
-} from '@tanstack/react-query';
import {
Container,
Tabs,
@@ -31,12 +27,6 @@ import SettingsApiCredentialsTab from './SettingsApiCredentialsTab';
import { features } from '../../config';
import { updatePortalConfigurationEvent } from '../../data/actions/portalConfiguration';
-const queryClient = new QueryClient({
- queries: {
- retry: true, // optional: you may disable automatic query retries for all queries or on a per-query basis.
- },
-});
-
const SettingsTabs = ({
enterpriseId,
enterpriseSlug,
@@ -90,12 +80,10 @@ const SettingsTabs = ({
eventKey={SETTINGS_TABS_VALUES.sso}
title={SETTINGS_TAB_LABELS.sso}
>
-
-
-
+
,
);
}
@@ -145,6 +133,7 @@ const SettingsTabs = ({
,
);
}
+
return initialTabs;
}, [
FEATURE_SSO_SETTINGS_TAB,
diff --git a/src/components/test/testUtils.jsx b/src/components/test/testUtils.jsx
index 02a98208fb..e4a22ba445 100644
--- a/src/components/test/testUtils.jsx
+++ b/src/components/test/testUtils.jsx
@@ -1,5 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies */
-/* eslint-disable import/prefer-default-export */
import React from 'react';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
@@ -12,6 +11,7 @@ export function renderWithRouter(
history = createMemoryHistory({ initialEntries: [route] }),
} = {},
) {
+ // eslint-disable-next-line react/prop-types
const Wrapper = ({ children }) => (
{children}
);
diff --git a/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap b/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap
index 49c9024f3b..ad4e4dcae7 100644
--- a/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap
+++ b/src/containers/Sidebar/__snapshots__/Sidebar.test.jsx.snap
@@ -33,7 +33,6 @@ exports[`
renders correctly 1`] = `
>
renders correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -77,7 +76,6 @@ exports[` renders correctly 1`] = `
>
renders correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -121,7 +119,6 @@ exports[` renders correctly 1`] = `
>
renders correctly 1`] = `
className="d-flex align-items-center"
>
renders correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -210,7 +207,6 @@ exports[` renders correctly 1`] = `
>
renders correctly 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -275,7 +271,6 @@ exports[` renders correctly when code management is hidden 1`] = `
>
renders correctly when code management is hidden 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -319,7 +314,7 @@ exports[` renders correctly when code management is hidden 1`] = `
className="d-flex align-items-center"
>
renders correctly when code management is hidden 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -364,7 +359,6 @@ exports[` renders correctly when code management is hidden 1`] = `
>
renders correctly when code management is hidden 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -429,7 +423,6 @@ exports[` renders correctly when expanded 1`] = `
>
renders correctly when expanded 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -471,7 +464,6 @@ exports[` renders correctly when expanded 1`] = `
>
renders correctly when expanded 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -513,7 +505,6 @@ exports[` renders correctly when expanded 1`] = `
>
renders correctly when expanded 1`] = `
className="d-flex align-items-center"
>
renders correctly when expanded 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -598,7 +589,6 @@ exports[` renders correctly when expanded 1`] = `
>
renders correctly when expanded 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -661,7 +651,6 @@ exports[` renders correctly when expanded by toggle 1`] = `
>
renders correctly when expanded by toggle 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -703,7 +692,6 @@ exports[` renders correctly when expanded by toggle 1`] = `
>
renders correctly when expanded by toggle 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -745,7 +733,6 @@ exports[` renders correctly when expanded by toggle 1`] = `
>
renders correctly when expanded by toggle 1`] = `
className="d-flex align-items-center"
>
renders correctly when expanded by toggle 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
@@ -830,7 +817,6 @@ exports[` renders correctly when expanded by toggle 1`] = `
>
renders correctly when expanded by toggle 1`] = `
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/src/data/hooks.js b/src/data/hooks.js
index c44b512dd5..5f9e7df7b1 100644
--- a/src/data/hooks.js
+++ b/src/data/hooks.js
@@ -1,8 +1,4 @@
-import {
- useEffect, useMemo, useState, useRef,
-} from 'react';
-
-import { CONTENT_TYPE_COURSE } from '../components/learner-credit-management/data/constants';
+import { useEffect, useRef } from 'react';
export function useInterval(callback, delay) {
const savedCallback = useRef();
@@ -45,12 +41,3 @@ export function useTimeout(callback, delay) {
timeoutIdRef.current = null;
}, [callback, delay]);
}
-
-export const useSelectedCourse = () => {
- const [course, setCourse] = useState(null);
- const isCourse = useMemo(
- () => course?.contentType === CONTENT_TYPE_COURSE,
- [course],
- );
- return [course, setCourse, isCourse];
-};
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index 380428686e..be704fe658 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -1,4 +1,5 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+import { snakeCaseObject } from '@edx/frontend-platform/utils';
import { configuration } from '../../config';
@@ -141,6 +142,30 @@ class EnterpriseAccessApiService {
const url = `${EnterpriseAccessApiService.baseUrl}/coupon-code-requests/overview/?${params.toString()}`;
return EnterpriseAccessApiService.apiClient().get(url);
}
+
+ /**
+ * List content assignments for a specific AssignmentConfiguration.
+ */
+ static listContentAssignments(assignmentConfigurationUUID, options = {}) {
+ const params = new URLSearchParams({
+ page: 1,
+ page_size: 25,
+ // Only include assignments with allocated or errored states. The table should NOT
+ // include assignments in the cancelled or accepted states.
+ state__in: 'allocated,errored',
+ ...snakeCaseObject(options),
+ });
+ const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/?${params.toString()}`;
+ return EnterpriseAccessApiService.apiClient().get(url);
+ }
+
+ /**
+ * Retrieve a specific subsidy access policy.
+ */
+ static retrieveSubsidyAccessPolicy(subsidyAccessPolicyUUID) {
+ const url = `${EnterpriseAccessApiService.baseUrl}/subsidy-access-policies/${subsidyAccessPolicyUUID}/`;
+ return EnterpriseAccessApiService.apiClient().get(url);
+ }
}
export default EnterpriseAccessApiService;
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index 485bf7db90..fd1497705d 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -17,6 +17,8 @@ const enterpriseAccessBaseUrl = `${process.env.ENTERPRISE_ACCESS_BASE_URL}`;
const mockEnterpriseUUID = 'test-enterprise-id';
const mockLicenseRequestUUID = 'test-license-request-uuid';
const mockCouponCodeRequestUUID = 'test-coupon-code-request-uuid';
+const mockAssignmentConfigurationUUID = 'test-assignment-configuration-uuid';
+const mockSubsidyAccessPolicyUUID = 'test-subsidy-access-policy-uuid';
describe('EnterpriseAccessApiService', () => {
beforeEach(() => {
@@ -131,4 +133,23 @@ describe('EnterpriseAccessApiService', () => {
subsidy_type: SUPPORTED_SUBSIDY_TYPES.coupon,
});
});
+
+ test('listContentAssignments calls enterprise-access to fetch content assignments', () => {
+ EnterpriseAccessApiService.listContentAssignments(mockAssignmentConfigurationUUID);
+ const expectedParams = new URLSearchParams({
+ page: 1,
+ page_size: 25,
+ state__in: 'allocated,errored',
+ }).toString();
+ expect(axios.get).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/?${expectedParams}`,
+ );
+ });
+
+ test('retrieveSubsidyAccessPolicy calls enterprise-access to fetch subsidy access policy', () => {
+ EnterpriseAccessApiService.retrieveSubsidyAccessPolicy(mockSubsidyAccessPolicyUUID);
+ expect(axios.get).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/subsidy-access-policies/${mockSubsidyAccessPolicyUUID}/`,
+ );
+ });
});
From 080692cd7e71a0290e364116b80d094ec3d4b7d9 Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Thu, 19 Oct 2023 16:17:14 -0700
Subject: [PATCH 043/124] fix: bug fix for remind/revoke functions in license
management table (#1063)
* fix: bug fix for remind/revoke functions in license management table
* fix: syntax
* fix: added test coverage
---
.../tests/LicenseManagementRemindModal.test.jsx | 6 +++++-
.../tests/LicenseManagementRevokeModal.test.jsx | 6 +++++-
.../LicenseManagementTableActionColumn.jsx | 6 ++++--
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx
index 73fb27b88e..d7fb86876a 100644
--- a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRemindModal.test.jsx
@@ -31,10 +31,12 @@ jest.mock('../../../../../data/services/LicenseManagerAPIService', () => ({
}));
const onSubmitMock = jest.fn();
+const onSuccessMock = jest.fn();
+
const basicProps = {
isOpen: true,
onClose: () => {},
- onSuccess: () => {},
+ onSuccess: onSuccessMock,
onSubmit: onSubmitMock,
subscription: {
uuid: 'lorem',
@@ -110,6 +112,7 @@ describe(' ', () => {
const button = screen.getByText('Remind (1)');
await act(async () => { userEvent.click(button); });
expect(onSubmitMock).toBeCalledTimes(1);
+ expect(onSuccessMock).toBeCalledTimes(1);
expect(screen.queryByText('Remind (1)')).toBeFalsy();
expect(screen.queryByText('Done')).toBeTruthy();
@@ -127,6 +130,7 @@ describe(' ', () => {
const button = screen.getByText('Remind (1)');
await act(async () => { userEvent.click(button); });
expect(onSubmitMock).toBeCalledTimes(1);
+ expect(onSuccessMock).toBeCalledTimes(0);
await waitFor(() => {
expect(screen.getByRole('alert')).toBeTruthy();
diff --git a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx
index d9bb7b769c..309897217d 100644
--- a/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementModals/tests/LicenseManagementRevokeModal.test.jsx
@@ -23,10 +23,12 @@ jest.mock('../../../../../data/services/LicenseManagerAPIService', () => ({
}));
const onSubmitMock = jest.fn();
+const onSuccessMock = jest.fn();
+
const basicProps = {
isOpen: true,
onClose: () => {},
- onSuccess: () => {},
+ onSuccess: onSuccessMock,
onSubmit: onSubmitMock,
subscription: {
uuid: 'lorem',
@@ -101,6 +103,7 @@ describe(' ', () => {
const button = screen.getByText('Revoke (1)');
await act(async () => { userEvent.click(button); });
expect(onSubmitMock).toBeCalledTimes(1);
+ expect(onSuccessMock).toBeCalledTimes(1);
expect(screen.queryByText('Revoke (1)')).toBeFalsy();
expect(screen.getByText('Done'));
@@ -117,6 +120,7 @@ describe(' ', () => {
const button = screen.getByText('Revoke (1)');
await act(async () => { userEvent.click(button); });
expect(onSubmitMock).toBeCalledTimes(1);
+ expect(onSuccessMock).toBeCalledTimes(0);
await waitFor(() => {
expect(screen.getByRole('alert')).toBeTruthy();
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/LicenseManagementTableActionColumn.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/LicenseManagementTableActionColumn.jsx
index 42878231d1..cbf889203c 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/LicenseManagementTableActionColumn.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/LicenseManagementTableActionColumn.jsx
@@ -65,12 +65,14 @@ const LicenseManagementTableActionColumn = ({
const handleRevokeSuccess = () => {
setRevokeModal(modalZeroState);
- onRevokeSuccess(clearSelection)();
+ clearSelection();
+ onRevokeSuccess();
};
const handleRemindSuccess = () => {
setRemindModal(modalZeroState);
- onRemindSuccess(clearSelection)();
+ clearSelection();
+ onRemindSuccess();
};
const handleRevokeSubmit = () => {
From 8b12828a859b89d40667fccc220cc272e60d7e1b Mon Sep 17 00:00:00 2001
From: Feanil Patel
Date: Mon, 23 Oct 2023 11:29:27 -0400
Subject: [PATCH 044/124] chore: Update to the new version of brand-openedx in
the new scope. (#1064)
Part of https://github.com/openedx/axim-engineering/issues/23
This updates the brand alias to point to the package at the `openedx`
scope. This does not impact imports because this package is used via an
alias.
---
package-lock.json | 10 +++++-----
package.json | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index d37f4b5d06..0a62f3ad47 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"license": "AGPL-3.0",
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
- "@edx/brand": "npm:@edx/brand-openedx@1.2.0",
+ "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-enterprise-catalog-search": "4.2.0",
"@edx/frontend-enterprise-hotjar": "1.3.0",
"@edx/frontend-enterprise-logistration": "3.2.0",
@@ -2101,10 +2101,10 @@
}
},
"node_modules/@edx/brand": {
- "name": "@edx/brand-openedx",
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.2.0.tgz",
- "integrity": "sha512-r4PDN3rCgDsLovW44ayxoNNHgG5I4Rvss6MG5CrQEX4oW8YhQVEod+jJtwR5vi0mFLN2GIaMlDpd7iIy03VqXg=="
+ "name": "@openedx/brand-openedx",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@openedx/brand-openedx/-/brand-openedx-1.2.2.tgz",
+ "integrity": "sha512-mBvxR7aB9290j9+h3d/9G8VkG1b8ecLSmlxc0vskfm7DL/fKUzFmHAj3PI7Z4kkwCQOL4QT5mJHJKC0ZFf7qvQ=="
},
"node_modules/@edx/browserslist-config": {
"version": "1.0.0",
diff --git a/package.json b/package.json
index 324366e66f..d042436e6c 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"license": "AGPL-3.0",
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
- "@edx/brand": "npm:@edx/brand-openedx@1.2.0",
+ "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-enterprise-catalog-search": "4.2.0",
"@edx/frontend-enterprise-hotjar": "1.3.0",
"@edx/frontend-enterprise-logistration": "3.2.0",
From b8110af99cacb6cc18ab6b4d88e050bc0f5fcaf8 Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Thu, 26 Oct 2023 13:49:10 -0700
Subject: [PATCH 045/124] feat: add course title to assigned table (#1066)
* feat: add course title to assigned table
* fix: unit test
* fix: add fall back 'view course' text
---
.../AssignmentDetailsTableCell.jsx | 4 +-
.../tests/BudgetDetailPage.test.jsx | 41 ++++++++++++++++++-
2 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
index 92826cb93f..21a2f6ddeb 100644
--- a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
@@ -20,9 +20,10 @@ const AssignmentDetailsTableCell = ({ row, enterpriseSlug }) => {
className="x-small"
destination={`${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${row.original.contentKey}`}
target="_blank"
+ showLaunchIcon={false}
isInline
>
- View course
+ {row.original?.contentTitle || 'View Course'}
>
@@ -39,6 +40,7 @@ AssignmentDetailsTableCell.propTypes = {
uuid: PropTypes.string.isRequired,
learnerEmail: PropTypes.string.isRequired,
contentKey: PropTypes.string.isRequired,
+ contentTitle: PropTypes.string,
}).isRequired,
}).isRequired,
enterpriseSlug: PropTypes.string,
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 781a012634..1e32550883 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -214,6 +214,7 @@ describe(' ', () => {
},
});
const mockLearnerEmail = 'edx@example.com';
+ const mockContentTitle = 'edx Demo';
const mockCourseKey = 'edX+DemoX';
useBudgetContentAssignments.mockReturnValue({
isLoading: false,
@@ -224,6 +225,7 @@ describe(' ', () => {
uuid: 'test-uuid',
learnerEmail: mockLearnerEmail,
contentKey: mockCourseKey,
+ contentTitle: mockContentTitle,
},
],
numPages: 1,
@@ -236,7 +238,44 @@ describe(' ', () => {
const assignedSection = within(screen.getByText('Assigned').closest('section'));
expect(assignedSection.queryByText('No results found')).not.toBeInTheDocument();
expect(assignedSection.getByText(mockLearnerEmail)).toBeInTheDocument();
- const viewCourseCTA = assignedSection.getByText('View course', { selector: 'a' });
+ const viewCourseCTA = assignedSection.getByText('edx Demo', { selector: 'a' });
+ expect(viewCourseCTA).toBeInTheDocument();
+ expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
+ });
+
+ it('renders with assigned table data "View Course" hyperlink default when content title is null', () => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: {
+ uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ isAssignable: true,
+ },
+ });
+ const mockLearnerEmail = 'edx@example.com';
+ const mockCourseKey = 'edX+DemoX';
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ learnerEmail: mockLearnerEmail,
+ contentKey: mockCourseKey,
+ },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ });
+ renderWithRouter( );
+
+ // Assigned table is visible within Activity tab contents
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ expect(assignedSection.queryByText('No results found')).not.toBeInTheDocument();
+ expect(assignedSection.getByText(mockLearnerEmail)).toBeInTheDocument();
+ const viewCourseCTA = assignedSection.getByText('View Course', { selector: 'a' });
expect(viewCourseCTA).toBeInTheDocument();
expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
});
From 3a2e08150b34def18bcac1ce2e5de53a07ac9ba9 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 27 Oct 2023 13:48:40 -0400
Subject: [PATCH 046/124] feat: learner credit management activity tab empty
states (#1068)
---
docs/decisions/0006-tanstack-react-query.rst | 71 ++++
.../AssignMoreCoursesEmptyStateMinimal.jsx | 51 +++
.../AssignmentDetailsTableCell.jsx | 5 +-
.../BudgetAssignmentsTable.jsx | 6 +-
.../BudgetCard-V2.jsx | 2 +-
.../BudgetDetailActivityTabContents.jsx | 71 ++--
.../BudgetDetailAssignments.jsx | 52 ++-
.../BudgetDetailRedemptions.jsx | 65 ++--
.../NoBudgetActivityEmptyState.jsx | 95 ++++++
.../SpendTableEnrollmentDetails.jsx | 2 +-
.../assets/confirmSpend.svg | 128 +++++++
.../assets/findTheRightCourse.svg | 42 +++
.../assets/nameYourLearners.svg | 77 +++++
.../data/constants.js | 10 +
.../data/hooks/index.js | 3 +
.../hooks/useBudgetContentAssignments.test.js | 2 +-
.../hooks/useBudgetDetailActivityOverview.js | 22 ++
.../useBudgetDetailActivityOverview.test.jsx | 160 +++++++++
.../data/hooks/useIsLargeOrGreater.js | 5 +
.../data/hooks/usePathToCatalogTab.js | 12 +
.../data/hooks/useSubsidyAccessPolicy.js | 11 +-
.../hooks/useSubsidyAccessPolicy.test.jsx | 5 +
.../data/tests/constants.js | 25 ++
.../learner-credit-management/data/utils.js | 100 ++++++
.../AssignMoreCoursesEmptyStateMinimal.scss | 14 +
.../tests/BudgetDetailPage.test.jsx | 318 +++++++++++++-----
src/index.scss | 1 +
27 files changed, 1165 insertions(+), 190 deletions(-)
create mode 100644 docs/decisions/0006-tanstack-react-query.rst
create mode 100644 src/components/learner-credit-management/AssignMoreCoursesEmptyStateMinimal.jsx
create mode 100644 src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx
create mode 100644 src/components/learner-credit-management/assets/confirmSpend.svg
create mode 100644 src/components/learner-credit-management/assets/findTheRightCourse.svg
create mode 100644 src/components/learner-credit-management/assets/nameYourLearners.svg
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.js
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/useIsLargeOrGreater.js
create mode 100644 src/components/learner-credit-management/data/hooks/usePathToCatalogTab.js
create mode 100644 src/components/learner-credit-management/data/tests/constants.js
create mode 100644 src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss
diff --git a/docs/decisions/0006-tanstack-react-query.rst b/docs/decisions/0006-tanstack-react-query.rst
new file mode 100644
index 0000000000..6b5523124c
--- /dev/null
+++ b/docs/decisions/0006-tanstack-react-query.rst
@@ -0,0 +1,71 @@
+6. Adopting ``@tanstack/react-query`` for data fetching and client-side caching
+=============================================================================
+
+Status
+******
+
+Accepted (October 2023)
+
+Context
+*******
+
+The ``frontend-app-admin-portal`` MFE currently relies on custom state variables when integrating with an API (e.g., managing loading states). As a result, there is generally a fair amount of boilerplate involved with each API integration. Additionally, the current approach does not provide any client-side caching out-of-the-box or any other best-in-class features like automatic query retries, which can lead to heavy reliance on approaches such as React Context or Redux in order to pass data returned by asynchronous API calls throughout the application.
+
+The existing approach of heavily relying on React Context providers has resulted in many nested context providers that can make it difficult for contributors to understand what data is available to them from which context provider. Additionally, the reliance on context providers means accepting some performance risk that all components nested under a context provider will re-render whenever any value within the context provider changes, which can lead to performance issues if the context provider is wrapping a large number of components if not mitigated with techniques like ``React.memo``, ``useMemo``, or context selectors.
+
+Decisions
+*********
+
+We will instead rely on ``@tanstack/react-query`` for data fetching and client-side caching. This library provides a number of benefits over the existing approach, including:
+
+* Using ``@tanstack/react-query`` will allow us to avoid writing a significant amount of boilerplate code that is currently required to integrate with an API. We can rely on the library to handle loading states, error states, and other common API integration concerns.
+* Flexible client-side caching, which will allow us to avoid using custom caching logic provided by ``@edx/frontend-platform``, which is not as full-featured.
+* A number of other features out-of-the-box, including automatic query retries on failed network requests, which will allow us to avoid writing custom logic to handle these scenarios.
+* Using ``@tanstack/react-query`` will allow us to avoid relying on React Context providers to pass data returned by asynchronous API calls throughout the application. Instead, we can rely on the library to handle this for us via custom hooks. For example, calling ``useQuery`` twice within the rendering lifecycle will not result in duplicate API calls. Instead, the library will first make the initial network call and then store its response in a client-side cache. The second call to ``useQuery`` will then return the cached response instead of making a duplicate network call. The cache invalidation is customizable globally for the application or by query.
+
+
+We will adopt query key factories to manage the implementation of query keys such that cache invalidation for independent features (e.g., Learner Credit Management) can be managed with adequate granularity. For example:
+
+::
+
+ // Query Key Factory
+ export const learnerCreditManagementQueryKeys = {
+ all: ['learner-credit-management'],
+ budgets: () => [...learnerCreditManagementQueryKeys.all, 'budgets'],
+ budget: (budgetId) => [...learnerCreditManagementQueryKeys.all, 'budget', budgetId],
+ budgetActivity: (budgetId) => [...learnerCreditManagementQueryKeys.budget(budgetId), 'activity'],
+ budgetActivityOverview: (budgetId) => [...learnerCreditManagementQueryKeys.budgetActivity(budgetId), 'overview'],
+ };
+
+By having a query key factory as suggested above, contributors may have a structured way to manage query keys for a given feature. This approach enabled granular control over cache invalidation, query prefetching, etc. Using the above example, one could invalidate the query cache for the entire ``['learner-credit-management']`` feature or opt to only invalidate the query cache for specific individual queries or even groups of queries:
+
+::
+
+ // Remove everything related to the learner credit management feature
+ queryClient.removeQueries({
+ queryKey: learnerCreditManagementQueryKeys.all,
+ })
+
+ // Invalidate all queries supporting the budget detail page route
+ queryClient.invalidateQueries({
+ queryKey: learnerCreditManagementQueryKeys.budget(budgetId),
+ })
+
+ // Invalidate the budget detail page route's activity tab's overview query
+ queryClient.invalidateQueries({
+ queryKey: learnerCreditManagementQueryKeys.budgetActivityOverview(budgetId),
+ })
+
+The recommendation for new asynchronous network calls moving forward is to use ``@tanstack/react-query``. Additionally, it's recommended to incrementally migrate existing network calls to use ``@tanstack/react-query``. This will allow us to avoid a large refactoring effort and instead migrate to the library over time as we touch existing network calls.
+
+Consequences
+************
+
+The entire application is wrapped within ``QueryClientProvider``, which contains a default configuration to at least extend the ``staleTime`` to be 20 seconds. This change in default behavior is to enable the use of ``@tanstack/react-query`` as a state manager. Instead of queries becoming instantly stale after success (i.e., ``staleTime: 0``) where network calls would be re-fetched on all window refocuses and component mounts, etc., having a ``staleTime`` of 20 seconds will keep the data fresh for 20 seconds before the query becomes stale, preventing unnecessary API calls (e.g., when calling duplicate ``useQuery`` hooks in the same rendering lifecycle).
+
+By adopting ``@tanstack/react-query``, by default, queries made with ``useQuery`` will have some automatic background refetching behavior baked in. It's recommended to consider whether any specific queries should override/extend the default options provided by the library. For example, some specific queries may not want the automatic refetches, etc. that are provided by default.
+
+Alternatives Considered
+***********************
+
+* In order to enable the same pattern of relying on ``@tanstack/react-query`` as a state manager to avoid additional context providers, it was considered whether we could make heavier use of the client-side caching provided by ``@edx/frontend-platform``. This was decided against as it is not as full-featured as ``@tanstack/react-query`` when it comes to caching alone. Additionally, it does not provide any other features that are provided by ``@tanstack/react-query``, which will enable more efficient API integrations moving forward.
diff --git a/src/components/learner-credit-management/AssignMoreCoursesEmptyStateMinimal.jsx b/src/components/learner-credit-management/AssignMoreCoursesEmptyStateMinimal.jsx
new file mode 100644
index 0000000000..d51595c5e9
--- /dev/null
+++ b/src/components/learner-credit-management/AssignMoreCoursesEmptyStateMinimal.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { Button, Card } from '@edx/paragon';
+
+import {
+ formatDate, formatPrice, useBudgetId, usePathToCatalogTab, useSubsidyAccessPolicy,
+} from './data';
+import nameYourLearner from './assets/nameYourLearners.svg';
+
+const AssignMoreCoursesEmptyStateMinimal = () => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const pathToCatalogTab = usePathToCatalogTab();
+
+ if (!subsidyAccessPolicy) {
+ return null;
+ }
+
+ const availableBalance = subsidyAccessPolicy.aggregates.spendAvailableUsd;
+ const subsidyExpirationDate = subsidyAccessPolicy.subsidyExpirationDatetime;
+
+ return (
+
+
+
+
+
+
Assign more courses to maximize your budget.
+
+ Your budget's available balance of {formatPrice(availableBalance)} will
+ expire on {formatDate(subsidyExpirationDate)}.
+
+
+
+
+
+
+ Assign courses
+
+
+
+ );
+};
+
+export default AssignMoreCoursesEmptyStateMinimal;
diff --git a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
index 21a2f6ddeb..b04f3b0a9b 100644
--- a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
@@ -20,10 +20,9 @@ const AssignmentDetailsTableCell = ({ row, enterpriseSlug }) => {
className="x-small"
destination={`${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${row.original.contentKey}`}
target="_blank"
- showLaunchIcon={false}
isInline
>
- {row.original?.contentTitle || 'View Course'}
+ {row.original.contentTitle || 'View Course'}
>
@@ -38,7 +37,7 @@ AssignmentDetailsTableCell.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
uuid: PropTypes.string.isRequired,
- learnerEmail: PropTypes.string.isRequired,
+ learnerEmail: PropTypes.string,
contentKey: PropTypes.string.isRequired,
contentTitle: PropTypes.string,
}).isRequired,
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 5b2b94b5b0..cbb7596feb 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -47,9 +47,9 @@ const BudgetAssignmentsTable = ({
filters: [],
}}
fetchData={fetchTableData}
- data={tableData.results}
- itemCount={tableData.count}
- pageCount={tableData.numPages}
+ data={tableData?.results || []}
+ itemCount={tableData?.count || 0}
+ pageCount={tableData?.numPages || 1}
EmptyTableComponent={CustomDataTableEmptyState}
/>
);
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
index 3be534f1dd..41ae121150 100644
--- a/src/components/learner-credit-management/BudgetCard-V2.jsx
+++ b/src/components/learner-credit-management/BudgetCard-V2.jsx
@@ -73,7 +73,7 @@ BudgetCard.propTypes = {
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
- offerType: PropTypes.string.isRequired,
+ offerType: PropTypes.oneOf(Object.values(BUDGET_TYPES)).isRequired,
displayName: PropTypes.string,
};
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index 28a152fce7..517c4e4954 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -1,56 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import { Stack } from '@edx/paragon';
+import { Stack, Skeleton } from '@edx/paragon';
import BudgetDetailRedemptions from './BudgetDetailRedemptions';
import BudgetDetailAssignments from './BudgetDetailAssignments';
-import {
- useOfferRedemptions,
- useBudgetContentAssignments,
- useBudgetId,
- useSubsidyAccessPolicy,
-} from './data';
+import { useBudgetDetailActivityOverview } from './data';
+import NoBudgetActivityEmptyState from './NoBudgetActivityEmptyState';
-const BudgetDetailActivityTabContents = ({
- enterpriseUUID,
- enterpriseFeatures,
-}) => {
- const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
+const BudgetDetailActivityTabContents = ({ enterpriseUUID, enterpriseFeatures }) => {
+ const isTopDownAssignmentEnabled = enterpriseFeatures.topDownAssignmentRealTimeLcm;
const {
- data: subsidyAccessPolicy,
- } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ isLoading: isBudgetActivityOverviewLoading,
+ data: budgetActivityOverview,
+ } = useBudgetDetailActivityOverview({
+ enterpriseUUID,
+ isTopDownAssignmentEnabled,
+ });
- const isTopDownAssignmentEnabled = enterpriseFeatures?.topDownAssignmentRealTimeLcm;
+ if (isBudgetActivityOverviewLoading || !budgetActivityOverview) {
+ return (
+ <>
+
+ loading budget activity overview
+ >
+ );
+ }
- const {
- isLoading: isLoadingOfferRedemptions,
- offerRedemptions,
- fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
+ const hasContentAssignments = !!budgetActivityOverview.contentAssignments?.count > 0;
+ const hasSpentTransactions = !!budgetActivityOverview.spentTransactions?.count > 0;
- const {
- isLoading: isLoadingContentAssignments,
- contentAssignments,
- fetchContentAssignments,
- } = useBudgetContentAssignments({
- assignmentConfigurationUUID: subsidyAccessPolicy?.assignmentConfiguration?.uuid,
- isEnabled: subsidyAccessPolicy?.isAssignable && isTopDownAssignmentEnabled,
- });
+ // If there is no activity whatsoever (no assignments, no spent transactions), show the
+ // full empty state.
+ if (!hasContentAssignments && !hasSpentTransactions) {
+ return (
+
+ );
+ }
+ // Otherwise, render the contents of the "Activity" tab.
return (
-
+
);
};
@@ -64,7 +59,7 @@ BudgetDetailActivityTabContents.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
enterpriseFeatures: PropTypes.shape({
topDownAssignmentRealTimeLcm: PropTypes.bool,
- }),
+ }).isRequired,
};
export default connect(mapStateToProps)(BudgetDetailActivityTabContents);
diff --git a/src/components/learner-credit-management/BudgetDetailAssignments.jsx b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
index f4ba6eb8f2..218ac84d5d 100644
--- a/src/components/learner-credit-management/BudgetDetailAssignments.jsx
+++ b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
@@ -1,17 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+
import BudgetAssignmentsTable from './BudgetAssignmentsTable';
+import AssignMoreCoursesEmptyStateMinimal from './AssignMoreCoursesEmptyStateMinimal';
+import { useBudgetContentAssignments, useBudgetId, useSubsidyAccessPolicy } from './data';
const BudgetDetailAssignments = ({
- isEnabled,
- isLoading,
- tableData,
- fetchTableData,
+ hasContentAssignments,
+ hasSpentTransactions,
+ enterpriseFeatures,
}) => {
- if (!isEnabled) {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const isAssignableBudget = !!subsidyAccessPolicy?.isAssignable;
+ const assignmentConfigurationUUID = subsidyAccessPolicy?.assignmentConfiguration?.uuid;
+ const isTopDownAssignmentEnabled = enterpriseFeatures.topDownAssignmentRealTimeLcm;
+ const {
+ isLoading,
+ contentAssignments,
+ fetchContentAssignments,
+ } = useBudgetContentAssignments({
+ isEnabled: isAssignableBudget && hasContentAssignments,
+ assignmentConfigurationUUID,
+ });
+
+ if (!isTopDownAssignmentEnabled || !isAssignableBudget) {
return null;
}
+ if (!hasContentAssignments && hasSpentTransactions) {
+ return (
+
+ );
+ }
+
return (
Assigned
@@ -21,18 +44,23 @@ const BudgetDetailAssignments = ({
);
};
+const mapStateToProps = state => ({
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
+});
+
BudgetDetailAssignments.propTypes = {
- isEnabled: PropTypes.bool.isRequired,
- isLoading: PropTypes.bool.isRequired,
- tableData: PropTypes.shape().isRequired,
- fetchTableData: PropTypes.func.isRequired,
+ hasContentAssignments: PropTypes.bool.isRequired,
+ hasSpentTransactions: PropTypes.bool.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }).isRequired,
};
-export default BudgetDetailAssignments;
+export default connect(mapStateToProps)(BudgetDetailAssignments);
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index bccf80aa6c..67fd14f087 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -3,55 +3,38 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
+import { useBudgetId, useOfferRedemptions } from './data';
-const BudgetDetailRedemptions = ({
- isLoading,
- offerRedemptions,
- fetchOfferRedemptions,
- enterpriseUUID,
- enterpriseSlug,
- enableLearnerPortal,
-}) => (
-
- Spent
-
- Spent activity is driven by completed enrollments. Enrollment data is automatically updated every 12 hours.
- Come back later to view more recent enrollments.
-
-
-
-);
+const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
+ const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
+ const {
+ isLoading,
+ offerRedemptions,
+ fetchOfferRedemptions,
+ } = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
+
+ return (
+
+ Spent
+
+ Spent activity is driven by completed enrollments. Enrollment data is automatically updated every 12 hours.
+ Come back later to view more recent enrollments.
+
+
+
+ );
+};
const mapStateToProps = state => ({
enterpriseUUID: state.portalConfiguration.enterpriseId,
- enterpriseSlug: state.portalConfiguration.enterpriseSlug,
- enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
});
BudgetDetailRedemptions.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
- enterpriseSlug: PropTypes.string.isRequired,
- enableLearnerPortal: PropTypes.bool.isRequired,
- isLoading: PropTypes.bool.isRequired,
- offerRedemptions: PropTypes.shape({
- results: PropTypes.arrayOf(PropTypes.shape({
- userEmail: PropTypes.string,
- courseTitle: PropTypes.string.isRequired,
- courseListPrice: PropTypes.number.isRequired,
- enrollmentDate: PropTypes.string.isRequired,
- courseProductLine: PropTypes.string.isRequired,
- })),
- itemCount: PropTypes.number.isRequired,
- pageCount: PropTypes.number.isRequired,
- }).isRequired,
- fetchOfferRedemptions: PropTypes.func.isRequired,
};
export default connect(mapStateToProps)(BudgetDetailRedemptions);
diff --git a/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx b/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx
new file mode 100644
index 0000000000..3d6f074208
--- /dev/null
+++ b/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx
@@ -0,0 +1,95 @@
+import React from 'react';
+import classNames from 'classnames';
+import {
+ Button, Card, Row, Col,
+} from '@edx/paragon';
+import { Link } from 'react-router-dom';
+
+import { useIsLargeOrGreater, usePathToCatalogTab } from './data';
+import nameYourLearners from './assets/nameYourLearners.svg';
+import findTheRightCourse from './assets/findTheRightCourse.svg';
+import confirmSpend from './assets/confirmSpend.svg';
+
+const FindTheRightCourseIllustration = (props) => (
+
+);
+
+const NameYourLearnersIllustration = (props) => (
+
+);
+
+const ConfirmSpendIllustration = (props) => (
+
+);
+
+const NoBudgetActivityEmptyState = () => {
+ const pathToCatalogTab = usePathToCatalogTab();
+ const isLargeOrGreater = useIsLargeOrGreater();
+
+ return (
+
+
+
+ No budget activity yet? Assign a course!
+
+ {isLargeOrGreater && (
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ {!isLargeOrGreater && }
+
+ 01
+ Find the right course
+
+
+ Check out your budget's catalog of courses and select the course you
+ want to assign to learners.
+
+
+
+ {!isLargeOrGreater && }
+
+ 02
+ Name your learners
+
+
+ You will be prompted to enter email addresses for the learner or
+ learners you want to assign.
+
+
+
+ {!isLargeOrGreater && }
+
+ 03
+ Confirm spend
+
+
+ Once confirmed, the total cost will be deducted from your budget,
+ and you can track your spending right here!
+
+
+
+
+
+ Get started
+
+
+
+
+ );
+};
+
+export default NoBudgetActivityEmptyState;
diff --git a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
index dafd44b88f..a0d61f8dda 100644
--- a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
+++ b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
@@ -39,7 +39,7 @@ const rowPropType = PropTypes.shape({
original: PropTypes.shape({
courseKey: PropTypes.string.isRequired,
courseTitle: PropTypes.string.isRequired,
- userEmail: PropTypes.string.isRequired,
+ userEmail: PropTypes.string,
enterpriseEnrollmentId: PropTypes.number,
fulfillmentIdentifier: PropTypes.string,
}).isRequired,
diff --git a/src/components/learner-credit-management/assets/confirmSpend.svg b/src/components/learner-credit-management/assets/confirmSpend.svg
new file mode 100644
index 0000000000..826f4eea85
--- /dev/null
+++ b/src/components/learner-credit-management/assets/confirmSpend.svg
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/learner-credit-management/assets/findTheRightCourse.svg b/src/components/learner-credit-management/assets/findTheRightCourse.svg
new file mode 100644
index 0000000000..5834e5fd6b
--- /dev/null
+++ b/src/components/learner-credit-management/assets/findTheRightCourse.svg
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/learner-credit-management/assets/nameYourLearners.svg b/src/components/learner-credit-management/assets/nameYourLearners.svg
new file mode 100644
index 0000000000..e015abf48a
--- /dev/null
+++ b/src/components/learner-credit-management/assets/nameYourLearners.svg
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 8db03bb0ce..8a93ee6f6d 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -37,3 +37,13 @@ export const EXEC_COURSE_TYPE = 'executive-education-2u';
// Number of items to display per page in Budget Detail assignment/spend tables
export const PAGE_SIZE = 25;
export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
+
+// Query Key factory for the learner credit management module, intended to be used with `@tanstack/react-query`.
+// Inspired by https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories.
+export const learnerCreditManagementQueryKeys = {
+ all: ['learner-credit-management'],
+ budgets: () => [...learnerCreditManagementQueryKeys.all, 'budgets'],
+ budget: (budgetId) => [...learnerCreditManagementQueryKeys.all, 'budget', budgetId],
+ budgetActivity: (budgetId) => [...learnerCreditManagementQueryKeys.budget(budgetId), 'activity'],
+ budgetActivityOverview: (budgetId) => [...learnerCreditManagementQueryKeys.budgetActivity(budgetId), 'overview'],
+};
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index 3b21c9be3f..b7c35f509f 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -4,3 +4,6 @@ export { default as useOfferRedemptions } from './useOfferRedemptions';
export { default as useBudgetContentAssignments } from './useBudgetContentAssignments';
export { default as useBudgetId } from './useBudgetId';
export { default as useSubsidyAccessPolicy } from './useSubsidyAccessPolicy';
+export { default as usePathToCatalogTab } from './usePathToCatalogTab';
+export { default as useBudgetDetailActivityOverview } from './useBudgetDetailActivityOverview';
+export { default as useIsLargeOrGreater } from './useIsLargeOrGreater';
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
index 0ede9195bd..a0715f22ef 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
+++ b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
@@ -1,6 +1,6 @@
import { renderHook } from '@testing-library/react-hooks';
-import useBudgetContentAssignments from './useBudgetContentAssignments'; // Import the hook
+import useBudgetContentAssignments from './useBudgetContentAssignments';
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
describe('useBudgetContentAssignments', () => {
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.js b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.js
new file mode 100644
index 0000000000..8b7a945e84
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.js
@@ -0,0 +1,22 @@
+import { useQuery } from '@tanstack/react-query';
+import { retrieveBudgetDetailActivityOverview } from '../utils';
+import useBudgetId from './useBudgetId';
+import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
+import { learnerCreditManagementQueryKeys } from '../constants';
+
+const useBudgetDetailActivityOverview = ({ enterpriseUUID, isTopDownAssignmentEnabled }) => {
+ const { budgetId, subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ return useQuery({
+ queryKey: learnerCreditManagementQueryKeys.budgetActivityOverview(budgetId),
+ queryFn: (args) => retrieveBudgetDetailActivityOverview({
+ ...args,
+ budgetId,
+ subsidyAccessPolicy,
+ enterpriseUUID,
+ isTopDownAssignmentEnabled,
+ }),
+ });
+};
+
+export default useBudgetDetailActivityOverview;
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
new file mode 100644
index 0000000000..cdb3b3e9bf
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
@@ -0,0 +1,160 @@
+import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
+import { renderHook } from '@testing-library/react-hooks';
+
+import useBudgetDetailActivityOverview from './useBudgetDetailActivityOverview';
+import useBudgetId from './useBudgetId';
+import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import {
+ mockAssignableSubsidyAccessPolicy,
+ mockPerLearnerSpendLimitSubsidyAccessPolicy,
+ mockEnterpriseOfferId,
+ mockSubsidyAccessPolicyUUID,
+} from '../tests/constants';
+
+jest.mock('./useBudgetId');
+jest.mock('./useSubsidyAccessPolicy');
+
+const mockEnterpriseUUID = 'mock-enterprise-uuid';
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+});
+
+const wrapper = ({ children }) => (
+
+ {children}
+
+);
+
+describe('useBudgetDetailActivityOverview', () => {
+ beforeEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it('handles when budget is an enterprise offer id (not a subsidy access policy uuid)', async () => {
+ useBudgetId.mockReturnValue({
+ budgetId: mockEnterpriseOfferId,
+ subsidyAccessPolicyId: undefined,
+ });
+ useSubsidyAccessPolicy.mockReturnValue({ data: undefined });
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ const mockFetchCourseEnrollments = jest.spyOn(EnterpriseDataApiService, 'fetchCourseEnrollments');
+ mockFetchCourseEnrollments.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [{ id: 'mock-course-enrollment-id' }],
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useBudgetDetailActivityOverview({
+ enterpriseUUID: mockEnterpriseUUID,
+ isTopDownAssignmentEnabled: true,
+ }),
+ { wrapper },
+ );
+
+ expect(useSubsidyAccessPolicy).toHaveBeenCalledWith(undefined);
+
+ expect(mockListContentAssignments).not.toHaveBeenCalled();
+ expect(mockFetchCourseEnrollments).toHaveBeenCalledTimes(1);
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: true,
+ data: undefined,
+ }),
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ spentTransactions: {
+ count: 1,
+ results: [{ id: 'mock-course-enrollment-id' }],
+ },
+ },
+ }),
+ );
+ });
+
+ it.each([
+ { hasAssignableBudget: false, isTopDownAssignmentEnabled: false },
+ { hasAssignableBudget: true, isTopDownAssignmentEnabled: false },
+ { hasAssignableBudget: false, isTopDownAssignmentEnabled: true },
+ { hasAssignableBudget: true, isTopDownAssignmentEnabled: true },
+ ])('handles when budget is a subsidy access policy uuid (not an enterprise offer id) (%s)', async ({ hasAssignableBudget, isTopDownAssignmentEnabled }) => {
+ useBudgetId.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ subsidyAccessPolicyId: mockSubsidyAccessPolicyUUID,
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ data: hasAssignableBudget ? mockAssignableSubsidyAccessPolicy : mockPerLearnerSpendLimitSubsidyAccessPolicy,
+ });
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ if (hasAssignableBudget && isTopDownAssignmentEnabled) {
+ mockListContentAssignments.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [{ id: 'mock-content-assignment-id' }],
+ },
+ });
+ }
+ const mockFetchCourseEnrollments = jest.spyOn(EnterpriseDataApiService, 'fetchCourseEnrollments');
+ mockFetchCourseEnrollments.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [{ id: 'mock-course-enrollment-id' }],
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useBudgetDetailActivityOverview({
+ enterpriseUUID: mockEnterpriseUUID,
+ isTopDownAssignmentEnabled: true,
+ }),
+ { wrapper },
+ );
+
+ expect(useSubsidyAccessPolicy).toHaveBeenCalledWith(mockSubsidyAccessPolicyUUID);
+
+ expect(mockFetchCourseEnrollments).toHaveBeenCalledTimes(1);
+ if (hasAssignableBudget) {
+ expect(mockListContentAssignments).toHaveBeenCalledTimes(1);
+ } else {
+ expect(mockListContentAssignments).not.toHaveBeenCalled();
+ }
+
+ await waitForNextUpdate();
+
+ const expectedData = {
+ spentTransactions: {
+ count: 1,
+ results: [{ id: 'mock-course-enrollment-id' }],
+ },
+ };
+
+ if (hasAssignableBudget && isTopDownAssignmentEnabled) {
+ expectedData.contentAssignments = {
+ count: 1,
+ results: [{ id: 'mock-content-assignment-id' }],
+ };
+ }
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: expectedData,
+ }),
+ );
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/useIsLargeOrGreater.js b/src/components/learner-credit-management/data/hooks/useIsLargeOrGreater.js
new file mode 100644
index 0000000000..b3ac4f0a64
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useIsLargeOrGreater.js
@@ -0,0 +1,5 @@
+import { breakpoints, useMediaQuery } from '@edx/paragon';
+
+const useIsLargeOrGreater = () => useMediaQuery({ query: `(min-width: ${breakpoints.large.minWidth}px)` });
+
+export default useIsLargeOrGreater;
diff --git a/src/components/learner-credit-management/data/hooks/usePathToCatalogTab.js b/src/components/learner-credit-management/data/hooks/usePathToCatalogTab.js
new file mode 100644
index 0000000000..1ff9faf586
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/usePathToCatalogTab.js
@@ -0,0 +1,12 @@
+import { useRouteMatch, generatePath } from 'react-router-dom';
+
+import useBudgetId from './useBudgetId';
+
+const usePathToCatalogTab = () => {
+ const { budgetId } = useBudgetId();
+ const routeMatch = useRouteMatch();
+ const pathToCatalogTab = generatePath(routeMatch.path, { budgetId, activeTabKey: 'catalog' });
+ return pathToCatalogTab;
+};
+
+export default usePathToCatalogTab;
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
index c36d72ffa0..7058cdcb1a 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
+++ b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
@@ -2,10 +2,13 @@ import { useQuery } from '@tanstack/react-query';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import { learnerCreditManagementQueryKeys } from '../constants';
-const determineBudgetAssignability = (policyType) => {
+const determineBudgetAssignability = (subsidyAccessPolicy) => {
+ const policyType = subsidyAccessPolicy?.policyType;
+ const isAssignable = !!subsidyAccessPolicy?.assignmentConfiguration;
const assignableSubsidyAccessPolicyTypes = ['AssignedLearnerCreditAccessPolicy'];
- return assignableSubsidyAccessPolicyTypes.includes(policyType);
+ return isAssignable && assignableSubsidyAccessPolicyTypes.includes(policyType);
};
/**
@@ -18,12 +21,12 @@ const getSubsidyAccessPolicy = async ({ queryKey }) => {
const subsidyAccessPolicyUUID = queryKey[2];
const response = await EnterpriseAccessApiService.retrieveSubsidyAccessPolicy(subsidyAccessPolicyUUID);
const subsidyAccessPolicy = camelCaseObject(response.data);
- subsidyAccessPolicy.isAssignable = determineBudgetAssignability(subsidyAccessPolicy.policyType);
+ subsidyAccessPolicy.isAssignable = determineBudgetAssignability(subsidyAccessPolicy);
return subsidyAccessPolicy;
};
const useSubsidyAccessPolicy = (subsidyAccessPolicyId, { queryOptions } = {}) => useQuery({
- queryKey: ['learner-credit-management', 'subsidy-access-policy', subsidyAccessPolicyId],
+ queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
queryFn: getSubsidyAccessPolicy,
enabled: !!subsidyAccessPolicyId,
...queryOptions,
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
index 4d67e12051..30edc0f3cc 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
@@ -5,6 +5,7 @@ import useSubsidyAccessPolicy from './useSubsidyAccessPolicy'; // Import the hoo
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
const mockSubsidyAccessPolicyUUID = '9af340a9-48de-4d94-976d-e2282b9eb7f3';
+const mockAssignmentConfiguration = { uuid: 'test-assignment-configuration-uuid' };
// Mock the EnterpriseAccessApiService
jest.mock('../../../../data/services/EnterpriseAccessApiService', () => ({
@@ -43,6 +44,7 @@ describe('useSubsidyAccessPolicy', () => {
data: {
uuid: mockSubsidyAccessPolicyUUID,
policyType: isAssignable ? 'AssignedLearnerCreditAccessPolicy' : 'PerLearnerCreditSpendLimitAccessPolicy',
+ assignmentConfiguration: isAssignable ? mockAssignmentConfiguration : undefined,
// Other properties...
},
});
@@ -59,6 +61,7 @@ describe('useSubsidyAccessPolicy', () => {
uuid: mockSubsidyAccessPolicyUUID,
policyType: isAssignable ? 'AssignedLearnerCreditAccessPolicy' : 'PerLearnerCreditSpendLimitAccessPolicy',
isAssignable,
+ assignmentConfiguration: isAssignable ? mockAssignmentConfiguration : undefined,
// Other expected properties...
});
});
@@ -89,6 +92,7 @@ describe('useSubsidyAccessPolicy', () => {
expectedData: {
uuid: mockSubsidyAccessPolicyUUID,
policyType: 'AssignedLearnerCreditAccessPolicy',
+ assignmentConfiguration: mockAssignmentConfiguration,
isAssignable: true,
// Other expected properties...
},
@@ -102,6 +106,7 @@ describe('useSubsidyAccessPolicy', () => {
data: {
uuid: mockSubsidyAccessPolicyUUID,
policyType: 'AssignedLearnerCreditAccessPolicy',
+ assignmentConfiguration: mockAssignmentConfiguration,
// Other properties...
},
});
diff --git a/src/components/learner-credit-management/data/tests/constants.js b/src/components/learner-credit-management/data/tests/constants.js
new file mode 100644
index 0000000000..a72d643f63
--- /dev/null
+++ b/src/components/learner-credit-management/data/tests/constants.js
@@ -0,0 +1,25 @@
+export const mockEnterpriseOfferId = '123';
+export const mockSubsidyAccessPolicyUUID = 'c17de32e-b80b-468f-b994-85e68fd32751';
+
+export const mockAssignableSubsidyAccessPolicy = {
+ uuid: mockSubsidyAccessPolicyUUID,
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ assignmentConfiguration: {
+ uuid: 'test-uuid',
+ },
+ displayName: 'Assignable Learner Credit',
+ aggregates: {
+ spendAvailableUsd: 10000,
+ },
+ isAssignable: true,
+};
+
+export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
+ uuid: mockSubsidyAccessPolicyUUID,
+ policyType: 'PerLearnerSpendCreditAccessPolicy',
+ displayName: 'Per Learner Spend Limit',
+ aggregates: {
+ spendAvailableUsd: 10000,
+ },
+ isAssignable: false,
+};
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 681c421759..9874c22f4b 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -1,11 +1,15 @@
import { v4 as uuidv4 } from 'uuid';
import dayjs from 'dayjs';
+import { camelCaseObject } from '@edx/frontend-platform';
import {
LOW_REMAINING_BALANCE_PERCENT_THRESHOLD,
NO_BALANCE_REMAINING_DOLLAR_THRESHOLD,
} from './constants';
import { BUDGET_STATUSES } from '../../EnterpriseApp/data/constants';
+import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
+import EnterpriseDataApiService from '../../../data/services/EnterpriseDataApiService';
+
/**
* Transforms offer summary from API for display in the UI, guarding
* against bad data (e.g., accounting for refunds).
@@ -147,6 +151,7 @@ export const formatPrice = (price, options = {}) => {
const USDollar = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
+ minimumFractionDigits: 0,
...options,
});
return USDollar.format(Math.abs(price));
@@ -185,6 +190,101 @@ export const orderOffers = (offers) => {
return offers;
};
+/**
+ * Formats a date string to MMM D, YYYY format.
+ * @param {string} date Date string.
+ * @returns Formatted date string.
+ */
export function formatDate(date) {
return dayjs(date).format('MMM D, YYYY');
}
+
+/**
+ * Retrieves content assignments for the given budget's assignment configuration UUID (retrieved from the associated
+ * subsidy access policy).
+ *
+ * @param {String} assignmentConfigurationUUID The UUID of the assignment configuration.
+ * @param {Object} options Optional options object to pass/override query parameters.
+ *
+ * @returns Camelcased response from the content assignments.
+ */
+export async function fetchContentAssignments(assignmentConfigurationUUID, options = {}) {
+ const response = await EnterpriseAccessApiService.listContentAssignments(assignmentConfigurationUUID, options);
+ return camelCaseObject(response.data);
+}
+
+/**
+ * Retrieves spent transactions for the given budget (either a subsidy access
+ * policy or an enterprise offer), if any.
+ *
+ * @param {Object} args An object containing various arguments.
+ * @param {String} args.enterpriseUUID The UUID of the enterprise customer.
+ * @param {String} args.subsidyAccessPolicyId The UUID of a subsidy access policy, if any.
+ * @param {String} args.enterpriseOfferId The UUID of an enterprise offer, if any.
+ *
+ * @returns Camelcased response from the spent transactions.
+ */
+export async function fetchSpentTransactions({
+ enterpriseUUID,
+ subsidyAccessPolicyId,
+ enterpriseOfferId,
+}) {
+ const options = {
+ page: 1,
+ pageSize: 25,
+ ignoreNullCourseListPrice: true,
+ };
+
+ if (subsidyAccessPolicyId) {
+ options.budgetId = subsidyAccessPolicyId;
+ } else if (enterpriseOfferId) {
+ options.offerId = enterpriseOfferId;
+ }
+
+ const response = await EnterpriseDataApiService.fetchCourseEnrollments(
+ enterpriseUUID,
+ options,
+ );
+ return camelCaseObject(response.data);
+}
+
+/**
+ * Retrieves the requisite overview budget detail activity from relevant APIs, including spent transactions
+ * and (if applicable) any content assignments for the budget. Content assignments are only fetched when the
+ * budget is a subsidy access policy that is assignable and the top-down assignment feature is enabled.
+ *
+ * @param {Object} args An object containing various arguments. Note: `@tanstack/reat-query` passes
+ * additional arguments.
+ * @param {Array} args.budgetId The budget id for the currently viewing budget.
+ * @param {Object} [args.subsidyAccessPolicy] The subsidy access policy metadata, if any. Not
+ * applicable when the budget is an enterprise offer.
+ * @param {String} args.enterpriseUUID The UUID of the enterprise customer.
+ * @param {Boolean} args.isTopDownAssignmentEnabled Whether the top-down assignment feature is enabled.
+ * @returns An object containing the first page of spent transactions and (if applicable) content assignments.
+ */
+export async function retrieveBudgetDetailActivityOverview({
+ budgetId,
+ subsidyAccessPolicy,
+ enterpriseUUID,
+ isTopDownAssignmentEnabled,
+}) {
+ const isBudgetAssignable = !!(isTopDownAssignmentEnabled && subsidyAccessPolicy?.isAssignable);
+ const promisesToFulfill = [
+ fetchSpentTransactions({
+ enterpriseUUID,
+ subsidyAccessPolicyId: subsidyAccessPolicy?.uuid,
+ enterpriseOfferId: budgetId,
+ }),
+ ];
+ if (isBudgetAssignable) {
+ promisesToFulfill.push(fetchContentAssignments(subsidyAccessPolicy.assignmentConfiguration.uuid));
+ }
+ const responses = await Promise.allSettled(promisesToFulfill);
+ const result = {
+ spentTransactions: responses[0].value,
+ };
+ if (isBudgetAssignable) {
+ result.contentAssignments = responses[1].value;
+ }
+ return result;
+}
diff --git a/src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss b/src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss
new file mode 100644
index 0000000000..8a1e4db6e7
--- /dev/null
+++ b/src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss
@@ -0,0 +1,14 @@
+// The `Card` component in Paragon does not seem to properly let consumers customize the width of the `Card.Body`
+// contents when in the horizontal card orientation without custom CSS. As a result, both the `Card.Footer` and
+// `Card.Body` incorrectly get equal column widths when the preference is that the `Card.Body` has more width than
+// the `Card.Footer`. The below styles force the `Card.Body` to have appropriately more width than the `Card.Footer` when
+// the `Card` is in the horizontal orientation.
+
+.assign-more-courses-empty-state-minimal {
+ .assign-more-courses__card-body {
+ flex: 3;
+ }
+ .assign-more-courses__card-footer {
+ flex: 1;
+ }
+}
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 1e32550883..96280e7e68 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -16,15 +16,20 @@ import {
useSubsidyAccessPolicy,
useOfferRedemptions,
useBudgetContentAssignments,
+ useBudgetDetailActivityOverview,
+ useIsLargeOrGreater,
} from '../data';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
+import {
+ mockAssignableSubsidyAccessPolicy,
+ mockPerLearnerSpendLimitSubsidyAccessPolicy,
+ mockSubsidyAccessPolicyUUID,
+ mockEnterpriseOfferId,
+} from '../data/tests/constants';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
- useParams: jest.fn().mockReturnValue({
- budgetId: '123',
- activeTabKey: 'activity',
- }),
+ useParams: jest.fn(),
}));
jest.mock('../data', () => ({
@@ -32,36 +37,10 @@ jest.mock('../data', () => ({
useOfferRedemptions: jest.fn(),
useBudgetContentAssignments: jest.fn(),
useSubsidyAccessPolicy: jest.fn(),
+ useBudgetDetailActivityOverview: jest.fn(),
+ useIsLargeOrGreater: jest.fn().mockReturnValue(true),
}));
-useSubsidyAccessPolicy.mockReturnValue({
- isInitialLoading: false,
- data: {
- uuid: 'test-budget-uuid',
- policyType: 'PerLearnerSpendCreditAccessPolicy',
- displayName: null,
- isAssignable: false,
- },
-});
-useBudgetContentAssignments.mockReturnValue({
- isLoading: false,
- contentAssignments: {
- count: 0,
- results: [],
- numPages: 1,
- },
- fetchContentAssignments: jest.fn(),
-});
-useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
- },
- fetchOfferRedemptions: jest.fn(),
-});
-
const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
const enterpriseSlug = 'test-enterprise';
@@ -77,9 +56,18 @@ const initialStoreState = {
},
};
-const mockEnterpriseOfferId = '123';
-const mockSubsidyAccessPolicyUUID = 'c17de32e-b80b-468f-b994-85e68fd32751';
-
+const mockLearnerEmail = 'edx@example.com';
+const mockCourseKey = 'edX+DemoX';
+const mockContentTitle = 'edx Demo';
+const mockEmptyStateBudgetDetailActivityOverview = {
+ contentAssignments: { count: 0 },
+ spentTransactions: { count: 0 },
+};
+const mockEmptyOfferRedemptions = {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+};
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
@@ -107,27 +95,24 @@ const BudgetDetailPageWrapper = ({
describe(' ', () => {
beforeEach(() => {
- jest.clearAllMocks();
- });
-
- afterEach(() => {
- useParams.mockReturnValue({
- budgetId: '123',
- activeTabKey: 'activity',
- });
+ jest.resetAllMocks();
});
it.each([
{ displayName: null },
{ displayName: 'Test Budget Display Name' },
- ])('renders budget header data', ({ displayName }) => {
+ ])('renders budget header data (%s)', ({ displayName }) => {
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ activeTabKey: 'activity',
+ });
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
- data: {
- uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
- policyType: 'AssignedLearnerCreditAccessPolicy',
- displayName,
- },
+ data: { ...mockAssignableSubsidyAccessPolicy, displayName },
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
});
const expectedDisplayName = displayName || 'Overview';
renderWithRouter( );
@@ -140,6 +125,32 @@ describe(' ', () => {
expect(screen.getByText(expectedDisplayName, { selector: 'h2' }));
});
+ it.each([
+ { isLargeViewport: true },
+ { isLargeViewport: false },
+ ])('displays budget activity overview empty state', ({ isLargeViewport }) => {
+ useIsLargeOrGreater.mockReturnValue(isLargeViewport);
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
+ });
+ renderWithRouter( );
+
+ // Overview empty state (no content assignments, no spent transactions)
+ expect(screen.getByText('No budget activity yet? Assign a course!')).toBeInTheDocument();
+ const illustrationTestIds = ['find-the-right-course-illustration', 'name-your-learners-illustration', 'confirm-spend-illustration'];
+ illustrationTestIds.forEach(testId => expect(screen.getByTestId(testId)).toBeInTheDocument());
+ expect(screen.getByText('Get started', { selector: 'a' })).toBeInTheDocument();
+ });
+
it.each([
{
budgetId: mockEnterpriseOfferId,
@@ -157,13 +168,29 @@ describe(' ', () => {
budgetId,
activeTabKey: 'activity',
});
- useOfferRedemptions.mockReturnValue({
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: undefined,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: undefined,
+ spentTransactions: { count: 1 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
isLoading: false,
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
+ contentAssignments: {
+ count: 0,
results: [],
+ numPages: 1,
},
+ fetchContentAssignments: jest.fn(),
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
fetchOfferRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -181,41 +208,63 @@ describe(' ', () => {
expect(spentSection.getByText('No results found')).toBeInTheDocument();
});
- it('renders with empty assigned table and catalog tab available for assignable budgets', () => {
+ it('renders with assigned table empty state with spent table and catalog tab available for assignable budgets', () => {
useParams.mockReturnValue({
- budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
});
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
data: {
- uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
- policyType: 'AssignedLearnerCreditAccessPolicy',
- isAssignable: true,
+ contentAssignments: { count: 0 },
+ spentTransactions: { count: 1 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 0,
+ results: [],
+ numPages: 1,
},
+ fetchContentAssignments: jest.fn(),
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
});
renderWithRouter( );
- // Assigned table is visible within Activity tab contents
- const assignedSection = within(screen.getByText('Assigned').closest('section'));
- expect(assignedSection.getByText('No results found')).toBeInTheDocument();
+ // Assigned table empty state is visible within Activity tab contents
+ expect(screen.getByText('Assign more courses to maximize your budget.')).toBeInTheDocument();
+ expect(screen.getByText('available balance of $10,000', { exact: false })).toBeInTheDocument();
+ expect(screen.getByText('Assign courses', { selector: 'a' })).toBeInTheDocument();
// Catalog tab exists and is NOT active
expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('false');
});
it('renders with assigned table data', () => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
data: {
- uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
- policyType: 'AssignedLearnerCreditAccessPolicy',
- isAssignable: true,
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
},
});
- const mockLearnerEmail = 'edx@example.com';
- const mockContentTitle = 'edx Demo';
- const mockCourseKey = 'edX+DemoX';
useBudgetContentAssignments.mockReturnValue({
isLoading: false,
contentAssignments: {
@@ -232,28 +281,38 @@ describe(' ', () => {
currentPage: 1,
},
});
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
renderWithRouter( );
// Assigned table is visible within Activity tab contents
const assignedSection = within(screen.getByText('Assigned').closest('section'));
expect(assignedSection.queryByText('No results found')).not.toBeInTheDocument();
expect(assignedSection.getByText(mockLearnerEmail)).toBeInTheDocument();
- const viewCourseCTA = assignedSection.getByText('edx Demo', { selector: 'a' });
+ const viewCourseCTA = assignedSection.getByText(mockContentTitle, { selector: 'a' });
expect(viewCourseCTA).toBeInTheDocument();
expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
});
it('renders with assigned table data "View Course" hyperlink default when content title is null', () => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
data: {
- uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
- policyType: 'AssignedLearnerCreditAccessPolicy',
- isAssignable: true,
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
},
});
- const mockLearnerEmail = 'edx@example.com';
- const mockCourseKey = 'edX+DemoX';
useBudgetContentAssignments.mockReturnValue({
isLoading: false,
contentAssignments: {
@@ -268,6 +327,12 @@ describe(' ', () => {
numPages: 1,
currentPage: 1,
},
+ fetchContentAssignments: jest.fn(),
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -282,9 +347,17 @@ describe(' ', () => {
it('renders with catalog tab active on initial load for assignable budgets', async () => {
useParams.mockReturnValue({
- budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'catalog',
});
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValueOnce({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
+ });
renderWithRouter( );
// Catalog tab exists and is active
@@ -292,12 +365,19 @@ describe(' ', () => {
});
it('hides catalog tab when budget is not assignable', () => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
+ data: mockPerLearnerSpendLimitSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
data: {
- uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
- policyType: 'PerLearnerSpendCreditAccessPolicy',
- isAssignable: false,
+ contentAssignments: undefined,
+ spentTransactions: { count: 0 },
},
});
renderWithRouter( );
@@ -306,7 +386,7 @@ describe(' ', () => {
expect(screen.queryByText('Catalog')).toBeFalsy();
});
- it('hides catalog tab when enterpriseFeatures.topDownAssignmentRealTimeLcm', () => {
+ it('hides catalog tab when enterpriseFeatures.topDownAssignmentRealTimeLcm is disabled', () => {
const initialState = {
portalConfiguration: {
...initialStoreState.portalConfiguration,
@@ -315,6 +395,21 @@ describe(' ', () => {
},
},
};
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: undefined,
+ spentTransactions: { count: 0 },
+ },
+ });
renderWithRouter( );
// Catalog tab does NOT exist
@@ -323,9 +418,20 @@ describe(' ', () => {
it('defaults to activity tab is no activeTabKey is provided', () => {
useParams.mockReturnValue({
- budgetId: '123',
+ budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: undefined,
});
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: undefined,
+ spentTransactions: { count: 0 },
+ },
+ });
renderWithRouter( );
// Activity tab exists and is active
@@ -334,22 +440,34 @@ describe(' ', () => {
it('displays not found message is invalid activeTabKey is provided', () => {
useParams.mockReturnValue({
- budgetId: '123',
+ budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'invalid',
});
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
+ });
renderWithRouter( );
expect(screen.getByText('404')).toBeInTheDocument();
expect(screen.getByText('something went wrong', { exact: false })).toBeInTheDocument();
});
it('handles user switching to catalog tab', async () => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
- data: {
- uuid: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
- policyType: 'AssignedLearnerCreditAccessPolicy',
- isAssignable: true,
- },
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
});
renderWithRouter( );
const catalogTab = screen.getByText('Catalog');
@@ -364,11 +482,18 @@ describe(' ', () => {
});
it('displays loading message while loading subsidy access policy metadata from API', () => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: true,
data: undefined,
});
-
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
+ });
renderWithRouter(
', () => {
expect(screen.getByText('loading budget details')).toBeInTheDocument();
});
+
+ it.each([
+ { isActivityOverviewLoading: true },
+ { isActivityOverviewLoading: false },
+ ])('displays loading skeletons while fetching budget detail activity overview data from API endpoints (%s)', ({ isActivityOverviewLoading }) => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: isActivityOverviewLoading,
+ data: undefined,
+ });
+ renderWithRouter( );
+
+ expect(screen.getByText('loading budget activity overview')).toBeInTheDocument();
+ });
});
diff --git a/src/index.scss b/src/index.scss
index 34c7e99ba0..dfdae9bc8e 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -24,6 +24,7 @@ $modal-max-width: 650px;
@import "./components/BulkEnrollmentPage/BulkEnrollment";
@import "./components/Admin/Admin";
@import "./components/settings/settings";
+@import "./components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal";
body {
overflow-x: hidden;
From c0a76057ecf79514e682d4180675acc62baa4efb Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 27 Oct 2023 14:42:22 -0400
Subject: [PATCH 047/124] fix: handle additional case when not viewing
assignable budget (#1073)
---
.../BudgetDetailActivityTabContents.jsx | 8 +++++++-
.../tests/BudgetDetailPage.test.jsx | 17 +++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index 517c4e4954..123b48f7e5 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -5,11 +5,13 @@ import { Stack, Skeleton } from '@edx/paragon';
import BudgetDetailRedemptions from './BudgetDetailRedemptions';
import BudgetDetailAssignments from './BudgetDetailAssignments';
-import { useBudgetDetailActivityOverview } from './data';
+import { useBudgetDetailActivityOverview, useBudgetId, useSubsidyAccessPolicy } from './data';
import NoBudgetActivityEmptyState from './NoBudgetActivityEmptyState';
const BudgetDetailActivityTabContents = ({ enterpriseUUID, enterpriseFeatures }) => {
const isTopDownAssignmentEnabled = enterpriseFeatures.topDownAssignmentRealTimeLcm;
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
isLoading: isBudgetActivityOverviewLoading,
data: budgetActivityOverview,
@@ -27,6 +29,10 @@ const BudgetDetailActivityTabContents = ({ enterpriseUUID, enterpriseFeatures })
);
}
+ if (!isTopDownAssignmentEnabled || !subsidyAccessPolicy?.isAssignable) {
+ return ;
+ }
+
const hasContentAssignments = !!budgetActivityOverview.contentAssignments?.count > 0;
const hasSpentTransactions = !!budgetActivityOverview.spentTransactions?.count > 0;
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 96280e7e68..fd87153c21 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -280,6 +280,7 @@ describe(' ', () => {
numPages: 1,
currentPage: 1,
},
+ fetchContentAssignments: jest.fn(),
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
@@ -380,10 +381,18 @@ describe(' ', () => {
spentTransactions: { count: 0 },
},
});
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
renderWithRouter( );
// Catalog tab does NOT exist
expect(screen.queryByText('Catalog')).toBeFalsy();
+
+ // Ensure no assignments-related empty states are rendered
+ expect(screen.queryByText('No budget activity yet? Assign a course!')).not.toBeInTheDocument();
});
it('hides catalog tab when enterpriseFeatures.topDownAssignmentRealTimeLcm is disabled', () => {
@@ -410,10 +419,18 @@ describe(' ', () => {
spentTransactions: { count: 0 },
},
});
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
renderWithRouter( );
// Catalog tab does NOT exist
expect(screen.queryByText('Catalog')).toBeFalsy();
+
+ // Ensure no assignments-related empty states are rendered
+ expect(screen.queryByText('No budget activity yet? Assign a course!')).not.toBeInTheDocument();
});
it('defaults to activity tab is no activeTabKey is provided', () => {
From 017f3490bbb2f7dce34ca3e3341a6c4393ed25be Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Fri, 27 Oct 2023 14:53:14 -0700
Subject: [PATCH 048/124] feat: display search result cards in catalog tab
(#1059)
* feat: display search result cards in catalog tab
* fix: failing test in BudgetDetailPage
* fix: replace word register with enroll
* fix: implemented reviewer comments
* fix: lint error
* fix: lint error
* feat: added policy's catalog uuid to search filter
* fix: failing test
* fix: refactored based on reviewer feedback
* fix: lint error
* fix: refactored code to include new api field and updated test
* fix: removing unused prop in test
* fix: refactored
* fix: search filters
* chore: refactored
---
.../BudgetDetailCatalogTabContents.jsx | 14 +-
.../cards/CourseCard.jsx | 152 +++++++++++-------
.../cards/CourseCard.test.jsx | 71 ++++----
.../learner-credit-management/constants.js | 18 +++
.../data/constants.js | 8 +-
.../learner-credit-management/data/utils.js | 11 ++
.../learner-credit-management/index.jsx | 1 -
.../learner-credit.scss | 25 ---
.../search/CatalogSearch.jsx | 14 +-
.../search/CatalogSearchResults.jsx | 44 ++---
.../tests/CatalogSearchResults.test.jsx | 14 +-
11 files changed, 214 insertions(+), 158 deletions(-)
create mode 100644 src/components/learner-credit-management/constants.js
delete mode 100644 src/components/learner-credit-management/learner-credit.scss
diff --git a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
index a99f268db3..a143c1f07a 100644
--- a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
@@ -5,10 +5,20 @@ import { Row, Col } from '@edx/paragon';
import { SearchData, SEARCH_FACET_FILTERS } from '@edx/frontend-enterprise-catalog-search';
import CatalogSearch from './search/CatalogSearch';
-import { LANGUAGE_REFINEMENT, LEARNING_TYPE_REFINEMENT } from './data';
+import {
+ LANGUAGE_REFINEMENT,
+ LEARNING_TYPE_REFINEMENT,
+ useBudgetId,
+ useSubsidyAccessPolicy,
+} from './data';
import { configuration } from '../../config';
const BudgetDetailCatalogTabContents = () => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const {
+ data: subsidyAccessPolicy,
+ } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
const language = {
attribute: LANGUAGE_REFINEMENT,
title: 'Language',
@@ -38,7 +48,7 @@ const BudgetDetailCatalogTabContents = () => {
indexName={configuration.ALGOLIA.INDEX_NAME}
searchClient={searchClient}
>
-
+
diff --git a/src/components/learner-credit-management/cards/CourseCard.jsx b/src/components/learner-credit-management/cards/CourseCard.jsx
index 9284369239..f4885ce433 100644
--- a/src/components/learner-credit-management/cards/CourseCard.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.jsx
@@ -3,95 +3,137 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { camelCaseObject } from '@edx/frontend-platform';
-import cardFallbackImg from '@edx/brand/paragon/images/card-imagecap-fallback.png';
import {
- Badge, Button, Card, Hyperlink,
+ Badge,
+ Button,
+ Card,
+ Stack,
+ Hyperlink,
+ useMediaQuery,
+ breakpoints,
} from '@edx/paragon';
-import { EXEC_COURSE_TYPE } from '../data/constants';
-import { formatDate } from '../data/utils';
+import { injectIntl } from '@edx/frontend-platform/i18n';
+import { camelCaseObject } from '@edx/frontend-platform';
+import cardFallbackImg from '@edx/brand/paragon/images/card-imagecap-fallback.png';
+
+import { EXEC_ED_COURSE_TYPE } from '../data';
+import { formatPrice, formatDate, getEnrollmentDeadline } from '../data/utils';
+import CARD_TEXT from '../constants';
const CourseCard = ({
- onClick, original,
+ original,
}) => {
const {
- title,
+ availability,
cardImageUrl,
courseType,
normalizedMetadata,
partners,
+ title,
} = camelCaseObject(original);
- let priceText;
+ const isSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth });
+ const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });
+
+ const {
+ BADGE,
+ BUTTON_ACTION,
+ PRICE,
+ ENROLLMENT,
+ } = CARD_TEXT;
+
+ const price = normalizedMetadata?.contentPrice ? formatPrice(normalizedMetadata.contentPrice, { minimumFractionDigits: 0 }) : 'N/A';
+
+ const imageSrc = cardImageUrl || cardFallbackImg;
+
+ let logoSrc;
+ let logoAlt;
+ if (partners.length === 1) {
+ logoSrc = partners[0]?.logoImageUrl;
+ logoAlt = `${partners[0]?.name}'s logo`;
+ }
+
const altText = `${title} course image`;
+ const formattedAvailability = availability?.length ? availability.join(', ') : null;
+
+ const enrollmentDeadline = getEnrollmentDeadline(normalizedMetadata?.enrollByDate);
+
+ let courseEnrollmentInfo;
+ let execEdEnrollmentInfo;
+ if (normalizedMetadata?.enrollByDate) {
+ courseEnrollmentInfo = `${formattedAvailability} • ${ENROLLMENT.text} ${enrollmentDeadline}`;
+ execEdEnrollmentInfo = `Starts ${formatDate(normalizedMetadata.startDate)} •
+ ${ENROLLMENT.text} ${enrollmentDeadline}`;
+ } else {
+ courseEnrollmentInfo = formattedAvailability;
+ execEdEnrollmentInfo = formattedAvailability;
+ }
+
+ const isExecEd = courseType === EXEC_ED_COURSE_TYPE;
+
return (
onClick(original)}
- orientation="horizontal"
- tabIndex="0"
+ orientation={isSmall ? 'vertical' : 'horizontal'}
>
-
-
-
{title}
-
{partners[0]?.name}
- {courseType === EXEC_COURSE_TYPE && (
-
- Executive Education
-
- )}
- {courseType !== EXEC_COURSE_TYPE && (
-
+
+
+ {price}
+ {PRICE.subText}
+
)}
-
- Starts {formatDate(normalizedMetadata?.start_date)} •
- Learner must register by {formatDate(normalizedMetadata?.enroll_by_date)}
-
-
-
- {priceText}
- Per learner price
-
- View course
-
- Assign
-
+ />
+
+
+ {isExecEd ? BADGE.execEd : BADGE.course}
+
-
+
+
+ {BUTTON_ACTION.viewCourse}
+
+ {BUTTON_ACTION.assign}
+
+
);
};
-CourseCard.defaultProps = {
- onClick: () => {},
-};
-
CourseCard.propTypes = {
- onClick: PropTypes.func,
original: PropTypes.shape({
- title: PropTypes.string,
+ availability: PropTypes.arrayOf(PropTypes.string),
cardImageUrl: PropTypes.string,
+ courseType: PropTypes.string,
+ normalizedMetadata: PropTypes.shape(),
+ originalImageUrl: PropTypes.string,
partners: PropTypes.arrayOf(
PropTypes.shape({
+ logoImageUrl: PropTypes.string,
name: PropTypes.string,
- logo_image_url: PropTypes.string,
}),
),
- normalizedMetadata: PropTypes.shape({
- startDate: PropTypes.string,
- endDate: PropTypes.string,
- enrollByDate: PropTypes.string,
- }),
- courseType: PropTypes.string,
+ title: PropTypes.string,
}).isRequired,
};
-export default CourseCard;
+export default injectIntl(CourseCard);
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index 963137e178..704a1f825c 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -4,47 +4,46 @@ import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import CourseCard from './CourseCard';
-import { CONTENT_TYPE_COURSE, EXEC_ED_TITLE } from '../data/constants';
-
-jest.mock('@edx/frontend-platform', () => ({
- ...jest.requireActual('@edx/frontend-platform'),
-}));
-
-const TEST_CATALOG = ['ayylmao'];
const originalData = {
- title: 'Course Title',
+ availability: ['Upcoming'],
card_image_url: undefined,
- partners: [{ logo_image_url: '', name: 'Course Provider' }],
- first_enrollable_paid_seat_price: 100,
+ course_type: 'course',
+ normalized_metadata: {
+ enroll_by_date: '2016-02-18T04:00:00Z',
+ start_date: '2016-04-18T04:00:00Z',
+ content_price: 100,
+ },
original_image_url: '',
- enterprise_catalog_query_titles: TEST_CATALOG,
- advertised_course_run: { pacing_type: 'self_paced' },
+ partners: [{ logo_image_url: '', name: 'Course Provider' }],
+ title: 'Course Title',
};
const defaultProps = {
original: originalData,
- learningType: CONTENT_TYPE_COURSE,
};
const execEdData = {
- title: 'Exec Ed Course Title',
+ availability: ['Upcoming'],
card_image_url: undefined,
- partners: [{ logo_image_url: '', name: 'Course Provider' }],
- first_enrollable_paid_seat_price: 100,
- original_image_url: '',
- enterprise_catalog_query_titles: TEST_CATALOG,
- advertised_course_run: { pacing_type: 'instructor_paced' },
+ course_type: 'executive-education-2u',
entitlements: [{ price: '999.00' }],
+ normalized_metadata: {
+ enroll_by_date: '2016-02-18T04:00:00Z',
+ start_date: '2016-04-18T04:00:00Z',
+ content_price: 999,
+ },
+ original_image_url: '',
+ partners: [{ logo_image_url: '', name: 'Course Provider' }],
+ title: 'Exec Ed Title',
};
const execEdProps = {
original: execEdData,
- learningType: EXEC_ED_TITLE,
};
describe('Course card works as expected', () => {
- test('card renders as expected', () => {
+ test('course card renders', () => {
render(
@@ -54,21 +53,14 @@ describe('Course card works as expected', () => {
expect(
screen.queryByText(defaultProps.original.partners[0].name),
).toBeInTheDocument();
- expect(screen.queryByText('Course Title')).toBeInTheDocument();
+ expect(screen.queryByText('$100')).toBeInTheDocument();
expect(screen.queryByText('Per learner price')).toBeInTheDocument();
+ expect(screen.queryByText('Upcoming • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
+ expect(screen.queryByText('Course')).toBeInTheDocument();
+ expect(screen.queryByText('View course')).toBeInTheDocument();
+ expect(screen.queryByText('Assign')).toBeInTheDocument();
});
- test('exec ed card renders as expected', () => {
- render(
-
-
- ,
- );
- expect(screen.queryByText(execEdProps.original.title)).toBeInTheDocument();
- expect(
- screen.queryByText(execEdProps.original.partners[0].name),
- ).toBeInTheDocument();
- expect(screen.queryByText('Exec Ed Course Title')).toBeInTheDocument();
- });
+
test('test card renders default image', async () => {
render(
@@ -79,4 +71,15 @@ describe('Course card works as expected', () => {
fireEvent.error(screen.getByAltText(imageAltText));
await expect(screen.getByAltText(imageAltText).src).not.toBeUndefined;
});
+
+ test('exec ed card renders', async () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.queryByText('$999')).toBeInTheDocument();
+ expect(screen.queryByText('Starts Apr 18, 2016 • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).toBeInTheDocument();
+ });
});
diff --git a/src/components/learner-credit-management/constants.js b/src/components/learner-credit-management/constants.js
new file mode 100644
index 0000000000..7148a852d2
--- /dev/null
+++ b/src/components/learner-credit-management/constants.js
@@ -0,0 +1,18 @@
+const CARD_TEXT = {
+ BADGE: {
+ course: 'Course',
+ execEd: 'Executive Education',
+ },
+ BUTTON_ACTION: {
+ viewCourse: 'View course',
+ assign: 'Assign',
+ },
+ ENROLLMENT: {
+ text: 'Learner must enroll by',
+ },
+ PRICE: {
+ subText: 'Per learner price',
+ },
+};
+
+export default CARD_TEXT;
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 8a93ee6f6d..08ad16ef5d 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -32,12 +32,18 @@ export const LANGUAGE_REFINEMENT = 'language';
// Learning types
export const CONTENT_TYPE_COURSE = 'course';
export const EXEC_ED_TITLE = 'Executive Education';
-export const EXEC_COURSE_TYPE = 'executive-education-2u';
+export const EXEC_ED_COURSE_TYPE = 'executive-education-2u';
+
+// Learner must enroll within 90 days of assignment
+export const ASSIGNMENT_ENROLLMENT_DEADLINE = 90;
// Number of items to display per page in Budget Detail assignment/spend tables
export const PAGE_SIZE = 25;
export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
+// Number of items to display per page in Budget Catalog tab
+export const SEARCH_RESULT_PAGE_SIZE = 15;
+
// Query Key factory for the learner credit management module, intended to be used with `@tanstack/react-query`.
// Inspired by https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories.
export const learnerCreditManagementQueryKeys = {
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 9874c22f4b..6a5a45888c 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -5,6 +5,7 @@ import { camelCaseObject } from '@edx/frontend-platform';
import {
LOW_REMAINING_BALANCE_PERCENT_THRESHOLD,
NO_BALANCE_REMAINING_DOLLAR_THRESHOLD,
+ ASSIGNMENT_ENROLLMENT_DEADLINE,
} from './constants';
import { BUDGET_STATUSES } from '../../EnterpriseApp/data/constants';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
@@ -199,6 +200,16 @@ export function formatDate(date) {
return dayjs(date).format('MMM D, YYYY');
}
+// Exec ed and open courses cards should display either the enrollment deadline
+// or 90 days from the present date on user pageload, whichever is sooner.
+export function getEnrollmentDeadline(enrollByDate) {
+ const courseEnrollByDate = dayjs(enrollByDate);
+ const assignmentEnrollmentDeadline = dayjs().add(ASSIGNMENT_ENROLLMENT_DEADLINE, 'days');
+
+ return courseEnrollByDate <= assignmentEnrollmentDeadline
+ ? formatDate(courseEnrollByDate)
+ : formatDate(assignmentEnrollmentDeadline);
+}
/**
* Retrieves content assignments for the given budget's assignment configuration UUID (retrieved from the associated
* subsidy access policy).
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
index eb68ef0a21..43786e8a0b 100644
--- a/src/components/learner-credit-management/index.jsx
+++ b/src/components/learner-credit-management/index.jsx
@@ -2,7 +2,6 @@ import React from 'react';
import { Route } from 'react-router-dom';
import PropTypes from 'prop-types';
import MultipleBudgetsPage from './MultipleBudgetsPage';
-import './learner-credit.scss';
import BudgetDetailPage from './BudgetDetailPage';
const LearnerCreditManagementRoutes = ({ match }) => (
diff --git a/src/components/learner-credit-management/learner-credit.scss b/src/components/learner-credit-management/learner-credit.scss
deleted file mode 100644
index c26c3e9859..0000000000
--- a/src/components/learner-credit-management/learner-credit.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-.card-container {
- display: flex;
- padding: 1rem;
- flex-grow: 1;
- justify-content: space-between;
-
- .section-1 {
- flex-direction: column;
- }
- .section-2 {
- margin-left: 0;
- text-align: end !important;
- min-width: 400px;
- padding-right: 0;
- justify-content: space-between;
- .footer {
- justify-content: end;
- padding: 0;
- }
- }
-}
-
-.badge {
- margin: 4px;
-}
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
index c19d87dc50..d32fa3015a 100644
--- a/src/components/learner-credit-management/search/CatalogSearch.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -1,19 +1,18 @@
import React from 'react';
-import { useParams } from 'react-router-dom';
import algoliasearch from 'algoliasearch/lite';
import { Configure, InstantSearch } from 'react-instantsearch-dom';
+import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { SearchHeader } from '@edx/frontend-enterprise-catalog-search';
import { configuration } from '../../../config';
import CatalogSearchResults from './CatalogSearchResults';
+import { SEARCH_RESULT_PAGE_SIZE } from '../data';
-const CatalogSearch = () => {
- const { budgetId } = useParams();
+const CatalogSearch = ({ catalogUuid }) => {
const searchClient = algoliasearch(configuration.ALGOLIA.APP_ID, configuration.ALGOLIA.SEARCH_API_KEY);
-
- const searchFilters = `enterprise_catalog_query_uuids:${budgetId}`;
+ const searchFilters = `enterprise_catalog_uuids:${catalogUuid} AND content_type:course`;
return (
@@ -28,6 +27,7 @@ const CatalogSearch = () => {
{
);
};
+CatalogSearch.propTypes = {
+ catalogUuid: PropTypes.string.isRequired,
+};
+
export default CatalogSearch;
diff --git a/src/components/learner-credit-management/search/CatalogSearchResults.jsx b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
index 5127568653..44faafff34 100644
--- a/src/components/learner-credit-management/search/CatalogSearchResults.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
@@ -1,19 +1,18 @@
-import React, { useEffect, useMemo } from 'react';
+import React, { useContext, useEffect, useMemo } from 'react';
import { connectStateResults } from 'react-instantsearch-dom';
import PropTypes from 'prop-types';
-import { SearchPagination } from '@edx/frontend-enterprise-catalog-search';
+import { SearchPagination, SearchContext } from '@edx/frontend-enterprise-catalog-search';
import { FormattedMessage, injectIntl } from '@edx/frontend-platform/i18n';
import {
- Alert, CardView, DataTable, Skeleton,
+ Alert, CardView, DataTable, TextFilter,
} from '@edx/paragon';
import CourseCard from '../cards/CourseCard';
+import { SEARCH_RESULT_PAGE_SIZE } from '../data';
export const ERROR_MESSAGE = 'An error occurred while retrieving data';
-export const SKELETON_DATA_TESTID = 'enterprise-catalog-skeleton';
-
/**
* The core search results rendering component.
*
@@ -27,8 +26,10 @@ export const SKELETON_DATA_TESTID = 'enterprise-catalog-skeleton';
export const BaseCatalogSearchResults = ({
searchResults,
+ searchState,
// algolia recommends this prop instead of searching
isSearchStalled,
+ paginationComponent: PaginationComponent,
error,
setNoContent,
}) => {
@@ -58,20 +59,13 @@ export const BaseCatalogSearchResults = ({
() => searchResults?.hits || [],
[searchResults?.hits],
);
-
- const renderCardComponent = (props) => ;
+ const { refinements } = useContext(SearchContext);
+ const page = refinements.page || (searchState.page || 0);
useEffect(() => {
setNoContent(searchResults === null || searchResults?.nbHits === 0);
}, [searchResults, setNoContent]);
- if (isSearchStalled) {
- return (
-
-
-
- );
- }
if (error) {
return (
@@ -88,21 +82,29 @@ export const BaseCatalogSearchResults = ({
return (
renderCardComponent(props)}
+ CardComponent={CourseCard}
/>
-
+
+
+
);
@@ -112,7 +114,6 @@ BaseCatalogSearchResults.defaultProps = {
searchResults: { disjunctiveFacetsRefinements: [], nbHits: 0, hits: [] },
error: null,
paginationComponent: SearchPagination,
- preview: false,
setNoContent: () => {},
};
@@ -137,7 +138,6 @@ BaseCatalogSearchResults.propTypes = {
page: PropTypes.number,
}).isRequired,
paginationComponent: PropTypes.func,
- preview: PropTypes.bool,
setNoContent: PropTypes.func,
};
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
index 34a75e3ac0..3cbd296449 100644
--- a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
@@ -5,9 +5,7 @@ import '@testing-library/jest-dom/extend-expect';
import { SearchContext } from '@edx/frontend-enterprise-catalog-search';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { BaseCatalogSearchResults, SKELETON_DATA_TESTID } from '../search/CatalogSearchResults';
-
-import { renderWithRouter } from '../../test/testUtils';
+import { BaseCatalogSearchResults } from '../search/CatalogSearchResults';
import { CONTENT_TYPE_COURSE } from '../data/constants';
@@ -141,14 +139,4 @@ describe('Main Catalogs view works as expected', () => {
expect(screen.queryByText(TEST_COURSE_NAME_2)).toBeInTheDocument();
expect(screen.getAllByText('Showing 2 of 2.')[0]).toBeInTheDocument();
});
- test('isSearchStalled leads to rendering skeleton and not content', () => {
- renderWithRouter(
-
-
- ,
- );
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
- expect(screen.queryByText(TEST_COURSE_NAME)).not.toBeInTheDocument();
- expect(screen.getByTestId(SKELETON_DATA_TESTID)).toBeInTheDocument();
- });
});
From 7ef63fec337dde2801cd65496ae0efef61064411 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Mon, 30 Oct 2023 11:16:49 -0400
Subject: [PATCH 049/124] chore: remove unused LCM components and one unused
NPM package (#1069)
---
package-lock.json | 13 --
package.json | 1 -
src/components/TextAreaAutoSize/index.jsx | 1 +
.../BudgetCard-V2.jsx | 80 -------
.../learner-credit-management/BudgetCard.jsx | 195 ++++------------
.../LearnerCreditManagement.jsx | 121 ----------
.../MultipleBudgetsPicker.jsx | 2 +-
.../OfferNameHeading.jsx | 18 --
.../tests/BudgetCard.test.jsx | 2 +-
.../tests/LearnerCreditManagement.test.jsx | 214 ------------------
.../tests/OfferNameHeading.test.jsx | 20 --
11 files changed, 51 insertions(+), 616 deletions(-)
delete mode 100644 src/components/learner-credit-management/BudgetCard-V2.jsx
delete mode 100644 src/components/learner-credit-management/LearnerCreditManagement.jsx
delete mode 100644 src/components/learner-credit-management/OfferNameHeading.jsx
delete mode 100644 src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx
delete mode 100644 src/components/learner-credit-management/tests/OfferNameHeading.test.jsx
diff --git a/package-lock.json b/package-lock.json
index 0a62f3ad47..86dab080e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,7 +44,6 @@
"react-redux": "7.1.1",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
- "react-textarea-autosize": "7.1.2",
"react-truncate": "^2.4.0",
"redux": "4.0.4",
"redux-devtools-extension": "2.13.8",
@@ -19392,18 +19391,6 @@
"object-assign": "^4.1.1"
}
},
- "node_modules/react-textarea-autosize": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz",
- "integrity": "sha512-uH3ORCsCa3C6LHxExExhF4jHoXYCQwE5oECmrRsunlspaDAbS4mGKNlWZqjLfInWtFQcf0o1n1jC/NGXFdUBCg==",
- "dependencies": {
- "@babel/runtime": "^7.1.2",
- "prop-types": "^15.6.0"
- },
- "peerDependencies": {
- "react": ">=0.14.0 <17.0.0"
- }
- },
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
diff --git a/package.json b/package.json
index d042436e6c..9cca08b084 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,6 @@
"react-redux": "7.1.1",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
- "react-textarea-autosize": "7.1.2",
"react-truncate": "^2.4.0",
"redux": "4.0.4",
"redux-devtools-extension": "2.13.8",
diff --git a/src/components/TextAreaAutoSize/index.jsx b/src/components/TextAreaAutoSize/index.jsx
index 0963f73c7a..1d08b1cd54 100644
--- a/src/components/TextAreaAutoSize/index.jsx
+++ b/src/components/TextAreaAutoSize/index.jsx
@@ -25,6 +25,7 @@ const TextAreaAutoSize = ({
isValid={touched && !error}
isInvalid={hasError}
rows={3}
+ autoResize
data-hj-suppress
/>
{hasError && {error} }
diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx
deleted file mode 100644
index 41ae121150..0000000000
--- a/src/components/learner-credit-management/BudgetCard-V2.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { useOfferSummary } from './data';
-import SubBudgetCard from './SubBudgetCard';
-import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
-
-/**
- * Renders one or more budget cards for the given offer (enterprise or Subsidy from enterprise-subsidy). If the offer is
- * an enterprise offer, it will render a single card. If the offer is a Subsidy, it will render one card for
- * each associated budget.
- *
- * @param {*} offer Represents either an enterprise offer or a Subsidy (enterprise-subsidy).
- * @returns Budget card component(s).
- */
-const BudgetCard = ({
- offer,
- enterpriseUUID,
- enterpriseSlug,
- offerType,
- displayName,
-}) => {
- const { start, end } = offer;
-
- const {
- isLoading: isLoadingOfferSummary,
- offerSummary,
- } = useOfferSummary(enterpriseUUID, offer);
-
- // Enterprise Offers will always have a single budget, so we can render a single card.
- if (offerType === BUDGET_TYPES.ecommerce) {
- return (
-
- );
- }
-
- // We're now dealing with a Subsidy (enterprise-subsidy), but the analytics API isn't aware of any
- // associated budgets; nothing should display.
- if (!offerSummary?.budgetsSummary) {
- return null;
- }
-
- // Render a card for each associated budget with the Subsidy (enterprise-subsidy)
- return offerSummary.budgetsSummary.map((budget) => (
-
- ));
-};
-
-BudgetCard.propTypes = {
- offer: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- start: PropTypes.string.isRequired,
- end: PropTypes.string.isRequired,
- }).isRequired,
- enterpriseUUID: PropTypes.string.isRequired,
- enterpriseSlug: PropTypes.string.isRequired,
- offerType: PropTypes.oneOf(Object.values(BUDGET_TYPES)).isRequired,
- displayName: PropTypes.string,
-};
-
-export default BudgetCard;
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index caef8e13c4..41ae121150 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -1,168 +1,67 @@
-import React, { useState } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
-import dayjs from 'dayjs';
-import {
- Card,
- Button,
- Stack,
- Row,
- Col,
- Breadcrumb,
-} from '@edx/paragon';
-
-import { getCourseProductLineAbbreviation } from '../../utils';
-import { useOfferRedemptions, useOfferSummary } from './data';
-import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
-import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
-import { EXEC_ED_OFFER_TYPE } from './data/constants';
+import { useOfferSummary } from './data';
+import SubBudgetCard from './SubBudgetCard';
+import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
+/**
+ * Renders one or more budget cards for the given offer (enterprise or Subsidy from enterprise-subsidy). If the offer is
+ * an enterprise offer, it will render a single card. If the offer is a Subsidy, it will render one card for
+ * each associated budget.
+ *
+ * @param {*} offer Represents either an enterprise offer or a Subsidy (enterprise-subsidy).
+ * @returns Budget card component(s).
+ */
const BudgetCard = ({
offer,
enterpriseUUID,
enterpriseSlug,
+ offerType,
+ displayName,
}) => {
- const {
- start,
- end,
- } = offer;
+ const { start, end } = offer;
const {
isLoading: isLoadingOfferSummary,
offerSummary,
} = useOfferSummary(enterpriseUUID, offer);
- const {
- isLoading: isLoadingOfferRedemptions,
- offerRedemptions,
- fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, offer?.id);
- const [detailPage, setDetailPage] = useState(false);
- const [activeLabel, setActiveLabel] = useState('');
- const links = [
- { label: 'Budgets', url: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` },
- ];
- const formattedStartDate = dayjs(start).format('MMMM D, YYYY');
- const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY');
- const navigateToBudgetRedemptions = (budgetType) => {
- setDetailPage(true);
- links.push({ label: budgetType, url: `/${enterpriseSlug}/admin/learner-credit` });
- setActiveLabel(budgetType);
- };
-
- const renderActions = (budgetType) => (
- navigateToBudgetRedemptions(budgetType)}
- >
- View Budget
-
- );
-
- const renderCardHeader = (budgetType) => {
- const subtitle = (
-
-
- {formattedStartDate} - {formattedExpirationDate}
-
-
- );
-
+ // Enterprise Offers will always have a single budget, so we can render a single card.
+ if (offerType === BUDGET_TYPES.ecommerce) {
return (
-
- {renderActions(budgetType)}
-
- )}
+
);
- };
-
- const renderCardSection = (available, spent) => (
-
-
-
- Available
- {available}
-
-
- Spent
- {spent}
-
-
-
- );
+ }
- const renderCardAggregate = () => (
-
-
-
- );
+ // We're now dealing with a Subsidy (enterprise-subsidy), but the analytics API isn't aware of any
+ // associated budgets; nothing should display.
+ if (!offerSummary?.budgetsSummary) {
+ return null;
+ }
- return (
-
-
-
-
-
-
- {!detailPage
- ? (
- <>
- {renderCardAggregate()}
- Budgets
-
-
-
- {renderCardHeader('Open Courses Marketplace')}
- {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFundsOcm)}
-
-
-
- {offerSummary?.offerType === EXEC_ED_OFFER_TYPE
- && (
-
-
-
- {renderCardHeader('Executive Education')}
- {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFundsExecEd)}
-
-
-
- )}
- >
- )
- : (
-
- )}
-
- );
+ // Render a card for each associated budget with the Subsidy (enterprise-subsidy)
+ return offerSummary.budgetsSummary.map((budget) => (
+
+ ));
};
BudgetCard.propTypes = {
@@ -174,6 +73,8 @@ BudgetCard.propTypes = {
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
+ offerType: PropTypes.oneOf(Object.values(BUDGET_TYPES)).isRequired,
+ displayName: PropTypes.string,
};
export default BudgetCard;
diff --git a/src/components/learner-credit-management/LearnerCreditManagement.jsx b/src/components/learner-credit-management/LearnerCreditManagement.jsx
deleted file mode 100644
index 4786f3e72f..0000000000
--- a/src/components/learner-credit-management/LearnerCreditManagement.jsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import React, {
- useContext, useEffect,
-} from 'react';
-import PropTypes from 'prop-types';
-import Helmet from 'react-helmet';
-import { connect } from 'react-redux';
-import dayjs from 'dayjs';
-import {
- Badge,
- Container,
- Stack,
- Skeleton,
-} from '@edx/paragon';
-import { logError } from '@edx/frontend-platform/logging';
-
-import Hero from '../Hero';
-import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
-import { NotFound } from '../NotFoundPage';
-import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import LearnerCreditAggregateCards from './LearnerCreditAggregateCards';
-import LearnerCreditDisclaimer from './LearnerCreditDisclaimer';
-import OfferDates from './OfferDates';
-import OfferNameHeading from './OfferNameHeading';
-import { useOfferSummary, useOfferRedemptions } from './data';
-import { DATE_FORMAT } from './data/constants';
-import OfferUtilizationAlerts from './OfferUtilizationAlerts';
-
-const LearnerCreditManagement = ({ enterpriseUUID }) => {
- const { offers } = useContext(EnterpriseSubsidiesContext);
- const enterpriseOffer = offers[0];
-
- const {
- isLoading: isLoadingOfferSummary,
- offerSummary,
- } = useOfferSummary(enterpriseUUID, enterpriseOffer);
- const {
- isLoading: isLoadingOfferRedemptions,
- offerRedemptions,
- fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, enterpriseOffer?.id);
-
- /**
- * Log error only once when no offer exists.
- */
- useEffect(() => {
- if (offers.length === 0) {
- logError(`"Learner Credit Management" accessed with no enterprise offer configured for enterprise ${enterpriseUUID}.`);
- }
- }, [offers, enterpriseUUID]);
-
- if (!enterpriseOffer) {
- return ;
- }
-
- // The LPR data is synced once per day, and all its data is fresh, meaning we can
- // deduce when the data was last updated based on when any of the offer redemptions
- // records were created.
- const offerDataLastUpdatedTimestamp = offerRedemptions.results[0]?.created;
- return (
- <>
-
- Learner Credit Management
-
-
-
-
-
-
-
- {enterpriseOffer.isCurrent ? (
- Active
- ) : (
- Ended
- )}
-
-
-
- {isLoadingOfferSummary || isLoadingOfferRedemptions ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
-
- >
- );
-};
-
-const mapStateToProps = state => ({
- enterpriseUUID: state.portalConfiguration.enterpriseId,
-});
-
-LearnerCreditManagement.propTypes = {
- enterpriseUUID: PropTypes.string.isRequired,
-};
-
-export default connect(mapStateToProps)(LearnerCreditManagement);
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index 44407531c0..b8220c4768 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -6,7 +6,7 @@ import {
Col,
} from '@edx/paragon';
-import BudgetCard from './BudgetCard-V2';
+import BudgetCard from './BudgetCard';
import { orderOffers } from './data/utils';
const MultipleBudgetsPicker = ({
diff --git a/src/components/learner-credit-management/OfferNameHeading.jsx b/src/components/learner-credit-management/OfferNameHeading.jsx
deleted file mode 100644
index 09af054c4b..0000000000
--- a/src/components/learner-credit-management/OfferNameHeading.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-const OfferNameHeading = ({ name }) => (
-
- {name}
-
-);
-
-OfferNameHeading.propTypes = {
- name: PropTypes.string,
-};
-
-OfferNameHeading.defaultProps = {
- name: 'Overview',
-};
-
-export default OfferNameHeading;
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index a2bc074944..7ddc100c8e 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -12,7 +12,7 @@ import {
import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import BudgetCard from '../BudgetCard-V2';
+import BudgetCard from '../BudgetCard';
import { useOfferSummary, useOfferRedemptions } from '../data';
import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
diff --git a/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx
deleted file mode 100644
index f268b2faf7..0000000000
--- a/src/components/learner-credit-management/tests/LearnerCreditManagement.test.jsx
+++ /dev/null
@@ -1,214 +0,0 @@
-/* eslint-disable react/prop-types */
-import React from 'react';
-import { Provider } from 'react-redux';
-import thunk from 'redux-thunk';
-import configureMockStore from 'redux-mock-store';
-import dayjs from 'dayjs';
-import {
- screen,
- render,
-} from '@testing-library/react';
-import '@testing-library/jest-dom/extend-expect';
-
-import LearnerCreditManagement from '../LearnerCreditManagement';
-import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
-import { DATE_FORMAT } from '../data/constants';
-import { useOfferSummary, useOfferRedemptions } from '../data/hooks';
-
-jest.mock('../data/hooks');
-useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: null,
-});
-useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
- },
- fetchOfferRedemptions: jest.fn(),
-});
-
-jest.mock('../../NotFoundPage', () => ({
- __esModule: true,
- NotFound: () => ,
-}));
-
-jest.mock('../OfferNameHeading', () => ({
- __esModule: true,
- default: ({ name }) => {name} ,
-}));
-
-jest.mock('../OfferDates', () => ({
- __esModule: true,
- default: ({ start, end }) => (
- <>
- {start}
- {end}
- >
- ),
-}));
-
-jest.mock('../LearnerCreditAllocationTable', () => ({
- __esModule: true,
- default: ({
- enterpriseUUID, isLoading, tableData, fetchTableData,
- }) => (
- <>
- {isLoading ? 'is loading' : 'is NOT loading'}
- {tableData?.results[0]?.enterpriseEnrollmentId}
- {typeof fetchTableData}
- {enterpriseUUID}
- >
- ),
-}));
-
-jest.mock('../LearnerCreditAggregateCards', () => ({
- __esModule: true,
- default: ({
- isLoading, totalFunds, redeemedFunds, remainingFunds, percentUtilized,
- }) => (
- <>
- {isLoading ? 'is loading' : 'is NOT loading'}
- {totalFunds}
- {redeemedFunds}
- {remainingFunds}
- {percentUtilized}
- >
- ),
-}));
-
-const mockStore = configureMockStore([thunk]);
-const getMockStore = store => mockStore(store);
-const enterpriseId = 'test-enterprise';
-const initialStore = {
- portalConfiguration: {
- enterpriseId,
- },
-};
-const store = getMockStore({ ...initialStore });
-
-const mockEnterpriseOfferId = 123;
-const mockEnterpriseOfferEnrollmentId = 456;
-const defaultEnterpriseSubsidiesContextValue = {
- offers: [],
-};
-
-const mockOfferDisplayName = 'Test Enterprise Offer';
-const mockOfferSummary = {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
-};
-
-const LearnerCreditManagementWrapper = ({
- enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
- ...rest
-}) => (
-
-
-
-
-
-);
-
-describe(' ', () => {
- it('displays not found page with no enterprise offer', () => {
- render( );
- expect(screen.getByTestId('404-page-not-found'));
- });
-
- describe('with enterprise offer', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('displays correctly', () => {
- const mockOffer = {
- id: mockEnterpriseOfferId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- };
- const mockOfferRedemption = {
- created: '2022-02-01',
- enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
- userEmail: 'test@example.com',
- courseTitle: 'edX Demonstration Course',
- courseListPrice: 100,
- enrollmentDate: '2022-01-01',
- uuid: '123abc-abc123',
- };
- const subsidiesContextValue = {
- offers: [mockOffer],
- };
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: mockOfferSummary,
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- results: [mockOfferRedemption],
- itemCount: 1,
- pageCount: 1,
- },
- fetchOfferRedemptions: jest.fn(),
- });
- render( );
- expect(screen.queryByTestId('404-page-not-found')).toBeFalsy();
- expect(screen.getByText('Learner Credit Management'));
- expect(screen.getByText(mockOffer.name));
-
- expect(screen.getByText(mockOffer.start));
- expect(screen.getByText(mockOffer.end));
-
- expect(screen.getByText(`Data last updated on ${dayjs(mockOfferRedemption.created).format(DATE_FORMAT)}`, { exact: false }));
-
- expect(screen.getByTestId('learner-credit-allocation--is-loading')).toHaveTextContent('is NOT loading');
- expect(screen.getByTestId('learner-credit-allocation--table-data')).toHaveTextContent(mockOfferRedemption.enterpriseEnrollmentId);
- expect(screen.getByTestId('learner-credit-allocation--fetch-table-data')).toHaveTextContent('function');
- expect(screen.getByTestId('learner-credit-allocation--enterprise-uuid')).toHaveTextContent(enterpriseId);
-
- expect(screen.getByTestId('learner-credit-aggregate-cards--loading')).toHaveTextContent('is NOT loading');
- expect(screen.getByTestId('learner-credit-aggregate-cards--total-funds')).toHaveTextContent('5000');
- expect(screen.getByTestId('learner-credit-aggregate-cards--redeemed-funds')).toHaveTextContent('200');
- expect(screen.getByTestId('learner-credit-aggregate-cards--remaining-funds')).toHaveTextContent('4800');
- expect(screen.getByTestId('learner-credit-aggregate-cards--percent-utilized')).toHaveTextContent('0.04');
- });
-
- describe('status badge', () => {
- it('with non-current offer', () => {
- const subsidiesContextValue = {
- offers: [{
- id: mockEnterpriseOfferId,
- isCurrent: false,
- }],
- };
- useOfferSummary.mockReturnValue = {
- isLoading: false,
- offerSummary: mockOfferSummary,
- };
- render( );
- expect(screen.getByText('Ended'));
- });
-
- it('with current offer', () => {
- const subsidiesContextValue = {
- offers: [{
- id: mockEnterpriseOfferId,
- isCurrent: true,
- }],
- };
- useOfferSummary.mockReturnValue = {
- isLoading: false,
- offerSummary: mockOfferSummary,
- };
- render( );
- expect(screen.getByText('Active'));
- });
- });
- });
-});
diff --git a/src/components/learner-credit-management/tests/OfferNameHeading.test.jsx b/src/components/learner-credit-management/tests/OfferNameHeading.test.jsx
deleted file mode 100644
index 913d92bb8d..0000000000
--- a/src/components/learner-credit-management/tests/OfferNameHeading.test.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react';
-import {
- screen,
- render,
-} from '@testing-library/react';
-
-import OfferNameHeading from '../OfferNameHeading';
-
-describe(' ', () => {
- it('with offer name present, display it', () => {
- const offerName = 'Test Enterprise Offer Title';
- render( );
- expect(screen.getByText(offerName));
- });
-
- it('without offer name present, fallback to "Overview"', async () => {
- render( );
- expect(screen.getByText('Overview'));
- });
-});
From 114a3601ec7800e54d4ad9337f1807250759655f Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Mon, 30 Oct 2023 12:46:51 -0400
Subject: [PATCH 050/124] feat: add status and recent action columns to
assigned table (#1071)
---
.../AssignmentRecentActionTableCell.jsx | 26 +++
.../AssignmentStatusTableCell.jsx | 63 +++++++
.../BudgetAssignmentsTable.jsx | 12 ++
.../BudgetDetailAssignments.jsx | 2 +-
.../BaseModalPopup.jsx | 82 +++++++++
.../FailedBadEmail.jsx | 63 +++++++
.../assignments-status-chips/FailedSystem.jsx | 52 ++++++
.../NotifyingLearner.jsx | 45 +++++
.../WaitingForLearner.jsx | 55 ++++++
.../data/constants.js | 3 +
.../AssignMoreCoursesEmptyStateMinimal.scss | 14 --
.../styles/index.scss | 22 +++
.../tests/BudgetDetailPage.test.jsx | 156 ++++++++++++++++++
src/index.scss | 2 +-
14 files changed, 581 insertions(+), 16 deletions(-)
create mode 100644 src/components/learner-credit-management/AssignmentRecentActionTableCell.jsx
create mode 100644 src/components/learner-credit-management/AssignmentStatusTableCell.jsx
create mode 100644 src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx
create mode 100644 src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
create mode 100644 src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
create mode 100644 src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx
create mode 100644 src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
delete mode 100644 src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss
create mode 100644 src/components/learner-credit-management/styles/index.scss
diff --git a/src/components/learner-credit-management/AssignmentRecentActionTableCell.jsx b/src/components/learner-credit-management/AssignmentRecentActionTableCell.jsx
new file mode 100644
index 0000000000..2fef6f39f2
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentRecentActionTableCell.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { formatDate } from './data';
+
+const AssignmentRecentActionTableCell = ({ row }) => {
+ const { original: { recentAction } } = row;
+ const { actionType, timestamp } = recentAction;
+ const formattedActionType = `${actionType.charAt(0).toUpperCase()}${actionType.slice(1)}`;
+ const formattedActionTimestamp = formatDate(timestamp);
+ return (
+ {formattedActionType}: {formattedActionTimestamp}
+ );
+};
+
+AssignmentRecentActionTableCell.propTypes = {
+ row: PropTypes.shape({
+ original: PropTypes.shape({
+ recentAction: PropTypes.shape({
+ actionType: PropTypes.string.isRequired,
+ timestamp: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+ }).isRequired,
+};
+
+export default AssignmentRecentActionTableCell;
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
new file mode 100644
index 0000000000..5ee9f6158e
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Chip,
+} from '@edx/paragon';
+import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
+import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
+import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
+import FailedSystem from './assignments-status-chips/FailedSystem';
+
+const AssignmentStatusTableCell = ({ row }) => {
+ const { original: { learnerEmail, learnerState } } = row;
+
+ // Learner state is not available for this assignment, so don't display anything.
+ if (!learnerState) {
+ return null;
+ }
+
+ if (learnerState === 'notifying') {
+ return (
+
+ );
+ }
+
+ if (learnerState === 'waiting') {
+ return (
+
+ );
+ }
+
+ if (learnerState === 'failed') {
+ // Determine the failure reason based on the actions.
+ const { actions } = row.original;
+ const mostRecentAction = actions[0]; // API returns actions in reverse chronological order.
+ const isBadEmailError = mostRecentAction.actionType === 'notified' && !!mostRecentAction.errorReason;
+
+ if (isBadEmailError) {
+ return (
+
+ );
+ }
+
+ return ;
+ }
+
+ // Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
+ return {`${learnerState.charAt(0).toUpperCase()}${learnerState.substr(1)}`} ;
+};
+
+AssignmentStatusTableCell.propTypes = {
+ row: PropTypes.shape({
+ original: PropTypes.shape({
+ learnerEmail: PropTypes.string,
+ learnerState: PropTypes.string.isRequired,
+ actions: PropTypes.arrayOf(PropTypes.shape({
+ actionType: PropTypes.string.isRequired,
+ errorReason: PropTypes.string,
+ })).isRequired,
+ }).isRequired,
+ }).isRequired,
+};
+
+export default AssignmentStatusTableCell;
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index cbb7596feb..1b8917ea0e 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -5,7 +5,9 @@ import { DataTable } from '@edx/paragon';
import TableTextFilter from './TableTextFilter';
import CustomDataTableEmptyState from './CustomDataTableEmptyState';
import AssignmentDetailsTableCell from './AssignmentDetailsTableCell';
+import AssignmentStatusTableCell from './AssignmentStatusTableCell';
import { DEFAULT_PAGE, PAGE_SIZE, formatPrice } from './data';
+import AssignmentRecentActionTableCell from './AssignmentRecentActionTableCell';
const FilterStatus = (rest) => ;
@@ -36,6 +38,16 @@ const BudgetAssignmentsTable = ({
Cell: ({ row }) => `-${formatPrice(row.original.contentQuantity / 100, { maximumFractionDigits: 0 })}`,
disableFilters: true,
},
+ {
+ Header: 'Status',
+ Cell: AssignmentStatusTableCell,
+ disableFilters: true,
+ },
+ {
+ Header: 'Recent action',
+ Cell: AssignmentRecentActionTableCell,
+ disableFilters: true,
+ },
]}
initialTableOptions={{
getRowId: row => row?.uuid?.toString(),
diff --git a/src/components/learner-credit-management/BudgetDetailAssignments.jsx b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
index 218ac84d5d..7e67eb5672 100644
--- a/src/components/learner-credit-management/BudgetDetailAssignments.jsx
+++ b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
@@ -36,7 +36,7 @@ const BudgetDetailAssignments = ({
}
return (
-
+
Assigned
Assigned activity earmarks funds in your budget so you can't overspend. For funds to move
diff --git a/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx b/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx
new file mode 100644
index 0000000000..ba25e18b6e
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { Icon, ModalPopup } from '@edx/paragon';
+import { ASSIGNMENT_STATUS_MODAL_MAX_WIDTH } from '../data';
+
+export const BaseModalPopupHeading = ({ icon, iconClassName, children }) => (
+
+
+
+ {children}
+
+
+);
+
+export const BaseModalPopupContent = ({ children }) => (
+ <>
+
+
+ {children}
+
+ >
+);
+
+const BaseModalPopup = ({
+ placement,
+ positionRef,
+ isOpen,
+ onClose,
+ children,
+ ...rest
+}) => (
+
+
+
+);
+
+BaseModalPopup.Heading = BaseModalPopupHeading;
+BaseModalPopup.Content = BaseModalPopupContent;
+
+BaseModalPopupHeading.propTypes = {
+ icon: PropTypes.elementType.isRequired,
+ iconClassName: PropTypes.string,
+ children: PropTypes.node.isRequired,
+};
+
+BaseModalPopupHeading.defaultProps = {
+ iconClassName: undefined,
+};
+
+BaseModalPopupContent.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+BaseModalPopup.propTypes = {
+ placement: PropTypes.string,
+ positionRef: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
+ ]),
+ isOpen: PropTypes.bool.isRequired,
+ onClose: PropTypes.func.isRequired,
+ children: PropTypes.node.isRequired,
+};
+
+BaseModalPopup.defaultProps = {
+ placement: 'auto',
+ positionRef: null,
+};
+
+export default BaseModalPopup;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
new file mode 100644
index 0000000000..22228de922
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
@@ -0,0 +1,63 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
+import BaseModalPopup from './BaseModalPopup';
+
+const FailedBadEmail = ({ learnerEmail }) => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Failed: Bad email
+
+
+
+ Failed: Bad email
+
+
+
+ This course assignment failed because a notification to {learnerEmail || 'the learner'} could not be sent.
+
+
+
Resolution steps
+
+
+ Cancel this assignment to release the associated Learner Credit funds into your available balance.
+
+
+ Get more troubleshooting help at{' '}
+
+ Help Center: Course Assignments
+ .
+
+
+
+
+
+ >
+ );
+};
+
+FailedBadEmail.propTypes = {
+ learnerEmail: PropTypes.string,
+};
+
+FailedBadEmail.defaultProps = {
+ learnerEmail: undefined,
+};
+
+export default FailedBadEmail;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
new file mode 100644
index 0000000000..cfee2cb7bb
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
@@ -0,0 +1,52 @@
+import React, { useState } from 'react';
+import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
+import BaseModalPopup from './BaseModalPopup';
+
+const FailedSystem = () => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Failed: System
+
+
+
+ Failed: System
+
+
+ Something went wrong behind the scenes.
+
+
Resolution steps
+
+
+ Cancel this assignment to release the associated Learner Credit funds into your available balance.
+
+
+ Get more troubleshooting help at{' '}
+
+ Help Center: Course Assignments
+ .
+
+
+
+
+
+ >
+ );
+};
+
+export default FailedSystem;
diff --git a/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx b/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx
new file mode 100644
index 0000000000..2380fda362
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx
@@ -0,0 +1,45 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { Chip, useToggle } from '@edx/paragon';
+import { Send } from '@edx/paragon/icons';
+import BaseModalPopup from './BaseModalPopup';
+
+const NotifyingLearner = ({ learnerEmail }) => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Notifying learner
+
+
+
+ Notifying {learnerEmail ?? 'learner'}
+
+
+
+ Our system is busy emailing {learnerEmail ?? 'the learner'}! Refresh in a few minutes to
+ confirm the assignment notification was successful.
+
+
+
+ >
+ );
+};
+
+NotifyingLearner.propTypes = {
+ learnerEmail: PropTypes.string,
+};
+
+export default NotifyingLearner;
diff --git a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
new file mode 100644
index 0000000000..1930262097
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
@@ -0,0 +1,55 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import { Timelapse } from '@edx/paragon/icons';
+
+import BaseModalPopup from './BaseModalPopup';
+
+const WaitingForLearner = ({ learnerEmail }) => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Waiting for learner
+
+
+
+ Waiting for {learnerEmail ?? 'learner'}
+
+
+
+ This learner must create an edX account and complete enrollment in the course before the
+ enrollment deadline or within 90 days of assignment, whichever is sooner.
+
+
+
Need help?
+
+ Learn more about learner enrollment in assigned courses at{' '}
+
+ Help Center: Course Assignments
+ .
+
+
+
+
+ >
+ );
+};
+
+WaitingForLearner.propTypes = {
+ learnerEmail: PropTypes.string,
+};
+
+export default WaitingForLearner;
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 08ad16ef5d..f448267938 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -44,6 +44,9 @@ export const DEFAULT_PAGE = 0; // `DataTable` uses zero-index array
// Number of items to display per page in Budget Catalog tab
export const SEARCH_RESULT_PAGE_SIZE = 15;
+// Max width of Assigned table status column's modalpopup dialog; matches `Popover`.
+export const ASSIGNMENT_STATUS_MODAL_MAX_WIDTH = 480;
+
// Query Key factory for the learner credit management module, intended to be used with `@tanstack/react-query`.
// Inspired by https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories.
export const learnerCreditManagementQueryKeys = {
diff --git a/src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss b/src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss
deleted file mode 100644
index 8a1e4db6e7..0000000000
--- a/src/components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-// The `Card` component in Paragon does not seem to properly let consumers customize the width of the `Card.Body`
-// contents when in the horizontal card orientation without custom CSS. As a result, both the `Card.Footer` and
-// `Card.Body` incorrectly get equal column widths when the preference is that the `Card.Body` has more width than
-// the `Card.Footer`. The below styles force the `Card.Body` to have appropriately more width than the `Card.Footer` when
-// the `Card` is in the horizontal orientation.
-
-.assign-more-courses-empty-state-minimal {
- .assign-more-courses__card-body {
- flex: 3;
- }
- .assign-more-courses__card-footer {
- flex: 1;
- }
-}
diff --git a/src/components/learner-credit-management/styles/index.scss b/src/components/learner-credit-management/styles/index.scss
new file mode 100644
index 0000000000..c3301e7af2
--- /dev/null
+++ b/src/components/learner-credit-management/styles/index.scss
@@ -0,0 +1,22 @@
+.budget-detail-assignments {
+ // This is a (temporary) workaround to ensure the `Chip` modal popups within the "Assigned" table status column
+ // properly overlays the underlying `DataTable`.
+ .pgn__data-table-container,
+ .pgn__data-table-layout-wrapper {
+ overflow-x: visible;
+ }
+
+ // The `Card` component in Paragon does not seem to properly let consumers customize the width of the `Card.Body`
+ // contents when in the horizontal card orientation without custom CSS. As a result, both the `Card.Footer` and
+ // `Card.Body` incorrectly get equal column widths when the preference is that the `Card.Body` has more width than
+ // the `Card.Footer`. The below styles force the `Card.Body` to have appropriately more width than the `Card.Footer` when
+ // the `Card` is in the horizontal orientation.
+ .assign-more-courses-empty-state-minimal {
+ .assign-more-courses__card-body {
+ flex: 3;
+ }
+ .assign-more-courses__card-footer {
+ flex: 1;
+ }
+ }
+}
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index fd87153c21..4264b686f4 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -18,6 +18,7 @@ import {
useBudgetContentAssignments,
useBudgetDetailActivityOverview,
useIsLargeOrGreater,
+ formatDate,
} from '../data';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
import {
@@ -59,6 +60,7 @@ const initialStoreState = {
const mockLearnerEmail = 'edx@example.com';
const mockCourseKey = 'edX+DemoX';
const mockContentTitle = 'edx Demo';
+
const mockEmptyStateBudgetDetailActivityOverview = {
contentAssignments: { count: 0 },
spentTransactions: { count: 0 },
@@ -68,6 +70,24 @@ const mockEmptyOfferRedemptions = {
pageCount: 0,
results: [],
};
+const mockSuccessfulNotifiedAction = {
+ uuid: 'test-assignment-action-uuid',
+ actionType: 'notified',
+ completedAt: '2023-10-27',
+ errorReason: null,
+};
+
+const mockFailedNotifiedAction = {
+ ...mockSuccessfulNotifiedAction,
+ completedAt: null,
+ errorReason: 'bad_email',
+};
+
+const mockFailedLinkedLearnerAction = {
+ ...mockFailedNotifiedAction,
+ actionType: 'learner_linked',
+ errorReason: 'internal_api_error',
+};
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
@@ -275,6 +295,10 @@ describe(' ', () => {
learnerEmail: mockLearnerEmail,
contentKey: mockCourseKey,
contentTitle: mockContentTitle,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulNotifiedAction],
},
],
numPages: 1,
@@ -296,6 +320,9 @@ describe(' ', () => {
const viewCourseCTA = assignedSection.getByText(mockContentTitle, { selector: 'a' });
expect(viewCourseCTA).toBeInTheDocument();
expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
+ expect(assignedSection.getByText('-$199')).toBeInTheDocument();
+ expect(assignedSection.getByText('Waiting for learner')).toBeInTheDocument();
+ expect(assignedSection.getByText(`Assigned: ${formatDate('2023-10-27')}`)).toBeInTheDocument();
});
it('renders with assigned table data "View Course" hyperlink default when content title is null', () => {
@@ -323,6 +350,11 @@ describe(' ', () => {
uuid: 'test-uuid',
learnerEmail: mockLearnerEmail,
contentKey: mockCourseKey,
+ contentTitle: null,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulNotifiedAction],
},
],
numPages: 1,
@@ -346,6 +378,130 @@ describe(' ', () => {
expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
});
+ it.each([
+ {
+ learnerState: 'notifying',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Notifying learner',
+ expectedModalPopupHeading: `Notifying ${mockLearnerEmail}`,
+ expectedModalPopupContent: `Our system is busy emailing ${mockLearnerEmail}!`,
+ actions: [],
+ },
+ {
+ learnerState: 'notifying',
+ hasLearnerEmail: false,
+ expectedChipStatus: 'Notifying learner',
+ expectedModalPopupHeading: 'Notifying learner',
+ expectedModalPopupContent: 'Our system is busy emailing the learner!',
+ actions: [],
+ },
+ {
+ learnerState: 'waiting',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Waiting for learner',
+ expectedModalPopupHeading: `Waiting for ${mockLearnerEmail}`,
+ expectedModalPopupContent: 'This learner must create an edX account and complete enrollment in the course',
+ actions: [mockSuccessfulNotifiedAction],
+ },
+ {
+ learnerState: 'waiting',
+ hasLearnerEmail: false,
+ expectedChipStatus: 'Waiting for learner',
+ expectedModalPopupHeading: 'Waiting for learner',
+ expectedModalPopupContent: 'This learner must create an edX account and complete enrollment in the course',
+ actions: [mockSuccessfulNotifiedAction],
+ },
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: Bad email',
+ expectedModalPopupHeading: 'Failed: Bad email',
+ expectedModalPopupContent: `This course assignment failed because a notification to ${mockLearnerEmail} could not be sent.`,
+ actions: [mockFailedNotifiedAction],
+ },
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: false,
+ expectedChipStatus: 'Failed: Bad email',
+ expectedModalPopupHeading: 'Failed: Bad email',
+ expectedModalPopupContent: 'This course assignment failed because a notification to the learner could not be sent.',
+ actions: [mockFailedNotifiedAction],
+ },
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: System',
+ expectedModalPopupHeading: 'Failed: System',
+ expectedModalPopupContent: 'Something went wrong behind the scenes.',
+ actions: [mockFailedLinkedLearnerAction],
+ },
+ ])('renders correct status chips with assigned table data (%s)', ({
+ learnerState,
+ hasLearnerEmail,
+ expectedChipStatus,
+ expectedModalPopupHeading,
+ expectedModalPopupContent,
+ actions,
+ }) => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ learnerEmail: hasLearnerEmail ? mockLearnerEmail : null,
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState,
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions,
+ },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
+ renderWithRouter( );
+
+ // Assigned table is visible within Activity tab contents
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ if (hasLearnerEmail) {
+ expect(assignedSection.getByText(mockLearnerEmail)).toBeInTheDocument();
+ } else {
+ expect(assignedSection.getByText('Email hidden')).toBeInTheDocument();
+ }
+ const statusChip = assignedSection.getByText(expectedChipStatus);
+ expect(statusChip).toBeInTheDocument();
+ userEvent.click(statusChip);
+
+ // Modal popup is visible with expected text
+ const modalPopupContents = within(screen.getByTestId('assignment-status-modalpopup-contents'));
+ expect(modalPopupContents.getByText(expectedModalPopupHeading)).toBeInTheDocument();
+ expect(modalPopupContents.getByText(expectedModalPopupContent, { exact: false })).toBeInTheDocument();
+ });
+
it('renders with catalog tab active on initial load for assignable budgets', async () => {
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
diff --git a/src/index.scss b/src/index.scss
index dfdae9bc8e..c2bfdfb974 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -24,7 +24,7 @@ $modal-max-width: 650px;
@import "./components/BulkEnrollmentPage/BulkEnrollment";
@import "./components/Admin/Admin";
@import "./components/settings/settings";
-@import "./components/learner-credit-management/styles/AssignMoreCoursesEmptyStateMinimal";
+@import "./components/learner-credit-management/styles";
body {
overflow-x: hidden;
From a33a21a475cab404ab77883d6e24fe25710c3903 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Mon, 30 Oct 2023 15:03:11 -0400
Subject: [PATCH 051/124] fix: use top placement, not auto (#1076)
---
.../assignments-status-chips/BaseModalPopup.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx b/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx
index ba25e18b6e..23f528d443 100644
--- a/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/BaseModalPopup.jsx
@@ -75,7 +75,7 @@ BaseModalPopup.propTypes = {
};
BaseModalPopup.defaultProps = {
- placement: 'auto',
+ placement: 'top',
positionRef: null,
};
From 5d2a73667ca2eed835404bab0f77faf3e4d26d55 Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Mon, 30 Oct 2023 12:34:24 -0700
Subject: [PATCH 052/124] feat: add course/program detail to 'View Course'
button (#1065)
* feat: display search result cards in catalog tab
* fix: failing test in BudgetDetailPage
* fix: replace word register with enroll
* fix: implemented reviewer comments
* fix: lint error
* fix: lint error
* feat: added policy's catalog uuid to search filter
* feat: implement view course button to learn more
* fix: failing test
* fix: added test
* fix: added test coverage
* fix: refactored based on reviewer feedback
* fix: lint error
* fix: refactored code to include new api field and updated test
* fix: removing unused prop in test
* fix: refactored
* fix: search filters
* chore: rebase
* chore: refactored
* chore: fix lint error
* chore: refactored
* fix: updated failing test
---
.../BudgetDetailCatalogTabContents.jsx | 9 +--
.../cards/CourseCard.jsx | 26 +++++--
.../cards/CourseCard.test.jsx | 67 ++++++++++++++-----
.../search/CatalogSearch.jsx | 15 ++---
.../tests/CatalogSearch.test.jsx | 36 +++++++---
.../tests/CatalogSearchResults.test.jsx | 42 ++++++++----
6 files changed, 134 insertions(+), 61 deletions(-)
diff --git a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
index a143c1f07a..ee67ecb6de 100644
--- a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
@@ -8,17 +8,10 @@ import CatalogSearch from './search/CatalogSearch';
import {
LANGUAGE_REFINEMENT,
LEARNING_TYPE_REFINEMENT,
- useBudgetId,
- useSubsidyAccessPolicy,
} from './data';
import { configuration } from '../../config';
const BudgetDetailCatalogTabContents = () => {
- const { subsidyAccessPolicyId } = useBudgetId();
- const {
- data: subsidyAccessPolicy,
- } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
-
const language = {
attribute: LANGUAGE_REFINEMENT,
title: 'Language',
@@ -48,7 +41,7 @@ const BudgetDetailCatalogTabContents = () => {
indexName={configuration.ALGOLIA.INDEX_NAME}
searchClient={searchClient}
>
-
+
diff --git a/src/components/learner-credit-management/cards/CourseCard.jsx b/src/components/learner-credit-management/cards/CourseCard.jsx
index f4885ce433..2d98bf20eb 100644
--- a/src/components/learner-credit-management/cards/CourseCard.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.jsx
@@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */
// variables taken from algolia not in camelcase
-import React from 'react';
+import React, { useContext } from 'react';
import PropTypes from 'prop-types';
+import { AppContext } from '@edx/frontend-platform/react';
+import { connect } from 'react-redux';
import {
Badge,
@@ -21,17 +23,19 @@ import { formatPrice, formatDate, getEnrollmentDeadline } from '../data/utils';
import CARD_TEXT from '../constants';
const CourseCard = ({
- original,
+ original, enterpriseSlug,
}) => {
const {
availability,
cardImageUrl,
courseType,
+ key,
normalizedMetadata,
partners,
title,
} = camelCaseObject(original);
+ const { config: { ENTERPRISE_LEARNER_PORTAL_URL } } = useContext(AppContext);
const isSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth });
const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });
@@ -72,6 +76,13 @@ const CourseCard = ({
const isExecEd = courseType === EXEC_ED_COURSE_TYPE;
+ let linkToCourse;
+ if (isExecEd) {
+ linkToCourse = `${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/executive-education-2u/course/${key}`;
+ } else {
+ linkToCourse = `${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${key}`;
+ }
+
return (
@@ -120,6 +131,7 @@ const CourseCard = ({
};
CourseCard.propTypes = {
+ enterpriseSlug: PropTypes.string.isRequired,
original: PropTypes.shape({
availability: PropTypes.arrayOf(PropTypes.string),
cardImageUrl: PropTypes.string,
@@ -136,4 +148,8 @@ CourseCard.propTypes = {
}).isRequired,
};
-export default injectIntl(CourseCard);
+const mapStateToProps = state => ({
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+export default connect(mapStateToProps)(injectIntl(CourseCard));
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index 704a1f825c..cb9304032a 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -1,7 +1,10 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
-
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import configureMockStore from 'redux-mock-store';
+import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import CourseCard from './CourseCard';
@@ -9,6 +12,7 @@ const originalData = {
availability: ['Upcoming'],
card_image_url: undefined,
course_type: 'course',
+ key: 'course-123x',
normalized_metadata: {
enroll_by_date: '2016-02-18T04:00:00Z',
start_date: '2016-04-18T04:00:00Z',
@@ -23,10 +27,13 @@ const defaultProps = {
original: originalData,
};
+const mockLearnerPortal = 'https://enterprise.stage.edx.org';
+
const execEdData = {
availability: ['Upcoming'],
card_image_url: undefined,
course_type: 'executive-education-2u',
+ key: 'exec-ed-course-123x',
entitlements: [{ price: '999.00' }],
normalized_metadata: {
enroll_by_date: '2016-02-18T04:00:00Z',
@@ -42,13 +49,41 @@ const execEdProps = {
original: execEdData,
};
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseSlug = 'test-enterprise-slug';
+const enterpriseUUID = '1234';
+const initialStoreState = {
+ portalConfiguration: {
+ enterpriseId: enterpriseUUID,
+ enterpriseSlug,
+ },
+};
+
+const CourseCardWrapper = ({
+ initialState = initialStoreState,
+ ...rest
+}) => {
+ const store = getMockStore({ ...initialState });
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
describe('Course card works as expected', () => {
test('course card renders', () => {
- render(
-
-
- ,
- );
+ render( );
expect(screen.queryByText(defaultProps.original.title)).toBeInTheDocument();
expect(
screen.queryByText(defaultProps.original.partners[0].name),
@@ -59,27 +94,27 @@ describe('Course card works as expected', () => {
expect(screen.queryByText('Course')).toBeInTheDocument();
expect(screen.queryByText('View course')).toBeInTheDocument();
expect(screen.queryByText('Assign')).toBeInTheDocument();
+ const hyperlink = screen.getByRole('link', {
+ name: 'View course Opens in a new tab',
+ });
+ expect(hyperlink.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/course/course-123x');
});
test('test card renders default image', async () => {
- render(
-
-
- ,
- );
+ render( );
const imageAltText = `${originalData.title} course image`;
fireEvent.error(screen.getByAltText(imageAltText));
await expect(screen.getByAltText(imageAltText).src).not.toBeUndefined;
});
test('exec ed card renders', async () => {
- render(
-
-
- ,
- );
+ render( );
expect(screen.queryByText('$999')).toBeInTheDocument();
expect(screen.queryByText('Starts Apr 18, 2016 • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
expect(screen.queryByText('Executive Education')).toBeInTheDocument();
+ const hyperlink = screen.getByRole('link', {
+ name: 'View course Opens in a new tab',
+ });
+ expect(hyperlink.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/executive-education-2u/course/exec-ed-course-123x');
});
});
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
index d32fa3015a..753f6ced65 100644
--- a/src/components/learner-credit-management/search/CatalogSearch.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -1,18 +1,21 @@
import React from 'react';
import algoliasearch from 'algoliasearch/lite';
import { Configure, InstantSearch } from 'react-instantsearch-dom';
-import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { SearchHeader } from '@edx/frontend-enterprise-catalog-search';
import { configuration } from '../../../config';
import CatalogSearchResults from './CatalogSearchResults';
-import { SEARCH_RESULT_PAGE_SIZE } from '../data';
+import { SEARCH_RESULT_PAGE_SIZE, useBudgetId, useSubsidyAccessPolicy } from '../data';
-const CatalogSearch = ({ catalogUuid }) => {
+const CatalogSearch = () => {
const searchClient = algoliasearch(configuration.ALGOLIA.APP_ID, configuration.ALGOLIA.SEARCH_API_KEY);
- const searchFilters = `enterprise_catalog_uuids:${catalogUuid} AND content_type:course`;
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const {
+ data: subsidyAccessPolicy,
+ } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const searchFilters = `enterprise_catalog_uuids:${subsidyAccessPolicy.catalogUuid} AND content_type:course`;
return (
@@ -41,8 +44,4 @@ const CatalogSearch = ({ catalogUuid }) => {
);
};
-CatalogSearch.propTypes = {
- catalogUuid: PropTypes.string.isRequired,
-};
-
export default CatalogSearch;
diff --git a/src/components/learner-credit-management/tests/CatalogSearch.test.jsx b/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
index ee74751e87..ab6a0e6a01 100644
--- a/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
@@ -6,32 +6,48 @@ import {
} from '@edx/frontend-enterprise-catalog-search';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { screen } from '@testing-library/react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderWithRouter } from '../../test/testUtils';
import CatalogSearch from '../search/CatalogSearch';
+import { useBudgetId, useSubsidyAccessPolicy } from '../data';
+
jest.mock('react-instantsearch-dom', () => ({
...jest.requireActual('react-instantsearch-dom'),
InstantSearch: () => SEARCH
,
Index: () => SEARCH
,
}));
+jest.mock('../data');
+
const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
+const queryClient = new QueryClient();
const SearchDataWrapper = ({ children, searchContextValue }) => (
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
);
describe('Catalog Search component', () => {
it('properly renders component', () => {
+ useBudgetId.mockReturnValue({
+ subsidyAccessPolicyId: 'test-id',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ data: {
+ catalogUuid: '123',
+ },
+ });
renderWithRouter(
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
index 3cbd296449..503833aa57 100644
--- a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
@@ -1,6 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import configureMockStore from 'redux-mock-store';
import { SearchContext } from '@edx/frontend-enterprise-catalog-search';
import { IntlProvider } from '@edx/frontend-platform/i18n';
@@ -21,17 +24,28 @@ jest.mock('react-instantsearch-dom', () => ({
}));
const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseSlug = 'test-enterprise-slug';
+const initialStoreState = {
+ portalConfiguration: {
+ enterpriseSlug,
+ },
+};
const SearchDataWrapper = ({
-
children,
-
searchContextValue = DEFAULT_SEARCH_CONTEXT_VALUE,
-}) => (
-
- {children}
-
-);
+}) => {
+ const store = getMockStore({ ...initialStoreState });
+ return (
+
+
+ {children}
+
+
+ );
+};
const mockConfig = () => ({
EDX_FOR_BUSINESS_TITLE: 'ayylmao',
@@ -106,13 +120,13 @@ const defaultProps = {
// mock i18n requirements
intl: {
formatMessage: (header) => header.defaultMessage,
- formatDate: () => {},
- formatTime: () => {},
- formatRelative: () => {},
- formatNumber: () => {},
- formatPlural: () => {},
- formatHTMLMessage: () => {},
- now: () => {},
+ formatDate: () => { },
+ formatTime: () => { },
+ formatRelative: () => { },
+ formatNumber: () => { },
+ formatPlural: () => { },
+ formatHTMLMessage: () => { },
+ now: () => { },
},
};
From ff9bba7ec879452c4ba91e24338cc7b9d603d552 Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Tue, 31 Oct 2023 08:22:59 -0700
Subject: [PATCH 053/124] feat: add display name in heading of catalog tab
(#1074)
* feat: display search result cards in catalog tab
* fix: failing test in BudgetDetailPage
* fix: replace word register with enroll
* fix: implemented reviewer comments
* fix: lint error
* fix: lint error
* feat: added policy's catalog uuid to search filter
* feat: implement view course button to learn more
* fix: failing test
* fix: added test
* fix: added test coverage
* fix: refactored based on reviewer feedback
* fix: lint error
* fix: refactored code to include new api field and updated test
* fix: removing unused prop in test
* fix: refactored
* fix: search filters
* chore: rebase
* feat: add display name
* chore: refactored
* chore: fix lint error
* chore: refactored
* fix: refactored and updated test
---
.../learner-credit-management/search/CatalogSearch.jsx | 3 ++-
.../tests/BudgetDetailPage.test.jsx | 10 +++++++---
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
index 753f6ced65..bde723051e 100644
--- a/src/components/learner-credit-management/search/CatalogSearch.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -16,12 +16,13 @@ const CatalogSearch = () => {
data: subsidyAccessPolicy,
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const searchFilters = `enterprise_catalog_uuids:${subsidyAccessPolicy.catalogUuid} AND content_type:course`;
+ const displayName = subsidyAccessPolicy.displayName ? `${subsidyAccessPolicy.displayName} catalog` : 'Overview';
return (
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 4264b686f4..253b0768c2 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -502,23 +502,27 @@ describe(' ', () => {
expect(modalPopupContents.getByText(expectedModalPopupContent, { exact: false })).toBeInTheDocument();
});
- it('renders with catalog tab active on initial load for assignable budgets', async () => {
+ it.each([
+ { displayName: null },
+ { displayName: 'Test Budget Display Name' },
+ ])('renders with catalog tab active on initial load for assignable budgets with %s display name', ({ displayName }) => {
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'catalog',
});
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
- data: mockAssignableSubsidyAccessPolicy,
+ data: { ...mockAssignableSubsidyAccessPolicy, displayName },
});
useBudgetDetailActivityOverview.mockReturnValueOnce({
isLoading: false,
data: mockEmptyStateBudgetDetailActivityOverview,
});
renderWithRouter( );
-
+ const expectedDisplayName = displayName ? `${displayName} catalog` : 'Overview';
// Catalog tab exists and is active
expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('true');
+ expect(screen.getByText(expectedDisplayName, { selector: 'h3' }));
});
it('hides catalog tab when budget is not assignable', () => {
From 3aa2e64ae33cfab6150534b6ef2ae430b4f7c0b4 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Tue, 31 Oct 2023 15:40:53 -0400
Subject: [PATCH 054/124] feat: add "Refresh" CTA to the "Assigned" table;
remove "Product" column from "Spent" table (#1077)
---
.../AssignmentsTableRefreshAction.jsx | 28 +++++++++++++++++++
.../BudgetAssignmentsTable.jsx | 7 ++++-
.../LearnerCreditAllocationTable.jsx | 19 ++++++-------
.../search/CatalogSearchResults.jsx | 4 +--
.../tests/BudgetDetailPage.test.jsx | 22 +++++++++++++--
src/utils.js | 14 ----------
6 files changed, 64 insertions(+), 30 deletions(-)
create mode 100644 src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx
diff --git a/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx b/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx
new file mode 100644
index 0000000000..f3186d9e75
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import { Button } from '@edx/paragon';
+import PropTypes from 'prop-types';
+
+const AssignmentsTableRefreshAction = ({ tableInstance, refresh }) => {
+ const handleRefresh = () => {
+ const { state: dataTableState } = tableInstance;
+ refresh(dataTableState);
+ };
+
+ return (
+
+ Refresh
+
+ );
+};
+
+AssignmentsTableRefreshAction.propTypes = {
+ refresh: PropTypes.func.isRequired,
+ tableInstance: PropTypes.shape({
+ state: PropTypes.shape(),
+ }),
+};
+
+export default AssignmentsTableRefreshAction;
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 1b8917ea0e..301dfc99f8 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -8,6 +8,7 @@ import AssignmentDetailsTableCell from './AssignmentDetailsTableCell';
import AssignmentStatusTableCell from './AssignmentStatusTableCell';
import { DEFAULT_PAGE, PAGE_SIZE, formatPrice } from './data';
import AssignmentRecentActionTableCell from './AssignmentRecentActionTableCell';
+import AssignmentsTableRefreshAction from './AssignmentsTableRefreshAction';
const FilterStatus = (rest) => ;
@@ -35,7 +36,8 @@ const BudgetAssignmentsTable = ({
},
{
Header: 'Amount',
- Cell: ({ row }) => `-${formatPrice(row.original.contentQuantity / 100, { maximumFractionDigits: 0 })}`,
+ accessor: 'amount',
+ Cell: ({ row }) => `-${formatPrice(row.original.contentQuantity / 100)}`,
disableFilters: true,
},
{
@@ -49,6 +51,9 @@ const BudgetAssignmentsTable = ({
disableFilters: true,
},
]}
+ tableActions={[
+ ,
+ ]}
initialTableOptions={{
getRowId: row => row?.uuid?.toString(),
}}
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index e5f1309490..8b412b066a 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -1,13 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
-import dayjs from 'dayjs';
import { DataTable } from '@edx/paragon';
import TableTextFilter from './TableTextFilter';
import CustomDataTableEmptyState from './CustomDataTableEmptyState';
import SpendTableEnrollmentDetails from './SpendTableEnrollmentDetails';
-import { getCourseProductLineText } from '../../utils';
-import { PAGE_SIZE, DEFAULT_PAGE } from './data';
+import {
+ PAGE_SIZE,
+ DEFAULT_PAGE,
+ formatDate,
+ formatPrice,
+} from './data';
const FilterStatus = (rest) => ;
@@ -30,7 +33,7 @@ const LearnerCreditAllocationTable = ({
{
Header: 'Date',
accessor: 'enrollmentDate',
- Cell: ({ row }) => dayjs(row.values.enrollmentDate).format('MMM D, YYYY'),
+ Cell: ({ row }) => formatDate(row.values.enrollmentDate),
disableFilters: true,
},
{
@@ -42,13 +45,7 @@ const LearnerCreditAllocationTable = ({
{
Header: 'Amount',
accessor: 'courseListPrice',
- Cell: ({ row }) => `$${row.values.courseListPrice}`,
- disableFilters: true,
- },
- {
- Header: 'Product',
- accessor: 'courseProductLine',
- Cell: ({ row }) => getCourseProductLineText(row.values.courseProductLine),
+ Cell: ({ row }) => formatPrice(row.values.courseListPrice),
disableFilters: true,
},
]}
diff --git a/src/components/learner-credit-management/search/CatalogSearchResults.jsx b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
index 44faafff34..fdd9bde51b 100644
--- a/src/components/learner-credit-management/search/CatalogSearchResults.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
@@ -9,7 +9,7 @@ import {
} from '@edx/paragon';
import CourseCard from '../cards/CourseCard';
-import { SEARCH_RESULT_PAGE_SIZE } from '../data';
+import { DEFAULT_PAGE, SEARCH_RESULT_PAGE_SIZE } from '../data';
export const ERROR_MESSAGE = 'An error occurred while retrieving data';
@@ -87,7 +87,7 @@ export const BaseCatalogSearchResults = ({
defaultColumnValues={{ Filter: TextFilter }}
initialState={{
pageSize: SEARCH_RESULT_PAGE_SIZE,
- pageIndex: 0,
+ pageIndex: DEFAULT_PAGE,
}}
isLoading={isSearchStalled}
isPaginated
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 253b0768c2..3c5574ec22 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -19,6 +19,8 @@ import {
useBudgetDetailActivityOverview,
useIsLargeOrGreater,
formatDate,
+ DEFAULT_PAGE,
+ PAGE_SIZE,
} from '../data';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
import {
@@ -269,7 +271,7 @@ describe(' ', () => {
expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('false');
});
- it('renders with assigned table data', () => {
+ it('renders with assigned table data and handles table refresh', () => {
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
@@ -285,6 +287,7 @@ describe(' ', () => {
spentTransactions: { count: 0 },
},
});
+ const mockFetchContentAssignments = jest.fn();
useBudgetContentAssignments.mockReturnValue({
isLoading: false,
contentAssignments: {
@@ -304,7 +307,7 @@ describe(' ', () => {
numPages: 1,
currentPage: 1,
},
- fetchContentAssignments: jest.fn(),
+ fetchContentAssignments: mockFetchContentAssignments,
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
@@ -323,6 +326,21 @@ describe(' ', () => {
expect(assignedSection.getByText('-$199')).toBeInTheDocument();
expect(assignedSection.getByText('Waiting for learner')).toBeInTheDocument();
expect(assignedSection.getByText(`Assigned: ${formatDate('2023-10-27')}`)).toBeInTheDocument();
+
+ // Verify "Refresh" behavior
+ const expectedRefreshArgs = {
+ pageIndex: DEFAULT_PAGE,
+ pageSize: PAGE_SIZE,
+ filters: [],
+ sortBy: [],
+ };
+ expect(mockFetchContentAssignments).toHaveBeenCalledTimes(1); // called once on initial render
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(expect.objectContaining(expectedRefreshArgs));
+ const refreshCTA = assignedSection.getByText('Refresh', { selector: 'button' });
+ expect(refreshCTA).toBeInTheDocument();
+ userEvent.click(refreshCTA);
+ expect(mockFetchContentAssignments).toHaveBeenCalledTimes(2); // should be called again on refresh
+ expect(mockFetchContentAssignments).toHaveBeenLastCalledWith(expect.objectContaining(expectedRefreshArgs));
});
it('renders with assigned table data "View Course" hyperlink default when content title is null', () => {
diff --git a/src/utils.js b/src/utils.js
index 9582955d3c..871af1524a 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -400,18 +400,6 @@ const pollAsync = async (pollFunc, timeout, interval, checkFunc) => {
return false;
};
-const getCourseProductLineText = (courseProductLine) => {
- let courseProductLineText = '';
- courseProductLineText = courseProductLine === 'OCM' ? 'Open Courses' : courseProductLine;
- return courseProductLineText;
-};
-
-const getCourseProductLineAbbreviation = (courseProductLine) => {
- let courseProductLineText = '';
- courseProductLineText = courseProductLine === 'Open Courses Marketplace' ? 'OCM' : 'Executive Education';
- return courseProductLineText;
-};
-
export {
camelCaseDict,
camelCaseDictArray,
@@ -445,6 +433,4 @@ export {
capitalizeFirstLetter,
pollAsync,
isNotValidNumberString,
- getCourseProductLineText,
- getCourseProductLineAbbreviation,
};
From 1224e74c998c40a9e48882e1c91c7016d3bce945 Mon Sep 17 00:00:00 2001
From: Mashal Malik <107556986+Mashal-m@users.noreply.github.com>
Date: Wed, 1 Nov 2023 11:06:58 +0500
Subject: [PATCH 055/124] refactor: updated README file to reflect template
changes (#1075)
---
README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index b900a93f7b..10d490cbd5 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,11 @@
![Build Status](https://github.com/openedx/frontend-app-admin-portal/actions/workflows/ci.yml/badge.svg)
![Codecov](https://codecov.io/gh/edx/frontend-app-admin-portal/branch/master/graph/badge.svg)
-## Overview
+# Purpose
frontend-app-admin-portal is a frontend that provides branded learning experiences as well as a dashboard for enterprise learning administrators.
+# Getting Started
+
## Setting up a dev environment
### The Short Story
@@ -101,3 +103,56 @@ module.exports = {
```
NB: In order for webpack to properly resolve scss imports locally, you must use a `~` before the import, like so: `@import "~@edx/brand/paragon/fonts";`
+
+
+## Getting Help
+
+If you're having trouble, we have discussion forums at
+https://discuss.openedx.org where you can connect with others in the community.
+
+Our real-time conversations are on Slack. You can request a `Slack
+invitation`_, then join our `community Slack workspace`_. Because this is a
+frontend repository, the best place to discuss it would be in the `#wg-frontend
+channel`_.
+
+For anything non-trivial, the best path is to open an issue in this repository
+with as many details about the issue you are facing as you can provide.
+
+https://github.com/openedx/frontend-app-admin-portal/issues
+
+For more information about these options, see the `Getting Help`_ page.
+
+.. _Slack invitation: https://openedx.org/slack
+.. _community Slack workspace: https://openedx.slack.com/
+.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
+.. _Getting Help: https://openedx.org/community/connect
+
+## Contributing
+
+Contributions are very welcome. Please read `How To Contribute`_ for details.
+
+.. _How To Contribute: https://openedx.org/r/how-to-contribute
+
+This project is currently accepting all types of contributions, bug fixes,
+security fixes, maintenance work, or new features. However, please make sure
+to have a discussion about your new feature idea with the maintainers prior to
+beginning development to maximize the chances of your change being accepted.
+You can start a conversation by creating a new issue on this repo summarizing
+your idea.
+
+## The Open edX Code of Conduct
+
+All community members are expected to follow the `Open edX Code of Conduct`_.
+
+.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
+
+## License
+
+The code in this repository is licensed under the AGPLv3 unless otherwise
+noted.
+
+Please see `LICENSE `_ for details.
+
+## Reporting Security Issues
+
+Please do not report security issues in public. Please email security@openedx.org.
From 6da44f2ff148fff975b4db2c27c7ae0199f2f985 Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan <67655836+alex-sheehan-edx@users.noreply.github.com>
Date: Wed, 1 Nov 2023 10:36:11 -0400
Subject: [PATCH 056/124] feat: adding cancel and remind actions to the budget
assignment table (#1070)
Co-authored-by: Kira Miller
Co-authored-by: Kira Miller <31229189+kiram15@users.noreply.github.com>
---
.../AssignmentRowActionTableCell.jsx | 64 ++++++++++
.../AssignmentTableCancel.jsx | 21 ++++
.../AssignmentTableRemind.jsx | 29 +++++
.../BudgetAssignmentsTable.jsx | 16 ++-
.../tests/BudgetDetailPage.test.jsx | 113 ++++++++++++++++++
5 files changed, 242 insertions(+), 1 deletion(-)
create mode 100644 src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
create mode 100644 src/components/learner-credit-management/AssignmentTableCancel.jsx
create mode 100644 src/components/learner-credit-management/AssignmentTableRemind.jsx
diff --git a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
new file mode 100644
index 0000000000..1d771c27fa
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Icon,
+ IconButton,
+ OverlayTrigger,
+ Stack,
+ Tooltip,
+} from '@edx/paragon';
+import { Mail, DoNotDisturbOn } from '@edx/paragon/icons';
+
+const AssignmentRowActionTableCell = ({ row }) => {
+ const cancelButtonMarginLeft = row.original.state === 'allocated' ? 'ml-2.5' : 'ml-auto';
+ return (
+
+ {row.original.state === 'allocated' && (
+ <>
+ Remind learner}
+ >
+ console.log(`Reminding ${row.original.uuid}`)}
+ data-testid={`remind-assignment-${row.original.uuid}`}
+ />
+
+
+ >
+ )}
+ Cancel assignment}
+ >
+ console.log(`Canceling ${row.original.uuid}`)}
+ data-testid={`cancel-assignment-${row.original.uuid}`}
+ />
+
+
+ );
+};
+
+AssignmentRowActionTableCell.propTypes = {
+ row: PropTypes.shape({
+ original: PropTypes.shape({
+ uuid: PropTypes.string.isRequired,
+ state: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+};
+
+export default AssignmentRowActionTableCell;
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
new file mode 100644
index 0000000000..37c3e8ef6d
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button } from '@edx/paragon';
+import { DoNotDisturbOn } from '@edx/paragon/icons';
+
+const AssignmentTableCancelAction = ({ selectedFlatRows, ...rest }) => (
+ // eslint-disable-next-line no-console
+ console.log('Cancel', selectedFlatRows, rest)}>
+ {`Cancel (${selectedFlatRows.length})`}
+
+);
+
+AssignmentTableCancelAction.propTypes = {
+ selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
+};
+
+AssignmentTableCancelAction.defaultProps = {
+ selectedFlatRows: [],
+};
+
+export default AssignmentTableCancelAction;
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
new file mode 100644
index 0000000000..bee8cc9814
--- /dev/null
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button } from '@edx/paragon';
+import { Mail } from '@edx/paragon/icons';
+
+const AssignmentTableRemindAction = ({ selectedFlatRows, ...rest }) => {
+ const hideRemindAction = selectedFlatRows.some(
+ row => row.original.state !== 'allocated',
+ );
+ if (hideRemindAction) {
+ return null;
+ }
+ return (
+ // eslint-disable-next-line no-console
+ console.log('Remind', selectedFlatRows, rest)}>
+ {`Remind (${selectedFlatRows.length})`}
+
+ );
+};
+
+AssignmentTableRemindAction.propTypes = {
+ selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
+};
+
+AssignmentTableRemindAction.defaultProps = {
+ selectedFlatRows: [],
+};
+
+export default AssignmentTableRemindAction;
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 301dfc99f8..cfc6603800 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -1,11 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { DataTable } from '@edx/paragon';
-
import TableTextFilter from './TableTextFilter';
import CustomDataTableEmptyState from './CustomDataTableEmptyState';
import AssignmentDetailsTableCell from './AssignmentDetailsTableCell';
import AssignmentStatusTableCell from './AssignmentStatusTableCell';
+import AssignmentRowActionTableCell from './AssignmentRowActionTableCell';
+import AssignmentTableRemindAction from './AssignmentTableRemind';
+import AssignmentTableCancelAction from './AssignmentTableCancel';
import { DEFAULT_PAGE, PAGE_SIZE, formatPrice } from './data';
import AssignmentRecentActionTableCell from './AssignmentRecentActionTableCell';
import AssignmentsTableRefreshAction from './AssignmentsTableRefreshAction';
@@ -19,6 +21,7 @@ const BudgetAssignmentsTable = ({
}) => (
,
]}
@@ -68,6 +78,10 @@ const BudgetAssignmentsTable = ({
itemCount={tableData?.count || 0}
pageCount={tableData?.numPages || 1}
EmptyTableComponent={CustomDataTableEmptyState}
+ bulkActions={[
+ ,
+ ,
+ ]}
/>
);
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 3c5574ec22..462b32d46c 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -723,4 +723,117 @@ describe(' ', () => {
expect(screen.getByText('loading budget activity overview')).toBeInTheDocument();
});
+
+ it('displays remind row and bulk actions when allocated', async () => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState: 'active',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [],
+ state: 'allocated',
+ },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ renderWithRouter( );
+ const cancelRowAction = screen.getByTestId('cancel-assignment-test-uuid');
+ const remindRowAction = screen.getByTestId('remind-assignment-test-uuid');
+ expect(cancelRowAction).toBeInTheDocument();
+ expect(remindRowAction).toBeInTheDocument();
+
+ const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell');
+ expect(checkBox).toBeInTheDocument();
+ userEvent.click(checkBox);
+ await waitFor(() => {
+ expect(screen.getByText('Remind (1)')).toBeInTheDocument();
+ });
+ await waitFor(() => {
+ expect(screen.getByText('Cancel (1)')).toBeInTheDocument();
+ });
+ });
+
+ it('hides remind row and bulk actions when allocated', () => {
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState: 'accepted',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [],
+ state: 'accepted',
+ },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ renderWithRouter( );
+ expect(screen.queryByTestId('remind-assignment-test-uuid')).not.toBeInTheDocument();
+ const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell');
+ userEvent.click(checkBox);
+ expect(screen.queryByText('Remind (1)')).not.toBeInTheDocument();
+ });
});
From 17f1d7df77d0b6c42c2ca174950a901d20794625 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Wed, 1 Nov 2023 11:26:29 -0400
Subject: [PATCH 057/124] feat: add assignment modal empty state (#1078)
---
__mocks__/react-instantsearch-dom.jsx | 9 +-
.../EnterpriseApp/EnterpriseAppRoutes.jsx | 1 +
src/components/EnterpriseApp/index.jsx | 6 +-
.../BudgetDetailPage.jsx | 2 +
.../BudgetDetailTabsAndRoutes.jsx | 3 +
.../WaitingForLearner.jsx | 4 +-
.../cards/AssignmentModalContent.jsx | 83 ++++++++
.../cards/BaseCourseCard.jsx | 101 +++++++++
.../cards/Collapsibles.jsx | 76 +++++++
.../cards/CourseCard.jsx | 173 +++-------------
.../cards/CourseCard.test.jsx | 195 ++++++++++++++----
.../cards/NewAssignmentModalButton.jsx | 46 +++++
.../cards/data/useCourseCardMetadata.js | 73 +++++++
.../learner-credit-management/data/utils.js | 6 +-
.../learner-credit-management/index.jsx | 4 +-
.../search/CatalogSearchResults.jsx | 1 +
.../styles/index.scss | 45 ++--
.../tests/CatalogSearchResults.test.jsx | 12 ++
18 files changed, 633 insertions(+), 207 deletions(-)
create mode 100644 src/components/learner-credit-management/cards/AssignmentModalContent.jsx
create mode 100644 src/components/learner-credit-management/cards/BaseCourseCard.jsx
create mode 100644 src/components/learner-credit-management/cards/Collapsibles.jsx
create mode 100644 src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
create mode 100644 src/components/learner-credit-management/cards/data/useCourseCardMetadata.js
diff --git a/__mocks__/react-instantsearch-dom.jsx b/__mocks__/react-instantsearch-dom.jsx
index 4444b52c33..dbb7fbaba4 100644
--- a/__mocks__/react-instantsearch-dom.jsx
+++ b/__mocks__/react-instantsearch-dom.jsx
@@ -12,11 +12,16 @@ const advertised_course_run = {
start: '2020-09-09T04:00:00Z',
key: 'course-v1:edX+Bee101+3T2020',
};
+const mockNormalizedData = {
+ start_date: '2020-09-09T04:00:00Z',
+ end_date: '2021-09-09T04:00:00Z',
+ enroll_by_date: '2020-09-15T04:00:00Z',
+};
/* eslint-disable camelcase */
const fakeHits = [
- { objectID: '1', aggregation_key: 'course:Bees101', title: 'bla', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Bees101' },
- { objectID: '2', aggregation_key: 'course:Wasps200', title: 'blp', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Wasps200' },
+ { objectID: '1', aggregation_key: 'course:Bees101', title: 'bla', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Bees101', normalized_metadata: mockNormalizedData },
+ { objectID: '2', aggregation_key: 'course:Wasps200', title: 'blp', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Wasps200', normalized_metadata: mockNormalizedData },
];
/* eslint-enable camelcase */
diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
index 07b10958cb..2393795907 100644
--- a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
+++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
@@ -29,6 +29,7 @@ const EnterpriseAppRoutes = ({
enableContentHighlightsPage,
}) => {
const { canManageLearnerCredit } = useContext(EnterpriseSubsidiesContext);
+ console.log('EnterpriseAppRoutes!!!');
return (
{
data: subsidyAccessPolicy,
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ console.log('BudgetDetailPage!!!');
+
if (isInitialLoadingSubsidyAccessPolicy) {
return (
diff --git a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
index 1d0ef0db7f..732682a35c 100644
--- a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
+++ b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
@@ -43,6 +43,9 @@ const BudgetDetailTabsAndRoutes = ({
enterpriseSlug,
enterpriseFeatures,
}) => {
+
+ console.log('BudgetDetailTabsAndRoutes!!!');
+
const { activeTabKey: routeActiveTabKey } = useParams();
const { budgetId, subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
diff --git a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
index 1930262097..50a06dc522 100644
--- a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
@@ -4,6 +4,7 @@ import { Chip, Hyperlink, useToggle } from '@edx/paragon';
import { Timelapse } from '@edx/paragon/icons';
import BaseModalPopup from './BaseModalPopup';
+import { ASSIGNMENT_ENROLLMENT_DEADLINE } from '../data';
const WaitingForLearner = ({ learnerEmail }) => {
const [isOpen, open, close] = useToggle(false);
@@ -31,7 +32,8 @@ const WaitingForLearner = ({ learnerEmail }) => {
This learner must create an edX account and complete enrollment in the course before the
- enrollment deadline or within 90 days of assignment, whichever is sooner.
+ enrollment deadline or within {ASSIGNMENT_ENROLLMENT_DEADLINE} days of assignment, whichever
+ is sooner.
Need help?
diff --git a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
new file mode 100644
index 0000000000..65bfd3e672
--- /dev/null
+++ b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
@@ -0,0 +1,83 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Container,
+ Stack,
+ Row,
+ Col,
+ Form,
+ Card,
+} from '@edx/paragon';
+
+import BaseCourseCard from './BaseCourseCard';
+import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../data';
+import { ImpactOnYourLearnerCreditBudget, ManagingThisAssignment, NextStepsForAssignedLearners } from './Collapsibles';
+
+const AssignmentModalContent = ({ course }) => {
+ const [emailAddresses, setEmailAddresses] = useState('');
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
+ return (
+
+
+
+
+ Use Learner Credit to assign this course
+
+
+
+
+
+ Assign to
+
+ setEmailAddresses(e.target.value)}
+ floatingLabel="Learner email addresses"
+ rows={10}
+ data-hj-suppress
+ />
+
+ To add more than one learner, enter one email address per line.
+
+
+ How assigning this course works
+
+
+
+
+
+
+
+ Pay by Learner Credit
+ Summary
+
+
+ You haven't entered any learners yet.
+ Add learner emails to get started.
+
+
+
+
+ Learner Credit Budget: {subsidyAccessPolicy.displayName ?? 'Overview'}
+
+
+
+ Available balance
+ {formatPrice(subsidyAccessPolicy.aggregates.spendAvailableUsd)}
+
+
+
+
+
+
+ );
+};
+
+AssignmentModalContent.propTypes = {
+ course: PropTypes.shape().isRequired, // Pass-thru prop to `BaseCourseCard`
+};
+
+export default AssignmentModalContent;
diff --git a/src/components/learner-credit-management/cards/BaseCourseCard.jsx b/src/components/learner-credit-management/cards/BaseCourseCard.jsx
new file mode 100644
index 0000000000..ef81a4f620
--- /dev/null
+++ b/src/components/learner-credit-management/cards/BaseCourseCard.jsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import {
+ useMediaQuery,
+ breakpoints,
+ Card,
+ Stack,
+ Badge,
+} from '@edx/paragon';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+
+import useCourseCardMetadata from './data/useCourseCardMetadata';
+import CARD_TEXT from '../constants';
+
+const BaseCourseCard = ({
+ original,
+ footerActions: CardFooterActions,
+ enterpriseSlug,
+ cardClassName,
+}) => {
+ const isSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth });
+ const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });
+ const courseCardMetadata = useCourseCardMetadata({
+ course: camelCaseObject(original),
+ enterpriseSlug,
+ });
+ const {
+ imageSrc,
+ altText,
+ logoSrc,
+ logoAlt,
+ title,
+ subtitle,
+ price,
+ isExecEdCourseType,
+ courseEnrollmentInfo,
+ execEdEnrollmentInfo,
+ } = courseCardMetadata;
+ const { BADGE, PRICE } = CARD_TEXT;
+
+ return (
+
+
+
+
+ {price}
+ {PRICE.subText}
+
+ )}
+ />
+
+
+ {isExecEdCourseType ? BADGE.execEd : BADGE.course}
+
+
+
+ {CardFooterActions && }
+
+
+
+ );
+};
+
+const mapStateToProps = state => ({
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+BaseCourseCard.propTypes = {
+ enterpriseSlug: PropTypes.string.isRequired,
+ original: PropTypes.shape({
+ availability: PropTypes.arrayOf(PropTypes.string),
+ cardImageUrl: PropTypes.string,
+ courseType: PropTypes.string,
+ normalizedMetadata: PropTypes.shape(),
+ originalImageUrl: PropTypes.string,
+ partners: PropTypes.arrayOf(
+ PropTypes.shape({
+ logoImageUrl: PropTypes.string,
+ name: PropTypes.string,
+ }),
+ ),
+ title: PropTypes.string,
+ }).isRequired,
+ footerActions: PropTypes.elementType,
+ cardClassName: PropTypes.string,
+};
+
+export default connect(mapStateToProps)(BaseCourseCard);
diff --git a/src/components/learner-credit-management/cards/Collapsibles.jsx b/src/components/learner-credit-management/cards/Collapsibles.jsx
new file mode 100644
index 0000000000..12d82a6be2
--- /dev/null
+++ b/src/components/learner-credit-management/cards/Collapsibles.jsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Collapsible } from '@edx/paragon';
+
+import { ASSIGNMENT_ENROLLMENT_DEADLINE } from '../data';
+
+export const NextStepsForAssignedLearners = ({ course }) => (
+
Next steps for assigned learners}
+ defaultOpen
+ >
+
+
+
+ Learners will be notified of this course assignment by email.
+
+
+ Learners must complete enrollment for this assignment by {course.enrollmentDeadline}. This deadline
+ is calculated based on the course enrollment deadline or {ASSIGNMENT_ENROLLMENT_DEADLINE} days
+ past the date of assignment, whichever is sooner.
+
+
+ Learners will receive automated reminder emails every 10-15 days until the enrollment
+ deadline is reached.
+
+
+
+
+);
+
+NextStepsForAssignedLearners.propTypes = {
+ course: PropTypes.shape({
+ enrollmentDeadline: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
+export const ImpactOnYourLearnerCreditBudget = () => (
+
Impact on your Learner Credit budget}
+ >
+
+
+
+ The total assignment cost will be earmarked as "assigned" funds in your
+ Learner Credit budget so you can't overspend.
+
+
+ The course cost will automatically convert from "assigned" to "spent" funds
+ when your learners complete registration.
+
+
+
+
+);
+
+export const ManagingThisAssignment = () => (
+
Managing this assignment}
+ >
+
+
+
+ You will be able to monitor the status of this assignment by reviewing
+ your Learner Credit Budget activity.
+
+
+ You can cancel this course assignment or send email reminders any time
+ before learners complete enrollment.
+
+
+
+
+);
diff --git a/src/components/learner-credit-management/cards/CourseCard.jsx b/src/components/learner-credit-management/cards/CourseCard.jsx
index 2d98bf20eb..df2e7379d8 100644
--- a/src/components/learner-credit-management/cards/CourseCard.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.jsx
@@ -1,155 +1,42 @@
-/* eslint-disable @typescript-eslint/naming-convention */
-// variables taken from algolia not in camelcase
-import React, { useContext } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
-import { AppContext } from '@edx/frontend-platform/react';
-import { connect } from 'react-redux';
+import { Button, Hyperlink } from '@edx/paragon';
-import {
- Badge,
- Button,
- Card,
- Stack,
- Hyperlink,
- useMediaQuery,
- breakpoints,
-} from '@edx/paragon';
-import { injectIntl } from '@edx/frontend-platform/i18n';
-import { camelCaseObject } from '@edx/frontend-platform';
-import cardFallbackImg from '@edx/brand/paragon/images/card-imagecap-fallback.png';
-
-import { EXEC_ED_COURSE_TYPE } from '../data';
-import { formatPrice, formatDate, getEnrollmentDeadline } from '../data/utils';
+import NewAssignmentModalButton from './NewAssignmentModalButton';
import CARD_TEXT from '../constants';
+import BaseCourseCard from './BaseCourseCard';
-const CourseCard = ({
- original, enterpriseSlug,
-}) => {
- const {
- availability,
- cardImageUrl,
- courseType,
- key,
- normalizedMetadata,
- partners,
- title,
- } = camelCaseObject(original);
-
- const { config: { ENTERPRISE_LEARNER_PORTAL_URL } } = useContext(AppContext);
- const isSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth });
- const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });
-
- const {
- BADGE,
- BUTTON_ACTION,
- PRICE,
- ENROLLMENT,
- } = CARD_TEXT;
-
- const price = normalizedMetadata?.contentPrice ? formatPrice(normalizedMetadata.contentPrice, { minimumFractionDigits: 0 }) : 'N/A';
-
- const imageSrc = cardImageUrl || cardFallbackImg;
-
- let logoSrc;
- let logoAlt;
- if (partners.length === 1) {
- logoSrc = partners[0]?.logoImageUrl;
- logoAlt = `${partners[0]?.name}'s logo`;
- }
-
- const altText = `${title} course image`;
+const { BUTTON_ACTION } = CARD_TEXT;
- const formattedAvailability = availability?.length ? availability.join(', ') : null;
+const CourseCardFooterActions = (course) => {
+ const { linkToCourse } = course;
- const enrollmentDeadline = getEnrollmentDeadline(normalizedMetadata?.enrollByDate);
-
- let courseEnrollmentInfo;
- let execEdEnrollmentInfo;
- if (normalizedMetadata?.enrollByDate) {
- courseEnrollmentInfo = `${formattedAvailability} • ${ENROLLMENT.text} ${enrollmentDeadline}`;
- execEdEnrollmentInfo = `Starts ${formatDate(normalizedMetadata.startDate)} •
- ${ENROLLMENT.text} ${enrollmentDeadline}`;
- } else {
- courseEnrollmentInfo = formattedAvailability;
- execEdEnrollmentInfo = formattedAvailability;
- }
-
- const isExecEd = courseType === EXEC_ED_COURSE_TYPE;
-
- let linkToCourse;
- if (isExecEd) {
- linkToCourse = `${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/executive-education-2u/course/${key}`;
- } else {
- linkToCourse = `${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${key}`;
- }
-
- return (
-
-
-
-
- {price}
- {PRICE.subText}
-
- )}
- />
-
-
- {isExecEd ? BADGE.execEd : BADGE.course}
-
-
-
-
- {BUTTON_ACTION.viewCourse}
-
- {BUTTON_ACTION.assign}
-
-
-
- );
+ {BUTTON_ACTION.viewCourse}
+ ,
+
+ {BUTTON_ACTION.assign}
+ ,
+ ];
};
+const CourseCard = ({ original }) => (
+
+);
+
CourseCard.propTypes = {
- enterpriseSlug: PropTypes.string.isRequired,
- original: PropTypes.shape({
- availability: PropTypes.arrayOf(PropTypes.string),
- cardImageUrl: PropTypes.string,
- courseType: PropTypes.string,
- normalizedMetadata: PropTypes.shape(),
- originalImageUrl: PropTypes.string,
- partners: PropTypes.arrayOf(
- PropTypes.shape({
- logoImageUrl: PropTypes.string,
- name: PropTypes.string,
- }),
- ),
- title: PropTypes.string,
- }).isRequired,
+ original: PropTypes.shape().isRequired, // pass-thru prop to `BaseCourseCard`
};
-const mapStateToProps = state => ({
- enterpriseSlug: state.portalConfiguration.enterpriseSlug,
-});
-
-export default connect(mapStateToProps)(injectIntl(CourseCard));
+export default CourseCard;
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index cb9304032a..be225b0c9a 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -1,12 +1,17 @@
import React from 'react';
-import { fireEvent, render, screen } from '@testing-library/react';
+import { screen, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { renderWithRouter } from '@edx/frontend-enterprise-utils';
+
import CourseCard from './CourseCard';
+import { formatPrice, useSubsidyAccessPolicy } from '../data';
const originalData = {
availability: ['Upcoming'],
@@ -22,6 +27,7 @@ const originalData = {
partners: [{ logo_image_url: '', name: 'Course Provider' }],
title: 'Course Title',
};
+const imageAltText = `${originalData.title} course image`;
const defaultProps = {
original: originalData,
@@ -60,6 +66,27 @@ const initialStoreState = {
},
};
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+});
+
+const mockSubsidyAccessPolicy = {
+ uuid: 'test-subsidy-access-policy-uuid',
+ displayName: 'Test Subsidy Access Policy',
+ aggregates: {
+ spendAvailableUsd: 50000,
+ },
+};
+
+jest.mock('../data', () => ({
+ ...jest.requireActual('../data'),
+ useSubsidyAccessPolicy: jest.fn(),
+}));
+
const CourseCardWrapper = ({
initialState = initialStoreState,
...rest
@@ -67,54 +94,146 @@ const CourseCardWrapper = ({
const store = getMockStore({ ...initialState });
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
};
describe('Course card works as expected', () => {
- test('course card renders', () => {
- render(
);
- expect(screen.queryByText(defaultProps.original.title)).toBeInTheDocument();
- expect(
- screen.queryByText(defaultProps.original.partners[0].name),
- ).toBeInTheDocument();
- expect(screen.queryByText('$100')).toBeInTheDocument();
- expect(screen.queryByText('Per learner price')).toBeInTheDocument();
- expect(screen.queryByText('Upcoming • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
- expect(screen.queryByText('Course')).toBeInTheDocument();
- expect(screen.queryByText('View course')).toBeInTheDocument();
- expect(screen.queryByText('Assign')).toBeInTheDocument();
- const hyperlink = screen.getByRole('link', {
- name: 'View course Opens in a new tab',
+ beforeEach(() => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ data: mockSubsidyAccessPolicy,
+ isLoading: false,
});
- expect(hyperlink.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/course/course-123x');
});
- test('test card renders default image', async () => {
- render(
);
- const imageAltText = `${originalData.title} course image`;
- fireEvent.error(screen.getByAltText(imageAltText));
- await expect(screen.getByAltText(imageAltText).src).not.toBeUndefined;
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('course card renders', () => {
+ renderWithRouter(
);
+ expect(screen.getByText(defaultProps.original.title)).toBeInTheDocument();
+ expect(screen.getByText(defaultProps.original.partners[0].name)).toBeInTheDocument();
+ expect(screen.getByText('$100')).toBeInTheDocument();
+ expect(screen.getByText('Per learner price')).toBeInTheDocument();
+ expect(screen.getByText('Upcoming • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
+ expect(screen.getByText('Course')).toBeInTheDocument();
+ // Has card image defined even though the course metadata does not contain an image URL
+ const cardImage = screen.getByAltText(imageAltText);
+ expect(cardImage).toBeInTheDocument();
+ expect(cardImage.src).toBeDefined();
+
+ // Footer actions
+ const viewCourseCTA = screen.getByText('View course', { selector: 'a' });
+ expect(viewCourseCTA).toBeInTheDocument();
+ expect(viewCourseCTA.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/course/course-123x');
+ const assignCourseCTA = screen.getByText('Assign', { selector: 'button' });
+ expect(assignCourseCTA).toBeInTheDocument();
});
- test('exec ed card renders', async () => {
- render(
);
+ test('card renders given image', () => {
+ const mockCardImageUrl = 'https://example.com/image.jpg';
+ const props = {
+ ...defaultProps,
+ original: {
+ ...originalData,
+ card_image_url: mockCardImageUrl,
+ },
+ };
+ renderWithRouter(
);
+ const cardImage = screen.getByAltText(imageAltText);
+ expect(cardImage).toBeInTheDocument();
+ expect(cardImage.src).toEqual(mockCardImageUrl);
+ });
+
+ test('executive education card renders', () => {
+ renderWithRouter(
);
expect(screen.queryByText('$999')).toBeInTheDocument();
expect(screen.queryByText('Starts Apr 18, 2016 • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
expect(screen.queryByText('Executive Education')).toBeInTheDocument();
- const hyperlink = screen.getByRole('link', {
- name: 'View course Opens in a new tab',
- });
- expect(hyperlink.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/executive-education-2u/course/exec-ed-course-123x');
+ const viewCourseCTA = screen.getByText('View course', { selector: 'a' });
+ expect(viewCourseCTA.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/executive-education-2u/course/exec-ed-course-123x');
+ });
+
+ test('opens assignment modal', () => {
+ renderWithRouter(
);
+ const assignCourseCTA = screen.getByText('Assign', { selector: 'button' });
+ expect(assignCourseCTA).toBeInTheDocument();
+ userEvent.click(assignCourseCTA);
+
+ const assignmentModal = within(screen.getByRole('dialog'));
+
+ expect(assignmentModal.getByText('Assign this course')).toBeInTheDocument();
+ expect(assignmentModal.getByText('Use Learner Credit to assign this course')).toBeInTheDocument();
+
+ // Verify course card is displayed WITHOUT footer actions
+ const modalCourseCard = within(assignmentModal.getByText('Course Title').closest('.pgn__card'));
+ expect(modalCourseCard.getByText(defaultProps.original.title)).toBeInTheDocument();
+ expect(modalCourseCard.getByText(defaultProps.original.partners[0].name)).toBeInTheDocument();
+ expect(modalCourseCard.getByText('$100')).toBeInTheDocument();
+ expect(modalCourseCard.getByText('Per learner price')).toBeInTheDocument();
+ expect(modalCourseCard.getByText('Upcoming • Learner must enroll by Feb 18, 2016')).toBeInTheDocument();
+ expect(modalCourseCard.getByText('Course')).toBeInTheDocument();
+ const cardImage = modalCourseCard.getByAltText(imageAltText);
+ expect(cardImage).toBeInTheDocument();
+ expect(cardImage.src).toBeDefined();
+ expect(modalCourseCard.queryByText('View course', { selector: 'a' })).not.toBeInTheDocument();
+ expect(modalCourseCard.queryByText('Assign', { selector: 'button' })).not.toBeInTheDocument();
+
+ // Verify empty state and textarea can accept emails
+ expect(assignmentModal.getByText('Assign to')).toBeInTheDocument();
+ const textareaInputLabel = assignmentModal.getByLabelText('Learner email addresses');
+ expect(textareaInputLabel).toBeInTheDocument();
+ const textareaInput = textareaInputLabel.closest('textarea');
+ expect(textareaInput).toBeInTheDocument();
+ userEvent.type(textareaInput, 'hello@example.com{enter}world@example.com');
+ expect(textareaInput).toHaveValue('hello@example.com\nworld@example.com');
+ expect(assignmentModal.getByText('To add more than one learner, enter one email address per line.')).toBeInTheDocument();
+ expect(assignmentModal.getByText('Pay by Learner Credit')).toBeInTheDocument();
+ expect(assignmentModal.getByText('Summary')).toBeInTheDocument();
+ expect(assignmentModal.getByText('You haven\'t entered any learners yet.')).toBeInTheDocument();
+ expect(assignmentModal.getByText('Add learner emails to get started.')).toBeInTheDocument();
+ expect(assignmentModal.getByText(`Learner Credit Budget: ${mockSubsidyAccessPolicy.displayName}`)).toBeInTheDocument();
+ expect(assignmentModal.getByText('Available balance')).toBeInTheDocument();
+ const expectedAvailableBalance = formatPrice(mockSubsidyAccessPolicy.aggregates.spendAvailableUsd);
+ expect(assignmentModal.getByText(expectedAvailableBalance)).toBeInTheDocument();
+
+ // Verify collapsibles
+ expect(assignmentModal.getByText('How assigning this course works')).toBeInTheDocument();
+ expect(assignmentModal.getByText('Next steps for assigned learners')).toBeInTheDocument();
+ expect(assignmentModal.getByText('Learners will be notified of this course assignment by email.')).toBeInTheDocument();
+ const budgetImpact = assignmentModal.getByText('Impact on your Learner Credit budget');
+ expect(budgetImpact).toBeInTheDocument();
+ expect(assignmentModal.queryByText('The total assignment cost will be earmarked as "assigned" funds', { exact: false })).not.toBeInTheDocument();
+ userEvent.click(budgetImpact);
+ expect(assignmentModal.getByText('The total assignment cost will be earmarked as "assigned" funds', { exact: false })).toBeInTheDocument();
+ const managingAssignment = assignmentModal.getByText('Managing this assignment');
+ expect(managingAssignment).toBeInTheDocument();
+ expect(assignmentModal.queryByText('You will be able to monitor the status of this assignment', { exact: false })).not.toBeInTheDocument();
+ userEvent.click(managingAssignment);
+ expect(assignmentModal.getByText('You will be able to monitor the status of this assignment', { exact: false })).toBeInTheDocument();
+
+ // Verify modal footer
+ expect(assignmentModal.getByText('Help Center: Course Assignments')).toBeInTheDocument();
+ const cancelAssignmentCTA = assignmentModal.getByText('Cancel', { selector: 'button' });
+ expect(cancelAssignmentCTA).toBeInTheDocument();
+ const submitAssignmentCTA = assignmentModal.getByText('Assign', { selector: 'button' });
+ expect(submitAssignmentCTA).toBeInTheDocument();
+
+ // Verify modal closes
+ userEvent.click(cancelAssignmentCTA);
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
});
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
new file mode 100644
index 0000000000..1b63968106
--- /dev/null
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ FullscreenModal,
+ ActionRow,
+ Button,
+ useToggle,
+ Hyperlink,
+} from '@edx/paragon';
+import AssignmentModalContent from './AssignmentModalContent';
+
+const NewAssignmentModalButton = ({ course, children }) => {
+ const [isOpen, open, close] = useToggle(false);
+
+ return (
+ <>
+
{children}
+
+
+ Help Center: Course Assignments
+
+
+ Cancel
+ {/* TODO: https://2u-internal.atlassian.net/browse/ENT-7826 */}
+ Assign
+
+ )}
+ >
+
+
+ >
+ );
+};
+
+NewAssignmentModalButton.propTypes = {
+ course: PropTypes.shape().isRequired, // Pass-thru prop to `BaseCourseCard`
+ children: PropTypes.node.isRequired, // Represents the button text
+};
+
+export default NewAssignmentModalButton;
diff --git a/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js b/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js
new file mode 100644
index 0000000000..cfb4312e20
--- /dev/null
+++ b/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js
@@ -0,0 +1,73 @@
+import { useContext } from 'react';
+import { AppContext } from '@edx/frontend-platform/react';
+import cardFallbackImg from '@edx/brand/paragon/images/card-imagecap-fallback.png';
+
+import CARD_TEXT from '../../constants';
+import {
+ EXEC_ED_COURSE_TYPE,
+ formatDate,
+ formatPrice,
+ getEnrollmentDeadline,
+} from '../../data';
+
+const { ENROLLMENT } = CARD_TEXT;
+
+const useCourseCardMetadata = ({
+ course,
+ enterpriseSlug,
+}) => {
+ const { config: { ENTERPRISE_LEARNER_PORTAL_URL } } = useContext(AppContext);
+ const {
+ availability,
+ cardImageUrl,
+ courseType,
+ key,
+ normalizedMetadata,
+ partners,
+ title,
+ } = course;
+ const price = (normalizedMetadata.contentPrice || normalizedMetadata.contentPrice === 0) ? formatPrice(normalizedMetadata.contentPrice) : 'N/A';
+ const imageSrc = cardImageUrl || cardFallbackImg;
+
+ let logoSrc;
+ let logoAlt;
+ if (partners.length === 1) {
+ logoSrc = partners[0]?.logoImageUrl;
+ logoAlt = `${partners[0]?.name}'s logo`;
+ }
+
+ const altText = `${title} course image`;
+ const formattedAvailability = availability?.length ? availability.join(', ') : null;
+ const enrollmentDeadline = getEnrollmentDeadline(normalizedMetadata.enrollByDate);
+
+ let courseEnrollmentInfo = '';
+ if (formattedAvailability) {
+ courseEnrollmentInfo = `${formattedAvailability} • `;
+ }
+ courseEnrollmentInfo += `${ENROLLMENT.text} ${enrollmentDeadline}`;
+ const execEdEnrollmentInfo = `Starts ${formatDate(normalizedMetadata.startDate)} • ${ENROLLMENT.text} ${enrollmentDeadline}`;
+
+ const isExecEdCourseType = courseType === EXEC_ED_COURSE_TYPE;
+
+ let linkToCourse = `${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${key}`;
+ if (isExecEdCourseType) {
+ linkToCourse = `${ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/executive-education-2u/course/${key}`;
+ }
+
+ return {
+ ...course,
+ subtitle: partners.map(partner => partner.name).join(', '),
+ price,
+ imageSrc,
+ altText,
+ logoSrc,
+ logoAlt,
+ enrollmentDeadline,
+ courseEnrollmentInfo,
+ execEdEnrollmentInfo,
+ linkToCourse,
+ isExecEdCourseType,
+ };
+};
+
+export default useCourseCardMetadata;
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 6a5a45888c..61122ad402 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -203,9 +203,11 @@ export function formatDate(date) {
// Exec ed and open courses cards should display either the enrollment deadline
// or 90 days from the present date on user pageload, whichever is sooner.
export function getEnrollmentDeadline(enrollByDate) {
- const courseEnrollByDate = dayjs(enrollByDate);
const assignmentEnrollmentDeadline = dayjs().add(ASSIGNMENT_ENROLLMENT_DEADLINE, 'days');
-
+ if (!enrollByDate) {
+ return formatDate(assignmentEnrollmentDeadline);
+ }
+ const courseEnrollByDate = dayjs(enrollByDate);
return courseEnrollByDate <= assignmentEnrollmentDeadline
? formatDate(courseEnrollByDate)
: formatDate(assignmentEnrollmentDeadline);
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
index 43786e8a0b..0eaef07494 100644
--- a/src/components/learner-credit-management/index.jsx
+++ b/src/components/learner-credit-management/index.jsx
@@ -5,7 +5,7 @@ import MultipleBudgetsPage from './MultipleBudgetsPage';
import BudgetDetailPage from './BudgetDetailPage';
const LearnerCreditManagementRoutes = ({ match }) => (
- <>
+
(
path={`${match.path}/:budgetId/:activeTabKey?`}
component={BudgetDetailPage}
/>
- >
+
);
LearnerCreditManagementRoutes.propTypes = {
diff --git a/src/components/learner-credit-management/search/CatalogSearchResults.jsx b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
index fdd9bde51b..a256e2f5e7 100644
--- a/src/components/learner-credit-management/search/CatalogSearchResults.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
@@ -33,6 +33,7 @@ export const BaseCatalogSearchResults = ({
error,
setNoContent,
}) => {
+ console.log('BaseCatalogSearchResults!!!', searchResults);
const courseColumns = useMemo(
() => [
{
diff --git a/src/components/learner-credit-management/styles/index.scss b/src/components/learner-credit-management/styles/index.scss
index c3301e7af2..3db792b691 100644
--- a/src/components/learner-credit-management/styles/index.scss
+++ b/src/components/learner-credit-management/styles/index.scss
@@ -1,22 +1,31 @@
-.budget-detail-assignments {
- // This is a (temporary) workaround to ensure the `Chip` modal popups within the "Assigned" table status column
- // properly overlays the underlying `DataTable`.
- .pgn__data-table-container,
- .pgn__data-table-layout-wrapper {
- overflow-x: visible;
- }
-
- // The `Card` component in Paragon does not seem to properly let consumers customize the width of the `Card.Body`
- // contents when in the horizontal card orientation without custom CSS. As a result, both the `Card.Footer` and
- // `Card.Body` incorrectly get equal column widths when the preference is that the `Card.Body` has more width than
- // the `Card.Footer`. The below styles force the `Card.Body` to have appropriately more width than the `Card.Footer` when
- // the `Card` is in the horizontal orientation.
- .assign-more-courses-empty-state-minimal {
- .assign-more-courses__card-body {
- flex: 3;
+.learner-credit-management {
+ .budget-detail-assignments {
+ // This is a (temporary) workaround to ensure the `Chip` modal popups within the "Assigned" table status column
+ // properly overlays the underlying `DataTable`.
+ .pgn__data-table-container,
+ .pgn__data-table-layout-wrapper {
+ overflow-x: visible;
}
- .assign-more-courses__card-footer {
- flex: 1;
+
+ // The `Card` component in Paragon does not seem to properly let consumers customize the width of the `Card.Body`
+ // contents when in the horizontal card orientation without custom CSS. As a result, both the `Card.Footer` and
+ // `Card.Body` incorrectly get equal column widths when the preference is that the `Card.Body` has more width than
+ // the `Card.Footer`. The below styles (temporary) force the `Card.Body` to have appropriately more width than the
+ // `Card.Footer` when the `Card` is in the horizontal orientation.
+ .assign-more-courses-empty-state-minimal {
+ .assign-more-courses__card-body {
+ flex: 3;
+ }
+ .assign-more-courses__card-footer {
+ flex: 1;
+ }
}
}
}
+
+
+// Must be defined outside of `.learner-credit-management` to ensure the styles are applied to the contents of
+// the `FullscreenModal`, which renders in a React Portal.
+.assignment-modal-collapsible-trigger {
+ text-decoration: underline;
+}
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
index 503833aa57..b26b2de7ec 100644
--- a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
@@ -88,6 +88,12 @@ const searchResults = {
upgrade_deadline: 1892678399,
pacing_type: 'self_paced',
},
+ normalized_metadata: {
+ start_date: '2020-09-09T04:00:00Z',
+ end_date: '2021-09-09T04:00:00Z',
+ enroll_by_date: '2020-09-15T04:00:00Z',
+ content_price: 199,
+ },
},
{
title: TEST_COURSE_NAME_2,
@@ -104,6 +110,12 @@ const searchResults = {
upgrade_deadline: 1892678399,
pacing_type: 'self_paced',
},
+ normalized_metadata: {
+ start_date: '2020-09-09T04:00:00Z',
+ end_date: '2021-09-09T04:00:00Z',
+ enroll_by_date: '2020-09-15T04:00:00Z',
+ content_price: 199,
+ },
},
],
page: 1,
From 8211761bdce0608d21e01ee2d48ff589315e681a Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 2 Nov 2023 11:23:38 -0400
Subject: [PATCH 058/124] fix: rely on new `error_reason` field to determine
failed `learner_state` reason (#1079)
---
.../EnterpriseApp/EnterpriseAppRoutes.jsx | 1 -
src/components/EnterpriseApp/index.jsx | 6 +---
.../AssignmentStatusTableCell.jsx | 17 ++++++-----
.../BudgetDetailPage.jsx | 2 --
.../BudgetDetailTabsAndRoutes.jsx | 3 --
.../cards/Collapsibles.jsx | 4 ---
.../search/CatalogSearchResults.jsx | 1 -
.../tests/BudgetDetailPage.test.jsx | 30 ++++++++++++++-----
8 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
index 2393795907..07b10958cb 100644
--- a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
+++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx
@@ -29,7 +29,6 @@ const EnterpriseAppRoutes = ({
enableContentHighlightsPage,
}) => {
const { canManageLearnerCredit } = useContext(EnterpriseSubsidiesContext);
- console.log('EnterpriseAppRoutes!!!');
return (
{
- const { original: { learnerEmail, learnerState } } = row;
+ const { original } = row;
+ const {
+ learnerEmail,
+ learnerState,
+ errorReason,
+ } = original;
// Learner state is not available for this assignment, so don't display anything.
if (!learnerState) {
return null;
}
+ // Display the appropriate status chip based on the learner state.
if (learnerState === 'notifying') {
return (
@@ -29,12 +35,8 @@ const AssignmentStatusTableCell = ({ row }) => {
}
if (learnerState === 'failed') {
- // Determine the failure reason based on the actions.
- const { actions } = row.original;
- const mostRecentAction = actions[0]; // API returns actions in reverse chronological order.
- const isBadEmailError = mostRecentAction.actionType === 'notified' && !!mostRecentAction.errorReason;
-
- if (isBadEmailError) {
+ // Determine which failure chip to display based on the error reason.
+ if (errorReason === 'email_error') {
return (
);
@@ -52,6 +54,7 @@ AssignmentStatusTableCell.propTypes = {
original: PropTypes.shape({
learnerEmail: PropTypes.string,
learnerState: PropTypes.string.isRequired,
+ errorReason: PropTypes.string,
actions: PropTypes.arrayOf(PropTypes.shape({
actionType: PropTypes.string.isRequired,
errorReason: PropTypes.string,
diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx
index 4591c9eee3..1ad1ff56cc 100644
--- a/src/components/learner-credit-management/BudgetDetailPage.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPage.jsx
@@ -13,8 +13,6 @@ const BudgetDetailPage = () => {
data: subsidyAccessPolicy,
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
- console.log('BudgetDetailPage!!!');
-
if (isInitialLoadingSubsidyAccessPolicy) {
return (
diff --git a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
index 732682a35c..1d0ef0db7f 100644
--- a/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
+++ b/src/components/learner-credit-management/BudgetDetailTabsAndRoutes.jsx
@@ -43,9 +43,6 @@ const BudgetDetailTabsAndRoutes = ({
enterpriseSlug,
enterpriseFeatures,
}) => {
-
- console.log('BudgetDetailTabsAndRoutes!!!');
-
const { activeTabKey: routeActiveTabKey } = useParams();
const { budgetId, subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
diff --git a/src/components/learner-credit-management/cards/Collapsibles.jsx b/src/components/learner-credit-management/cards/Collapsibles.jsx
index 12d82a6be2..501b113dcf 100644
--- a/src/components/learner-credit-management/cards/Collapsibles.jsx
+++ b/src/components/learner-credit-management/cards/Collapsibles.jsx
@@ -20,10 +20,6 @@ export const NextStepsForAssignedLearners = ({ course }) => (
is calculated based on the course enrollment deadline or {ASSIGNMENT_ENROLLMENT_DEADLINE} days
past the date of assignment, whichever is sooner.
-
- Learners will receive automated reminder emails every 10-15 days until the enrollment
- deadline is reached.
-
diff --git a/src/components/learner-credit-management/search/CatalogSearchResults.jsx b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
index a256e2f5e7..fdd9bde51b 100644
--- a/src/components/learner-credit-management/search/CatalogSearchResults.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearchResults.jsx
@@ -33,7 +33,6 @@ export const BaseCatalogSearchResults = ({
error,
setNoContent,
}) => {
- console.log('BaseCatalogSearchResults!!!', searchResults);
const courseColumns = useMemo(
() => [
{
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 462b32d46c..1be7a7488a 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -78,13 +78,17 @@ const mockSuccessfulNotifiedAction = {
completedAt: '2023-10-27',
errorReason: null,
};
-
+const mockSuccessfulLinkedLearnerAction = {
+ uuid: 'test-assignment-action-uuid',
+ actionType: 'notified',
+ completedAt: '2023-10-27',
+ errorReason: null,
+};
const mockFailedNotifiedAction = {
...mockSuccessfulNotifiedAction,
completedAt: null,
- errorReason: 'bad_email',
+ errorReason: 'email_error',
};
-
const mockFailedLinkedLearnerAction = {
...mockFailedNotifiedAction,
actionType: 'learner_linked',
@@ -302,6 +306,7 @@ describe(' ', () => {
learnerState: 'waiting',
recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
},
],
numPages: 1,
@@ -373,6 +378,7 @@ describe(' ', () => {
learnerState: 'waiting',
recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
},
],
numPages: 1,
@@ -404,6 +410,7 @@ describe(' ', () => {
expectedModalPopupHeading: `Notifying ${mockLearnerEmail}`,
expectedModalPopupContent: `Our system is busy emailing ${mockLearnerEmail}!`,
actions: [],
+ errorReason: null,
},
{
learnerState: 'notifying',
@@ -412,6 +419,7 @@ describe(' ', () => {
expectedModalPopupHeading: 'Notifying learner',
expectedModalPopupContent: 'Our system is busy emailing the learner!',
actions: [],
+ errorReason: null,
},
{
learnerState: 'waiting',
@@ -419,7 +427,8 @@ describe(' ', () => {
expectedChipStatus: 'Waiting for learner',
expectedModalPopupHeading: `Waiting for ${mockLearnerEmail}`,
expectedModalPopupContent: 'This learner must create an edX account and complete enrollment in the course',
- actions: [mockSuccessfulNotifiedAction],
+ actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction],
+ errorReason: null,
},
{
learnerState: 'waiting',
@@ -427,7 +436,8 @@ describe(' ', () => {
expectedChipStatus: 'Waiting for learner',
expectedModalPopupHeading: 'Waiting for learner',
expectedModalPopupContent: 'This learner must create an edX account and complete enrollment in the course',
- actions: [mockSuccessfulNotifiedAction],
+ actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction],
+ errorReason: null,
},
{
learnerState: 'failed',
@@ -435,7 +445,8 @@ describe(' ', () => {
expectedChipStatus: 'Failed: Bad email',
expectedModalPopupHeading: 'Failed: Bad email',
expectedModalPopupContent: `This course assignment failed because a notification to ${mockLearnerEmail} could not be sent.`,
- actions: [mockFailedNotifiedAction],
+ actions: [mockSuccessfulLinkedLearnerAction, mockFailedNotifiedAction],
+ errorReason: 'email_error',
},
{
learnerState: 'failed',
@@ -443,7 +454,8 @@ describe(' ', () => {
expectedChipStatus: 'Failed: Bad email',
expectedModalPopupHeading: 'Failed: Bad email',
expectedModalPopupContent: 'This course assignment failed because a notification to the learner could not be sent.',
- actions: [mockFailedNotifiedAction],
+ actions: [mockSuccessfulLinkedLearnerAction, mockFailedNotifiedAction],
+ errorReason: 'email_error',
},
{
learnerState: 'failed',
@@ -452,6 +464,7 @@ describe(' ', () => {
expectedModalPopupHeading: 'Failed: System',
expectedModalPopupContent: 'Something went wrong behind the scenes.',
actions: [mockFailedLinkedLearnerAction],
+ errorReason: 'internal_api_error',
},
])('renders correct status chips with assigned table data (%s)', ({
learnerState,
@@ -460,6 +473,7 @@ describe(' ', () => {
expectedModalPopupHeading,
expectedModalPopupContent,
actions,
+ errorReason,
}) => {
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
@@ -489,6 +503,7 @@ describe(' ', () => {
learnerState,
recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions,
+ errorReason,
},
],
numPages: 1,
@@ -757,6 +772,7 @@ describe(' ', () => {
learnerState: 'active',
recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions: [],
+ errorReason: null,
state: 'allocated',
},
],
From e97f7ae67b158b748a33535ed04b0eb995858786 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 3 Nov 2023 12:15:56 -0400
Subject: [PATCH 059/124] fix: integrate with `allocate` API for creating
assignments, update global retry behavior on queries, handle 404 subsidy
access policy (#1080)
---
src/components/App/index.jsx | 2 +
.../BudgetDetailActivityTabContents.jsx | 5 +-
.../BudgetDetailPage.jsx | 15 ++-
.../BudgetDetailPageWrapper.jsx | 14 +-
.../cards/AssignmentModalContent.jsx | 18 ++-
.../cards/BaseCourseCard.jsx | 2 +-
.../cards/CourseCard.jsx | 2 +-
.../cards/CourseCard.test.jsx | 125 ++++++++++++++----
.../cards/NewAssignmentModalButton.jsx | 68 +++++++++-
.../useBudgetDetailActivityOverview.test.jsx | 13 +-
.../hooks/useSubsidyAccessPolicy.test.jsx | 13 +-
.../tests/BudgetDetailPage.test.jsx | 25 +++-
.../tests/CatalogSearch.test.jsx | 7 +-
.../tests/CatalogSearchResults.test.jsx | 21 +--
.../tests/NewExistingSSOConfigs.test.jsx | 14 +-
.../tests/SettingsSSOTab.test.jsx | 13 +-
src/components/test/testUtils.jsx | 27 +++-
.../services/EnterpriseAccessApiService.js | 5 +
.../tests/EnterpriseAccessApiService.test.js | 13 ++
src/utils.js | 13 ++
src/utils.test.js | 21 +++
21 files changed, 332 insertions(+), 104 deletions(-)
diff --git a/src/components/App/index.jsx b/src/components/App/index.jsx
index 5915c030a4..ee1afbbf11 100644
--- a/src/components/App/index.jsx
+++ b/src/components/App/index.jsx
@@ -23,10 +23,12 @@ import { SystemWideWarningBanner } from '../system-wide-banner';
import store from '../../data/store';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import { defaultQueryClientRetryHandler } from '../../utils';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
+ retry: defaultQueryClientRetryHandler,
// Specifying a longer `staleTime` of 20 seconds means queries will not refetch their data
// as often; mitigates making duplicate queries when within the `staleTime` window, instead
// relying on the cached data until the `staleTime` window has exceeded. This may be modified
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index 123b48f7e5..8c41d33eef 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -14,13 +14,16 @@ const BudgetDetailActivityTabContents = ({ enterpriseUUID, enterpriseFeatures })
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
isLoading: isBudgetActivityOverviewLoading,
+ isFetching: isBudgetActivityOverviewFetching,
data: budgetActivityOverview,
} = useBudgetDetailActivityOverview({
enterpriseUUID,
isTopDownAssignmentEnabled,
});
- if (isBudgetActivityOverviewLoading || !budgetActivityOverview) {
+ // // If the budget activity overview data is loading (either the initial request OR any
+ // // background re-fetching), show a skeleton.
+ if (isBudgetActivityOverviewLoading || isBudgetActivityOverviewFetching || !budgetActivityOverview) {
return (
<>
diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx
index 1ad1ff56cc..3a7192e5e4 100644
--- a/src/components/learner-credit-management/BudgetDetailPage.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPage.jsx
@@ -5,17 +5,20 @@ import { useBudgetId, useSubsidyAccessPolicy } from './data';
import BudgetDetailTabsAndRoutes from './BudgetDetailTabsAndRoutes';
import BudgetDetailPageWrapper from './BudgetDetailPageWrapper';
import BudgetDetailPageHeader from './BudgetDetailPageHeader';
+import NotFoundPage from '../NotFoundPage';
const BudgetDetailPage = () => {
const { subsidyAccessPolicyId } = useBudgetId();
const {
- isInitialLoading: isInitialLoadingSubsidyAccessPolicy,
data: subsidyAccessPolicy,
+ isInitialLoading: isSubsidyAccessPolicyInitialLoading,
+ isError: isSubsidyAccessPolicyError,
+ error,
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
- if (isInitialLoadingSubsidyAccessPolicy) {
+ if (isSubsidyAccessPolicyInitialLoading) {
return (
-
+
@@ -25,6 +28,12 @@ const BudgetDetailPage = () => {
);
}
+ // If the budget is intended to be a subsidy access policy (by presence of a policy UUID),
+ // and the subsidy access policy is not found, show 404 messaging.
+ if (subsidyAccessPolicyId && isSubsidyAccessPolicyError && error?.customAttributes.httpErrorStatus === 404) {
+ return ;
+ }
+
return (
diff --git a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
index 1651094dd4..9d6d95943d 100644
--- a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
@@ -7,7 +7,11 @@ import Hero from '../Hero';
const PAGE_TITLE = 'Learner Credit Management';
-const BudgetDetailPageWrapper = ({ subsidyAccessPolicy, children }) => {
+const BudgetDetailPageWrapper = ({
+ subsidyAccessPolicy,
+ includeHero,
+ children,
+}) => {
// display name is an optional field, and may not be set for all budgets so fallback to "Overview"
// similar to the display name logic for budgets on the overview page route.
const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
@@ -15,7 +19,7 @@ const BudgetDetailPageWrapper = ({ subsidyAccessPolicy, children }) => {
return (
<>
-
+ {includeHero && }
{children}
@@ -26,6 +30,12 @@ const BudgetDetailPageWrapper = ({ subsidyAccessPolicy, children }) => {
BudgetDetailPageWrapper.propTypes = {
children: PropTypes.node.isRequired,
subsidyAccessPolicy: PropTypes.shape(),
+ includeHero: PropTypes.bool,
+};
+
+BudgetDetailPageWrapper.defaultProps = {
+ includeHero: true,
+ subsidyAccessPolicy: undefined,
};
export default BudgetDetailPageWrapper;
diff --git a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
index 65bfd3e672..e13e317d58 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
@@ -13,18 +13,25 @@ import BaseCourseCard from './BaseCourseCard';
import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../data';
import { ImpactOnYourLearnerCreditBudget, ManagingThisAssignment, NextStepsForAssignedLearners } from './Collapsibles';
-const AssignmentModalContent = ({ course }) => {
- const [emailAddresses, setEmailAddresses] = useState('');
+const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
+ const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const handleEmailAddressInputChange = (e) => {
+ const inputValue = e.target.value;
+ const emailAddresses = inputValue.split('\n').filter((email) => email.trim().length > 0);
+ setEmailAddressesInputValue(inputValue);
+ onEmailAddressesChange(emailAddresses);
+ };
+
return (
Use Learner Credit to assign this course
-
+
@@ -33,8 +40,8 @@ const AssignmentModalContent = ({ course }) => {
setEmailAddresses(e.target.value)}
+ value={emailAddressesInputValue}
+ onChange={handleEmailAddressInputChange}
floatingLabel="Learner email addresses"
rows={10}
data-hj-suppress
@@ -78,6 +85,7 @@ const AssignmentModalContent = ({ course }) => {
AssignmentModalContent.propTypes = {
course: PropTypes.shape().isRequired, // Pass-thru prop to `BaseCourseCard`
+ onEmailAddressesChange: PropTypes.func.isRequired,
};
export default AssignmentModalContent;
diff --git a/src/components/learner-credit-management/cards/BaseCourseCard.jsx b/src/components/learner-credit-management/cards/BaseCourseCard.jsx
index ef81a4f620..a9d0832b8b 100644
--- a/src/components/learner-credit-management/cards/BaseCourseCard.jsx
+++ b/src/components/learner-credit-management/cards/BaseCourseCard.jsx
@@ -67,7 +67,7 @@ const BaseCourseCard = ({
orientation={isExtraSmall ? 'horizontal' : 'vertical'}
textElement={isExecEdCourseType ? execEdEnrollmentInfo : courseEnrollmentInfo}
>
- {CardFooterActions && }
+ {CardFooterActions && }
diff --git a/src/components/learner-credit-management/cards/CourseCard.jsx b/src/components/learner-credit-management/cards/CourseCard.jsx
index df2e7379d8..2e1ec52cdb 100644
--- a/src/components/learner-credit-management/cards/CourseCard.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.jsx
@@ -8,7 +8,7 @@ import BaseCourseCard from './BaseCourseCard';
const { BUTTON_ACTION } = CARD_TEXT;
-const CourseCardFooterActions = (course) => {
+const CourseCardFooterActions = ({ course }) => {
const { linkToCourse } = course;
return [
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index be225b0c9a..bc6b38aba8 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -1,17 +1,37 @@
import React from 'react';
-import { screen, within } from '@testing-library/react';
+import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryClientProvider, useQueryClient } from '@tanstack/react-query';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { renderWithRouter } from '@edx/frontend-enterprise-utils';
import CourseCard from './CourseCard';
-import { formatPrice, useSubsidyAccessPolicy } from '../data';
+import {
+ formatPrice,
+ learnerCreditManagementQueryKeys,
+ useBudgetId,
+ useSubsidyAccessPolicy,
+} from '../data';
+import { getButtonElement, queryClient } from '../../test/testUtils';
+
+import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
+
+jest.mock('@tanstack/react-query', () => ({
+ ...jest.requireActual('@tanstack/react-query'),
+ useQueryClient: jest.fn(),
+}));
+
+jest.mock('../data', () => ({
+ ...jest.requireActual('../data'),
+ useBudgetId: jest.fn(),
+ useSubsidyAccessPolicy: jest.fn(),
+}));
+jest.mock('../../../data/services/EnterpriseAccessApiService');
const originalData = {
availability: ['Upcoming'],
@@ -50,7 +70,6 @@ const execEdData = {
partners: [{ logo_image_url: '', name: 'Course Provider' }],
title: 'Exec Ed Title',
};
-
const execEdProps = {
original: execEdData,
};
@@ -66,14 +85,6 @@ const initialStoreState = {
},
};
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
-});
-
const mockSubsidyAccessPolicy = {
uuid: 'test-subsidy-access-policy-uuid',
displayName: 'Test Subsidy Access Policy',
@@ -81,11 +92,7 @@ const mockSubsidyAccessPolicy = {
spendAvailableUsd: 50000,
},
};
-
-jest.mock('../data', () => ({
- ...jest.requireActual('../data'),
- useSubsidyAccessPolicy: jest.fn(),
-}));
+const mockLearnerEmails = ['hello@example.com', 'world@example.com'];
const CourseCardWrapper = ({
initialState = initialStoreState,
@@ -94,7 +101,7 @@ const CourseCardWrapper = ({
const store = getMockStore({ ...initialState });
return (
-
+
{
data: mockSubsidyAccessPolicy,
isLoading: false,
});
+ useBudgetId.mockReturnValue({ subsidyAccessPolicyId: mockSubsidyAccessPolicy.uuid });
});
afterEach(() => {
@@ -139,7 +147,7 @@ describe('Course card works as expected', () => {
const viewCourseCTA = screen.getByText('View course', { selector: 'a' });
expect(viewCourseCTA).toBeInTheDocument();
expect(viewCourseCTA.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/course/course-123x');
- const assignCourseCTA = screen.getByText('Assign', { selector: 'button' });
+ const assignCourseCTA = getButtonElement('Assign');
expect(assignCourseCTA).toBeInTheDocument();
});
@@ -167,9 +175,42 @@ describe('Course card works as expected', () => {
expect(viewCourseCTA.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/executive-education-2u/course/exec-ed-course-123x');
});
- test('opens assignment modal', () => {
+ test.each([
+ { shouldSubmitAssignments: true, hasAllocationException: true },
+ { shouldSubmitAssignments: true, hasAllocationException: false },
+ { shouldSubmitAssignments: false, hasAllocationException: false },
+ ])('opens assignment modal, submits assignments successfully (%s)', async ({ shouldSubmitAssignments, hasAllocationException }) => {
+ const mockAllocateContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'allocateContentAssignments');
+ if (hasAllocationException) {
+ mockAllocateContentAssignments.mockRejectedValue(new Error('oops'));
+ } else {
+ mockAllocateContentAssignments.mockResolvedValue({
+ data: {
+ updated: [],
+ created: mockLearnerEmails.map(learnerEmail => ({
+ uuid: '095be615-a8ad-4c33-8e9c-c7612fbf6c9f',
+ assignment_configuration: 'fd456a98-653b-41e9-94d1-94d7b136832a',
+ learner_email: learnerEmail,
+ lms_user_id: 0,
+ content_key: 'string',
+ content_title: 'string',
+ content_quantity: 0,
+ state: 'allocated',
+ transaction_uuid: '3a6bcbed-b7dc-4791-84fe-b20f12be4001',
+ last_notification_at: '2019-08-24T14:15:22Z',
+ actions: [],
+ })),
+ no_change: [],
+ },
+ });
+ }
+ useBudgetId.mockReturnValue({ subsidyAccessPolicyId: mockSubsidyAccessPolicy.uuid });
+ const mockInvalidateQueries = jest.fn();
+ useQueryClient.mockReturnValue({
+ invalidateQueries: mockInvalidateQueries,
+ });
renderWithRouter( );
- const assignCourseCTA = screen.getByText('Assign', { selector: 'button' });
+ const assignCourseCTA = getButtonElement('Assign');
expect(assignCourseCTA).toBeInTheDocument();
userEvent.click(assignCourseCTA);
@@ -190,7 +231,7 @@ describe('Course card works as expected', () => {
expect(cardImage).toBeInTheDocument();
expect(cardImage.src).toBeDefined();
expect(modalCourseCard.queryByText('View course', { selector: 'a' })).not.toBeInTheDocument();
- expect(modalCourseCard.queryByText('Assign', { selector: 'button' })).not.toBeInTheDocument();
+ expect(getButtonElement('Assign', { screenOverride: modalCourseCard, isQueryByRole: true })).not.toBeInTheDocument();
// Verify empty state and textarea can accept emails
expect(assignmentModal.getByText('Assign to')).toBeInTheDocument();
@@ -227,13 +268,43 @@ describe('Course card works as expected', () => {
// Verify modal footer
expect(assignmentModal.getByText('Help Center: Course Assignments')).toBeInTheDocument();
- const cancelAssignmentCTA = assignmentModal.getByText('Cancel', { selector: 'button' });
+ const cancelAssignmentCTA = getButtonElement('Cancel', { screenOverride: assignmentModal });
expect(cancelAssignmentCTA).toBeInTheDocument();
- const submitAssignmentCTA = assignmentModal.getByText('Assign', { selector: 'button' });
+ const submitAssignmentCTA = getButtonElement('Assign', { screenOverride: assignmentModal });
expect(submitAssignmentCTA).toBeInTheDocument();
- // Verify modal closes
- userEvent.click(cancelAssignmentCTA);
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ if (shouldSubmitAssignments) {
+ // Verify assignment is submitted successfully
+ userEvent.click(submitAssignmentCTA);
+ await waitFor(() => expect(mockAllocateContentAssignments).toHaveBeenCalledTimes(1));
+ expect(mockAllocateContentAssignments).toHaveBeenCalledWith(
+ mockSubsidyAccessPolicy.uuid,
+ expect.objectContaining({
+ content_price_cents: 10000,
+ content_key: 'course-123x',
+ learner_emails: mockLearnerEmails,
+ }),
+ );
+
+ if (hasAllocationException) {
+ // Verify error state
+ expect(getButtonElement('Try again', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'false');
+ } else {
+ // Verify success state
+ expect(mockInvalidateQueries).toHaveBeenCalledTimes(1);
+ expect(mockInvalidateQueries).toHaveBeenCalledWith({
+ queryKey: learnerCreditManagementQueryKeys.budget(mockSubsidyAccessPolicy.uuid),
+ });
+ expect(getButtonElement('Assigned', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'true');
+ // Verify modal closes
+ await waitFor(() => {
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ });
+ }
+ } else {
+ // Otherwise, verify modal closes when cancel button is clicked
+ userEvent.click(cancelAssignmentCTA);
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ }
});
});
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 1b63968106..e23dbb5273 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -1,16 +1,67 @@
-import React from 'react';
+import React, { useState } from 'react';
import PropTypes from 'prop-types';
+import { useRouteMatch, useHistory, generatePath } from 'react-router-dom';
import {
FullscreenModal,
ActionRow,
Button,
useToggle,
Hyperlink,
+ StatefulButton,
} from '@edx/paragon';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { snakeCaseObject } from '@edx/frontend-platform/utils';
+
import AssignmentModalContent from './AssignmentModalContent';
+import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
+import { learnerCreditManagementQueryKeys, useBudgetId } from '../data';
+
+const useAllocateContentAssignments = () => useMutation({
+ mutationFn: async ({
+ subsidyAccessPolicyId,
+ payload,
+ }) => EnterpriseAccessApiService.allocateContentAssignments(subsidyAccessPolicyId, payload),
+});
const NewAssignmentModalButton = ({ course, children }) => {
+ const history = useHistory();
+ const routeMatch = useRouteMatch();
+ const queryClient = useQueryClient();
+ const { subsidyAccessPolicyId } = useBudgetId();
+
const [isOpen, open, close] = useToggle(false);
+ const [learnerEmails, setLearnerEmails] = useState([]);
+ const [assignButtonState, setAssignButtonState] = useState('default');
+
+ const { mutate } = useAllocateContentAssignments();
+
+ const pathToActivityTab = generatePath(routeMatch.path, { budgetId: subsidyAccessPolicyId, activeTabKey: 'activity' });
+
+ const handleAllocateContentAssignments = () => {
+ const payload = snakeCaseObject({
+ contentPriceCents: course.normalizedMetadata.contentPrice * 100, // Convert to USD cents
+ contentKey: course.key,
+ learnerEmails,
+ });
+ const mutationArgs = {
+ subsidyAccessPolicyId,
+ payload,
+ };
+ setAssignButtonState('pending');
+ mutate(mutationArgs, {
+ onSuccess: () => {
+ setAssignButtonState('complete');
+ queryClient.invalidateQueries({
+ queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
+ });
+ close();
+ history.push(pathToActivityTab);
+ },
+ onError: () => {
+ setAssignButtonState('error');
+ },
+ });
+ };
return (
<>
@@ -27,12 +78,21 @@ const NewAssignmentModalButton = ({ course, children }) => {
Cancel
- {/* TODO: https://2u-internal.atlassian.net/browse/ENT-7826 */}
- Assign
+
)}
>
-
+
>
);
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
index cdb3b3e9bf..3290ef90dc 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
@@ -1,4 +1,4 @@
-import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
+import { QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
import useBudgetDetailActivityOverview from './useBudgetDetailActivityOverview';
@@ -12,22 +12,15 @@ import {
mockEnterpriseOfferId,
mockSubsidyAccessPolicyUUID,
} from '../tests/constants';
+import { queryClient } from '../../../test/testUtils';
jest.mock('./useBudgetId');
jest.mock('./useSubsidyAccessPolicy');
const mockEnterpriseUUID = 'mock-enterprise-uuid';
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
-});
-
const wrapper = ({ children }) => (
-
+
{children}
);
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
index 30edc0f3cc..cc773fcb79 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
@@ -1,8 +1,9 @@
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
import useSubsidyAccessPolicy from './useSubsidyAccessPolicy'; // Import the hook
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import { queryClient } from '../../../test/testUtils';
const mockSubsidyAccessPolicyUUID = '9af340a9-48de-4d94-976d-e2282b9eb7f3';
const mockAssignmentConfiguration = { uuid: 'test-assignment-configuration-uuid' };
@@ -18,16 +19,8 @@ jest.mock('../../../../data/services/EnterpriseAccessApiService', () => ({
}),
}));
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
-});
-
const wrapper = ({ children }) => (
- {children}
+ {children}
);
describe('useSubsidyAccessPolicy', () => {
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 1be7a7488a..6c0a0c8040 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { useParams } from 'react-router-dom';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryClientProvider } from '@tanstack/react-query';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
@@ -29,6 +29,7 @@ import {
mockSubsidyAccessPolicyUUID,
mockEnterpriseOfferId,
} from '../data/tests/constants';
+import { queryClient } from '../../test/testUtils';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@@ -98,8 +99,6 @@ const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
-const queryClient = new QueryClient();
-
const BudgetDetailPageWrapper = ({
initialState = initialStoreState,
enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue,
@@ -107,7 +106,7 @@ const BudgetDetailPageWrapper = ({
}) => {
const store = getMockStore({ ...initialState });
return (
-
+
@@ -124,6 +123,24 @@ describe(' ', () => {
jest.resetAllMocks();
});
+ it('renders page not found messaging if budget is a subsidy access policy, but the REST API returns a 404', () => {
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ isError: true,
+ error: { customAttributes: { httpErrorStatus: 404 } },
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: mockEmptyStateBudgetDetailActivityOverview,
+ });
+ renderWithRouter( );
+ expect(screen.getByText('404', { selector: 'h1' }));
+ });
+
it.each([
{ displayName: null },
{ displayName: 'Test Budget Display Name' },
diff --git a/src/components/learner-credit-management/tests/CatalogSearch.test.jsx b/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
index ab6a0e6a01..dc17350f21 100644
--- a/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearch.test.jsx
@@ -6,8 +6,8 @@ import {
} from '@edx/frontend-enterprise-catalog-search';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { screen } from '@testing-library/react';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { renderWithRouter } from '../../test/testUtils';
+import { QueryClientProvider } from '@tanstack/react-query';
+import { queryClient, renderWithRouter } from '../../test/testUtils';
import CatalogSearch from '../search/CatalogSearch';
import { useBudgetId, useSubsidyAccessPolicy } from '../data';
@@ -21,10 +21,9 @@ jest.mock('react-instantsearch-dom', () => ({
jest.mock('../data');
const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
-const queryClient = new QueryClient();
const SearchDataWrapper = ({ children, searchContextValue }) => (
-
+
{
const store = getMockStore({ ...initialStoreState });
return (
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
);
};
@@ -153,7 +156,7 @@ describe('Main Catalogs view works as expected', () => {
});
test('all courses rendered when search results available', async () => {
- render(
+ renderWithRouter(
diff --git a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
index cd0b7dfdd1..5ac24d2601 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
@@ -1,9 +1,6 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
-import {
- QueryClient,
- QueryClientProvider,
-} from '@tanstack/react-query';
+import { QueryClientProvider } from '@tanstack/react-query';
import userEvent from '@testing-library/user-event';
import {
act,
@@ -19,12 +16,7 @@ import { features } from '../../../../config';
import NewExistingSSOConfigs from '../NewExistingSSOConfigs';
import { SSOConfigContext, SSO_INITIAL_STATE } from '../SSOConfigContext';
import LmsApiService from '../../../../data/services/LmsApiService';
-
-const queryClient = new QueryClient({
- queries: {
- retry: true, // optional: you may disable automatic query retries for all queries or on a per-query basis.
- },
-});
+import { queryClient } from '../../../test/testUtils';
jest.mock('../../utils');
jest.mock('../../../../data/services/LmsApiService');
@@ -152,7 +144,7 @@ const setupNewExistingSSOConfigs = (configs) => {
features.AUTH0_SELF_SERVICE_INTEGRATION = true;
return render(
-
+
mockStore(aStore);
@@ -102,7 +95,7 @@ describe('SAML Config Tab', () => {
}));
await waitFor(() => render(
-
+
,
diff --git a/src/components/test/testUtils.jsx b/src/components/test/testUtils.jsx
index e4a22ba445..931afffc6e 100644
--- a/src/components/test/testUtils.jsx
+++ b/src/components/test/testUtils.jsx
@@ -2,8 +2,10 @@
import React from 'react';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
-import { render, screen } from '@testing-library/react';
+import { render, screen as rtlScreen } from '@testing-library/react';
+import { QueryClient } from '@tanstack/react-query';
+// TODO: this could likely be replaced by `renderWithRouter` from `@edx/frontend-enterprise-utils`.
export function renderWithRouter(
ui,
{
@@ -30,4 +32,25 @@ export function findElementWithText(container, type, text) {
return [...elements].find((elem) => elem.innerHTML.includes(text));
}
-export const getButtonElement = (buttonText) => screen.getByRole('button', { name: buttonText });
+export const getButtonElement = (buttonText, options = {}) => {
+ const {
+ screenOverride,
+ isQueryByRole,
+ } = options;
+ const screen = screenOverride || rtlScreen;
+ if (isQueryByRole) {
+ return screen.queryByRole('button', { name: buttonText });
+ }
+ return screen.getByRole('button', { name: buttonText });
+};
+
+export function queryClient(options = {}) {
+ return new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ ...options,
+ },
+ });
+}
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index be704fe658..ad431633c3 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -166,6 +166,11 @@ class EnterpriseAccessApiService {
const url = `${EnterpriseAccessApiService.baseUrl}/subsidy-access-policies/${subsidyAccessPolicyUUID}/`;
return EnterpriseAccessApiService.apiClient().get(url);
}
+
+ static allocateContentAssignments(subsidyAccessPolicyUUID, payload) {
+ const url = `${EnterpriseAccessApiService.baseUrl}/policy-allocation/${subsidyAccessPolicyUUID}/allocate/`;
+ return EnterpriseAccessApiService.apiClient().post(url, payload);
+ }
}
export default EnterpriseAccessApiService;
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index fd1497705d..7fd27b22a1 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -152,4 +152,17 @@ describe('EnterpriseAccessApiService', () => {
`${enterpriseAccessBaseUrl}/api/v1/subsidy-access-policies/${mockSubsidyAccessPolicyUUID}/`,
);
});
+
+ test('allocateContentAssignments calls enterprise-access allocate POST API to create assignments', () => {
+ const payload = {
+ learner_emails: ['edx@example.com'],
+ content_key: 'edX+DemoX',
+ content_price_cents: 19900,
+ };
+ EnterpriseAccessApiService.allocateContentAssignments(mockSubsidyAccessPolicyUUID, payload);
+ expect(axios.post).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/policy-allocation/${mockSubsidyAccessPolicyUUID}/allocate/`,
+ payload,
+ );
+ });
});
diff --git a/src/utils.js b/src/utils.js
index 871af1524a..207d25c8bc 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -400,6 +400,18 @@ const pollAsync = async (pollFunc, timeout, interval, checkFunc) => {
return false;
};
+/**
+ * Modifies the retry behavior of queries to retry up to max 3 times (default) or if
+ * the error returned by the query is a 404 HTTP status code (not found). This configuration
+ * may be overridden per-query, as needed.
+ */
+function defaultQueryClientRetryHandler(failureCount, err) {
+ if (failureCount >= 3 || err.customAttributes.httpErrorStatus === 404) {
+ return false;
+ }
+ return true;
+}
+
export {
camelCaseDict,
camelCaseDictArray,
@@ -433,4 +445,5 @@ export {
capitalizeFirstLetter,
pollAsync,
isNotValidNumberString,
+ defaultQueryClientRetryHandler,
};
diff --git a/src/utils.test.js b/src/utils.test.js
index c2c713bdc1..977582e89c 100644
--- a/src/utils.test.js
+++ b/src/utils.test.js
@@ -5,6 +5,7 @@ import {
snakeCaseFormData,
pollAsync,
isValidNumber,
+ defaultQueryClientRetryHandler,
} from './utils';
describe('utils', () => {
@@ -93,4 +94,24 @@ describe('utils', () => {
expect(isValidNumber(undefined)).toEqual(false);
});
});
+
+ describe('defaultQueryClientRetryHandler', () => {
+ const mockError404 = { customAttributes: { httpErrorStatus: 404 } };
+ const mockError500 = { customAttributes: { httpErrorStatus: 500 } };
+
+ it.each([3, 4])('return false if failureCount >= 3 (failureCount: %s)', (failureCount) => {
+ const result = defaultQueryClientRetryHandler(failureCount, mockError500);
+ expect(result).toEqual(false);
+ });
+
+ it('return false if error is a 404 HTTP status code', () => {
+ const result = defaultQueryClientRetryHandler(1, mockError404);
+ expect(result).toEqual(false);
+ });
+
+ it.each([1, 2])('return true if first failure and error is not a 404 (failureCount: %s)', (failureCount) => {
+ const result = defaultQueryClientRetryHandler(failureCount, mockError500);
+ expect(result).toEqual(true);
+ });
+ });
});
From bb42b6020019280053843440d7df47aeeae8bf50 Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Mon, 6 Nov 2023 11:57:57 -0700
Subject: [PATCH 060/124] fix: altering bulk enrollment action behaviour
(#1081)
* fix: altering bulk enrollment action behaviour
* fix: PR requests
* fix: PR requests
---
.../tests/DeleteHighlightSet.test.jsx | 2 +-
.../AssignmentRowActionTableCell.jsx | 56 ++++++------
.../AssignmentTableRemind.jsx | 17 ++--
.../tests/BudgetDetailPage.test.jsx | 88 +++++--------------
.../steps/SSOConfigConfigureStep.jsx | 2 +-
.../services/EnterpriseAccessApiService.js | 2 +-
6 files changed, 59 insertions(+), 108 deletions(-)
diff --git a/src/components/ContentHighlights/tests/DeleteHighlightSet.test.jsx b/src/components/ContentHighlights/tests/DeleteHighlightSet.test.jsx
index 336cb008c0..ffb283c5fe 100644
--- a/src/components/ContentHighlights/tests/DeleteHighlightSet.test.jsx
+++ b/src/components/ContentHighlights/tests/DeleteHighlightSet.test.jsx
@@ -97,7 +97,7 @@ describe(' ', () => {
expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
});
- it('cancelling confirmation modal closes modal', () => {
+ it('canceling confirmation modal closes modal', () => {
renderWithRouter(
,
{ route: initialRouterEntry },
diff --git a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
index 1d771c27fa..a180598d31 100644
--- a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
@@ -1,62 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- Icon,
- IconButton,
- OverlayTrigger,
- Stack,
- Tooltip,
+ Icon, IconButton, OverlayTrigger, Stack, Tooltip,
} from '@edx/paragon';
import { Mail, DoNotDisturbOn } from '@edx/paragon/icons';
const AssignmentRowActionTableCell = ({ row }) => {
- const cancelButtonMarginLeft = row.original.state === 'allocated' ? 'ml-2.5' : 'ml-auto';
+ const isLearnerStateWaiting = row.original.learnerState === 'waiting';
+ const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
return (
-
- {row.original.state === 'allocated' && (
- <>
- Remind learner}
- >
- console.log(`Reminding ${row.original.uuid}`)}
- data-testid={`remind-assignment-${row.original.uuid}`}
- />
-
-
- >
+
+ {isLearnerStateWaiting && (
+ Remind learner}
+ >
+ console.log(`Reminding ${row.original.uuid}`)}
+ data-testid={`remind-assignment-${row.original.uuid}`}
+ />
+
)}
Cancel assignment}
+ overlay={Cancel assignment }
>
console.log(`Canceling ${row.original.uuid}`)}
data-testid={`cancel-assignment-${row.original.uuid}`}
/>
-
+
);
};
AssignmentRowActionTableCell.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
+ learnerEmail: PropTypes.string,
+ learnerState: PropTypes.string,
uuid: PropTypes.string.isRequired,
- state: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
};
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
index bee8cc9814..cc62c9a703 100644
--- a/src/components/learner-credit-management/AssignmentTableRemind.jsx
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -4,16 +4,15 @@ import { Button } from '@edx/paragon';
import { Mail } from '@edx/paragon/icons';
const AssignmentTableRemindAction = ({ selectedFlatRows, ...rest }) => {
- const hideRemindAction = selectedFlatRows.some(
- row => row.original.state !== 'allocated',
- );
- if (hideRemindAction) {
- return null;
- }
+ const selectedRemindableRows = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').length;
return (
- // eslint-disable-next-line no-console
- console.log('Remind', selectedFlatRows, rest)}>
- {`Remind (${selectedFlatRows.length})`}
+ console.log('Remind', selectedFlatRows, rest)}
+ >
+ {`Remind (${selectedRemindableRows})`}
);
};
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 6c0a0c8040..5f3beec3a5 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -756,7 +756,16 @@ describe(' ', () => {
expect(screen.getByText('loading budget activity overview')).toBeInTheDocument();
});
- it('displays remind row and bulk actions when allocated', async () => {
+ it.each([
+ {
+ learnerState: 'waiting',
+ shouldDisplayRemindAction: true,
+ },
+ {
+ learnerState: 'notifying',
+ shouldDisplayRemindAction: false,
+ },
+ ])('displays remind and cancel row and bulk actions when appropriate (%s)', async ({ learnerState, shouldDisplayRemindAction }) => {
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
@@ -786,7 +795,7 @@ describe(' ', () => {
uuid: 'test-uuid',
contentKey: mockCourseKey,
contentQuantity: -19900,
- learnerState: 'active',
+ learnerState,
recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions: [],
errorReason: null,
@@ -800,73 +809,22 @@ describe(' ', () => {
});
renderWithRouter( );
const cancelRowAction = screen.getByTestId('cancel-assignment-test-uuid');
- const remindRowAction = screen.getByTestId('remind-assignment-test-uuid');
expect(cancelRowAction).toBeInTheDocument();
- expect(remindRowAction).toBeInTheDocument();
+ if (shouldDisplayRemindAction) {
+ const remindRowAction = screen.getByTestId('remind-assignment-test-uuid');
+ expect(remindRowAction).toBeInTheDocument();
+ }
const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell');
expect(checkBox).toBeInTheDocument();
userEvent.click(checkBox);
- await waitFor(() => {
- expect(screen.getByText('Remind (1)')).toBeInTheDocument();
- });
- await waitFor(() => {
- expect(screen.getByText('Cancel (1)')).toBeInTheDocument();
- });
- });
-
- it('hides remind row and bulk actions when allocated', () => {
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
- });
- useBudgetDetailActivityOverview.mockReturnValue({
- isLoading: false,
- data: {
- contentAssignments: { count: 1 },
- spentTransactions: { count: 0 },
- },
- });
- useParams.mockReturnValue({
- budgetId: mockSubsidyAccessPolicyUUID,
- activeTabKey: 'activity',
- });
- useSubsidyAccessPolicy.mockReturnValue({
- isInitialLoading: false,
- data: mockAssignableSubsidyAccessPolicy,
- });
- useBudgetDetailActivityOverview.mockReturnValue({
- isLoading: false,
- data: {
- contentAssignments: { count: 1 },
- spentTransactions: { count: 0 },
- },
- });
- useBudgetContentAssignments.mockReturnValue({
- isLoading: false,
- contentAssignments: {
- count: 1,
- results: [
- {
- uuid: 'test-uuid',
- contentKey: mockCourseKey,
- contentQuantity: -19900,
- learnerState: 'accepted',
- recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
- actions: [],
- state: 'accepted',
- },
- ],
- numPages: 1,
- currentPage: 1,
- },
- fetchContentAssignments: jest.fn(),
- });
- renderWithRouter( );
- expect(screen.queryByTestId('remind-assignment-test-uuid')).not.toBeInTheDocument();
- const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell');
- userEvent.click(checkBox);
- expect(screen.queryByText('Remind (1)')).not.toBeInTheDocument();
+ expect(await screen.findByText('Cancel (1)')).toBeInTheDocument();
+ if (shouldDisplayRemindAction) {
+ expect(await screen.findByText('Remind (1)')).toBeInTheDocument();
+ } else {
+ const remindButton = await screen.findByText('Remind (0)');
+ expect(remindButton).toBeInTheDocument();
+ expect(remindButton).toBeDisabled();
+ }
});
});
diff --git a/src/components/settings/SettingsSSOTab/steps/SSOConfigConfigureStep.jsx b/src/components/settings/SettingsSSOTab/steps/SSOConfigConfigureStep.jsx
index d5c61adc1c..62c615e8fb 100644
--- a/src/components/settings/SettingsSSOTab/steps/SSOConfigConfigureStep.jsx
+++ b/src/components/settings/SettingsSSOTab/steps/SSOConfigConfigureStep.jsx
@@ -449,7 +449,7 @@ const SSOConfigConfigureStep = ({
/>
Configurable value that represents the amount of time in seconds, no greater than 30, that the
- edX system will wait for a response before cancelling the request.
+ edX system will wait for a response before canceling the request.
{!odataApiTimeoutIntervalValid && (
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index ad431633c3..b115f194fb 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -151,7 +151,7 @@ class EnterpriseAccessApiService {
page: 1,
page_size: 25,
// Only include assignments with allocated or errored states. The table should NOT
- // include assignments in the cancelled or accepted states.
+ // include assignments in the canceled or accepted states.
state__in: 'allocated,errored',
...snakeCaseObject(options),
});
From 1f1c1ac29292cd31ab77cae49f5197e333231518 Mon Sep 17 00:00:00 2001
From: Marlon Keating <322346+marlonkeating@users.noreply.github.com>
Date: Mon, 6 Nov 2023 11:26:57 -0800
Subject: [PATCH 061/124] feat: Hook up sso config workflow to api (#1072)
chore: Address code review feedback
chore: Address code review feedback
fix: SAP field validation
fix: Add username field
---
src/components/forms/FormContextWrapper.tsx | 16 +-
src/components/forms/FormWorkflow.tsx | 32 +++-
src/components/forms/ValidatedFormControl.tsx | 10 +-
src/components/forms/ValidatedFormRadio.tsx | 13 +-
src/components/forms/data/reducer.test.ts | 2 +
src/components/forms/data/reducer.ts | 7 +-
.../SettingsLMSTab/UnsavedChangesModal.tsx | 8 +-
.../settings/SettingsSSOTab/NewSSOStepper.jsx | 35 +++-
.../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 134 +++++++++++++-
.../SettingsSSOTab/UnsavedSSOChangesModal.tsx | 36 ++++
.../steps/NewSSOConfigAuthorizeStep.tsx | 120 ++++++++----
.../steps/NewSSOConfigConfigureStep.tsx | 78 ++++++--
.../steps/NewSSOConfigConnectStep.tsx | 98 +++++++---
.../tests/NewSSOConfigForm.test.jsx | 173 +++++++++++++-----
14 files changed, 584 insertions(+), 178 deletions(-)
create mode 100644 src/components/settings/SettingsSSOTab/UnsavedSSOChangesModal.tsx
diff --git a/src/components/forms/FormContextWrapper.tsx b/src/components/forms/FormContextWrapper.tsx
index a6d745f896..fa3c8a2b75 100644
--- a/src/components/forms/FormContextWrapper.tsx
+++ b/src/components/forms/FormContextWrapper.tsx
@@ -1,11 +1,15 @@
import React, { useReducer } from 'react';
import FormContextProvider from './FormContext';
-import FormWorkflow, { FormWorkflowProps } from './FormWorkflow';
+import FormWorkflow, { DynamicComponent, FormWorkflowProps, UnsavedChangesModalProps } from './FormWorkflow';
import {
FormReducer, FormReducerType, initializeForm, InitializeFormArguments,
} from './data/reducer';
+import DefaultUnsavedChangesModal from '../settings/SettingsLMSTab/UnsavedChangesModal';
-type FormWrapperProps = FormWorkflowProps & { formData: FormConfigData };
+type FormWrapperProps = FormWorkflowProps & {
+ formData: FormConfigData;
+ unsavedChangesModal?: DynamicComponent;
+};
const FormContextWrapper = ({
workflowTitle,
@@ -13,6 +17,7 @@ const FormContextWrapper = ({
onClickOut,
formData,
isStepperOpen,
+ UnsavedChangesModal,
}: FormWrapperProps) => {
const initializeAction: InitializeFormArguments = {
formFields: formData as FormConfigData,
@@ -33,7 +38,12 @@ const FormContextWrapper = ({
>
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index d3bb072534..80288c075a 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -8,10 +8,14 @@ import { Launch } from '@edx/paragon/icons';
import { useFormContext } from './FormContext';
import type { FormFieldValidation, FormContext } from './FormContext';
import {
- FORM_ERROR_MESSAGE, FormActionArguments, setStepAction, setWorkflowStateAction, setShowErrorsAction,
+ FORM_ERROR_MESSAGE,
+ FormActionArguments,
+ setStepAction,
+ setWorkflowStateAction,
+ setShowErrorsAction,
+ updateFormFieldsAction,
} from './data/actions';
import { HELP_CENTER_LINK, SUBMIT_TOAST_MESSAGE } from '../settings/data/constants';
-import UnsavedChangesModal from '../settings/SettingsLMSTab/UnsavedChangesModal';
import ConfigErrorModal from '../settings/ConfigErrorModal';
import { channelMapping, pollAsync } from '../../utils';
import HelpCenterButton from '../settings/HelpCenterButton';
@@ -21,7 +25,7 @@ export const WAITING_FOR_ASYNC_OPERATION = 'WAITING FOR ASYNC OPERATION';
export type FormWorkflowErrorHandler = (errMsg: string) => void;
export type FormWorkflowHandlerArgs = {
- formFields?: FormData;
+ formFields: FormData;
formFieldsChanged: boolean;
errHandler?: FormWorkflowErrorHandler;
dispatch?: Dispatch;
@@ -41,12 +45,15 @@ export type FormWorkflowButtonConfig = {
awaitSuccess?: FormWorkflowAwaitHandler;
};
-type DynamicComponent = React.FunctionComponent | React.ComponentClass | React.ElementType;
+export type DynamicComponent =
+ | React.FunctionComponent
+ | React.ComponentClass
+ | React.ElementType;
export type FormWorkflowStep = {
index: number;
stepName: string;
- formComponent: DynamicComponent;
+ formComponent: DynamicComponent;
validations: FormFieldValidation[];
saveChanges?: (
formData: FormData,
@@ -62,12 +69,20 @@ export type FormWorkflowConfig = {
getCurrentStep: () => FormWorkflowStep;
};
+export type UnsavedChangesModalProps = {
+ isOpen: boolean;
+ close: () => void;
+ exitWithoutSaving?: () => void;
+ saveDraft?: () => void
+};
+
export type FormWorkflowProps = {
workflowTitle: string;
formWorkflowConfig: FormWorkflowConfig;
onClickOut: (() => void) | ((edited?: boolean, msg?: string) => null);
dispatch: Dispatch;
isStepperOpen: boolean;
+ UnsavedChangesModal?: DynamicComponent;
};
// Modal container for multi-step forms
@@ -77,6 +92,7 @@ const FormWorkflow = ({
onClickOut,
isStepperOpen,
dispatch,
+ UnsavedChangesModal,
}: FormWorkflowProps) => {
const {
formFields,
@@ -121,6 +137,9 @@ const FormWorkflow = ({
dispatch,
formFieldsChanged: !!isEdited,
});
+ if (newFormFields) {
+ dispatch(updateFormFieldsAction({ formFields: newFormFields }));
+ }
if (nextButtonConfig?.awaitSuccess) {
advance = await pollAsync(
() => nextButtonConfig.awaitSuccess?.awaitCondition?.({
@@ -165,7 +184,7 @@ const FormWorkflow = ({
const stepBody = (currentStep: FormWorkflowStep) => {
if (currentStep) {
- const FormComponent: DynamicComponent = currentStep?.formComponent;
+ const FormComponent: DynamicComponent = currentStep?.formComponent;
return (
({
close={clearFormError}
configTextOverride={stateMap && stateMap[FORM_ERROR_MESSAGE]}
/>
+ {/* @ts-ignore JSX element type 'UnsavedChangesModal' does not have any construct or call signatures. */}
{
id: props.formId,
value: formFields && formFields[props.formId],
};
- // we need to set the original values on load in order to trigger the validation
- useEffect(() => {
- if (dispatch) {
- dispatch(
- setFormFieldAction({ fieldId: props.formId, value: formControlProps.value }),
- );
- }
- }, [dispatch, props.formId, formControlProps.value]);
return (
<>
diff --git a/src/components/forms/ValidatedFormRadio.tsx b/src/components/forms/ValidatedFormRadio.tsx
index a37e6e0b8c..bfc29fb2d2 100644
--- a/src/components/forms/ValidatedFormRadio.tsx
+++ b/src/components/forms/ValidatedFormRadio.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement, useEffect } from 'react';
+import React, { ReactElement } from 'react';
import omit from 'lodash/omit';
import isString from 'lodash/isString';
@@ -30,7 +30,7 @@ const ValidatedFormRadio = (props: ValidatedFormRadioProps) => {
dispatch(setFormFieldAction({ fieldId: props.formId, value: e.target.value }));
}
};
-
+ const value = formFields?.[props?.formId];
const errors = errorMap?.[props.formId];
// Show error message if an error message was part of any detected errors
const showError = errors?.find?.(error => isString(error));
@@ -41,14 +41,6 @@ const ValidatedFormRadio = (props: ValidatedFormRadioProps) => {
id: props.formId,
value: formFields && formFields[props.formId],
};
- // we need to set the original values on load in order to trigger the validation
- useEffect(() => {
- if (dispatch) {
- dispatch(
- setFormFieldAction({ fieldId: props.formId, value: formRadioProps.value }),
- );
- }
- }, [dispatch, props.formId, formRadioProps.value]);
const createOptions = (options: [string, string][]) => {
const optionList: ReactElement[] = [];
@@ -71,6 +63,7 @@ const ValidatedFormRadio = (props: ValidatedFormRadioProps) => {
name={formRadioProps.id}
onChange={formRadioProps.onChange}
isInline={formRadioProps.isInline}
+ value={value}
>
{createOptions(formRadioProps.options)}
diff --git a/src/components/forms/data/reducer.test.ts b/src/components/forms/data/reducer.test.ts
index 6c3838d6fa..988102d634 100644
--- a/src/components/forms/data/reducer.test.ts
+++ b/src/components/forms/data/reducer.test.ts
@@ -80,6 +80,8 @@ describe('Form reducer tests', () => {
};
expect(initializeForm(initializeFormArguments)).toEqual({
formFields,
+ errorMap: {},
+ hasErrors: false,
currentStep: steps[0],
isEdited: false,
});
diff --git a/src/components/forms/data/reducer.ts b/src/components/forms/data/reducer.ts
index 1aa424be21..afee5e8c8c 100644
--- a/src/components/forms/data/reducer.ts
+++ b/src/components/forms/data/reducer.ts
@@ -86,9 +86,7 @@ export function initializeForm(action: InitializeFormArguments FormContext;
@@ -120,7 +118,8 @@ export const FormReducer: FormReducerType = (
};
} case SET_STEP: {
const setStepArgs = action as SetStepArguments;
- return { ...state, currentStep: setStepArgs.step };
+ const newStepState = { ...state, currentStep: setStepArgs.step };
+ return processFormErrors(newStepState);
} case SET_SHOW_ERRORS: {
const SetShowErrorsArgs = action as SetShowErrorsArguments;
return { ...state, showErrors: SetShowErrorsArgs.showErrors };
diff --git a/src/components/settings/SettingsLMSTab/UnsavedChangesModal.tsx b/src/components/settings/SettingsLMSTab/UnsavedChangesModal.tsx
index ac09b409aa..1eba8b9e8d 100644
--- a/src/components/settings/SettingsLMSTab/UnsavedChangesModal.tsx
+++ b/src/components/settings/SettingsLMSTab/UnsavedChangesModal.tsx
@@ -1,16 +1,10 @@
import React from 'react';
import { ModalDialog, ActionRow, Button } from '@edx/paragon';
+import { UnsavedChangesModalProps } from '../../forms/FormWorkflow';
const MODAL_TITLE = 'Exit configuration';
const MODAL_TEXT = 'Your configuration data will be saved under your Learning Platform settings';
-type UnsavedChangesModalProps = {
- isOpen: boolean;
- close: () => void;
- exitWithoutSaving: () => void;
- saveDraft: () => void
-};
-
// will have to pass in individual saveDraft method and config when
// drafting is allowed
const UnsavedChangesModal = ({
diff --git a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
index 55ae8eb1f0..80f23b4d69 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
@@ -1,31 +1,54 @@
-import React, { useState, useContext } from 'react';
+import React, {
+ useState, useContext,
+} from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
import FormContextWrapper from '../../forms/FormContextWrapper';
import { SSOConfigContext } from './SSOConfigContext';
import SSOFormWorkflowConfig from './SSOFormWorkflowConfig';
+import { camelCaseDict } from '../../../utils';
+import UnsavedSSOChangesModal from './UnsavedSSOChangesModal';
+import { IDP_URL_SELECTION, IDP_XML_SELECTION } from './steps/NewSSOConfigConnectStep';
-const NewSSOStepper = () => {
+const NewSSOStepper = ({ enterpriseId }) => {
const {
- setProviderConfig,
+ setProviderConfig, refreshBool, setRefreshBool, ssoState: { providerConfig },
} = useContext(SSOConfigContext);
+ const providerConfigCamelCase = camelCaseDict(providerConfig || {});
const [isStepperOpen, setIsStepperOpen] = useState(true);
const handleCloseWorkflow = () => {
setProviderConfig?.(null);
setIsStepperOpen(false);
+ setRefreshBool(!refreshBool);
};
+ if (providerConfigCamelCase.metadataXml || providerConfigCamelCase.metadataUrl) {
+ providerConfigCamelCase.idpConnectOption = providerConfigCamelCase?.metadataUrl
+ ? IDP_URL_SELECTION
+ : IDP_XML_SELECTION;
+ }
return (isStepperOpen
&& (
)
);
};
-export default NewSSOStepper;
+NewSSOStepper.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+};
+
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(NewSSOStepper);
diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
index dcaff6c46e..b27a8df687 100644
--- a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
+++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
@@ -1,37 +1,151 @@
-import type { FormWorkflowStep } from '../../forms/FormWorkflow';
-import SSOConfigConnectStep from './steps/NewSSOConfigConnectStep';
-import SSOConfigConfigureStep from './steps/NewSSOConfigConfigureStep';
-import SSOConfigAuthorizeStep from './steps/NewSSOConfigAuthorizeStep';
+import omit from 'lodash/omit';
+
+import type { FormWorkflowHandlerArgs, FormWorkflowStep } from '../../forms/FormWorkflow';
+import SSOConfigConnectStep, { validations as SSOConfigConnectStepValidations } from './steps/NewSSOConfigConnectStep';
+import SSOConfigConfigureStep, { validations as SSOConfigConfigureStepValidations } from './steps/NewSSOConfigConfigureStep';
+import SSOConfigAuthorizeStep, { validations as SSOConfigAuthorizeStepValidations } from './steps/NewSSOConfigAuthorizeStep';
import SSOConfigConfirmStep from './steps/NewSSOConfigConfirmStep';
+import LmsApiService from '../../../data/services/LmsApiService';
+import handleErrors from '../utils';
+import { snakeCaseDict } from '../../../utils';
+
+type SSOConfigSnakeCase = {
+ uuid?: string,
+ enterprise_customer: string,
+ is_removed: boolean,
+ active: boolean,
+ identity_provider: string,
+ metadata_url: string,
+ metadata_xml: string,
+ entity_id: string,
+ update_from_metadata: boolean,
+ user_id_attribute: string,
+ full_name_attribute: string,
+ last_name_attribute: string,
+ email_attribute: string,
+ username_attribute: string,
+ country_attribute: string,
+ submitted_at: null,
+ configured_at: null,
+ validated_at: null,
+ odata_api_timeout_interval: null,
+ odata_api_root_url: string,
+ odata_company_id: string,
+ sapsf_oauth_root_url: string,
+ odata_api_request_timeout: null,
+ sapsf_private_key: string,
+ odata_client_id: string,
+ oauth_user_id: string,
+ sp_metadata_url?: string
+};
+
+type SSOConfigCamelCase = {
+ uuid?: string,
+ enterpriseCustomer: string,
+ isRemoved: boolean,
+ active: boolean,
+ identityProvider: string,
+ metadataUrl: string,
+ metadataXml: string,
+ entityId: string,
+ updateFromMetadata: boolean,
+ userIdAttribute: string,
+ fullNameAttribute: string,
+ firstNameAttribute: string,
+ lastNameAttribute: string,
+ emailAttribute: string,
+ usernameAttribute: string,
+ countryAttribute: string,
+ submittedAt: null,
+ configuredAt: null,
+ validatedAt: null,
+ odataApiTimeoutInterval: null,
+ odataApiRootUrl: string,
+ odataCompanyId: string,
+ sapsfOauthRootUrl: string,
+ odataApiRequestTimeout: null,
+ sapsfPrivateKey: string,
+ odataClientId: string,
+ oauthUserId: string,
+ spMetadataUrl?: string
+};
+
+type SSOConfigFormControlVariables = {
+ idpConnectOption?: boolean,
+ confirmAuthorizedEdxServiceProvider?: boolean
+};
-type SSOConfigCamelCase = {};
+type SSOConfigFormContextData = SSOConfigCamelCase & SSOConfigFormControlVariables;
-export const SSOFormWorkflowConfig = () => {
+export const SSOFormWorkflowConfig = ({ enterpriseId }) => {
const placeHolderButton = (buttonName?: string) => () => ({
buttonText: buttonName || 'Next',
opensNewWindow: false,
onClick: () => {},
});
+ const saveChanges = async ({
+ formFields,
+ errHandler,
+ formFieldsChanged,
+ }:FormWorkflowHandlerArgs) => {
+ let err = null;
+ if (!formFieldsChanged) {
+ // Don't submit if nothing has changed
+ return formFields;
+ }
+ let updatedFormFields: SSOConfigCamelCase = omit(formFields, ['idpConnectOption', 'spMetadataUrl', 'isPendingConfiguration']);
+ updatedFormFields.enterpriseCustomer = enterpriseId;
+ const submittedFormFields: SSOConfigSnakeCase = snakeCaseDict(updatedFormFields) as SSOConfigSnakeCase;
+ if (submittedFormFields?.uuid) {
+ try {
+ const updateResponse = await LmsApiService.updateEnterpriseSsoOrchestrationRecord(
+ submittedFormFields,
+ formFields?.uuid,
+ );
+ updatedFormFields = updateResponse.data;
+ } catch (error) {
+ err = handleErrors(error);
+ }
+ } else {
+ try {
+ const createResponse = await LmsApiService.createEnterpriseSsoOrchestrationRecord(submittedFormFields);
+ updatedFormFields.uuid = createResponse.data.record;
+ updatedFormFields.spMetadataUrl = createResponse.data.sp_metadata_url;
+ } catch (error) {
+ err = handleErrors(error);
+ }
+ }
+ if (err && errHandler) {
+ errHandler(err);
+ }
+ const newFormFields = { ...formFields, ...updatedFormFields } as SSOConfigCamelCase;
+ return newFormFields;
+ };
+
const steps: FormWorkflowStep[] = [
{
index: 0,
formComponent: SSOConfigConnectStep,
- validations: [],
+ validations: SSOConfigConnectStepValidations,
stepName: 'Connect',
nextButtonConfig: placeHolderButton(),
}, {
index: 1,
formComponent: SSOConfigConfigureStep,
- validations: [],
+ validations: SSOConfigConfigureStepValidations,
stepName: 'Configure',
- nextButtonConfig: placeHolderButton('Configure'),
+ nextButtonConfig: () => ({
+ buttonText: 'Configure',
+ opensNewWindow: false,
+ onClick: saveChanges,
+ }),
showBackButton: true,
showCancelButton: false,
}, {
index: 2,
formComponent: SSOConfigAuthorizeStep,
- validations: [],
+ validations: SSOConfigAuthorizeStepValidations,
stepName: 'Authorize',
nextButtonConfig: placeHolderButton(),
showBackButton: true,
diff --git a/src/components/settings/SettingsSSOTab/UnsavedSSOChangesModal.tsx b/src/components/settings/SettingsSSOTab/UnsavedSSOChangesModal.tsx
new file mode 100644
index 0000000000..7f4711347a
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/UnsavedSSOChangesModal.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { ModalDialog, ActionRow, Button } from '@edx/paragon';
+import { UnsavedChangesModalProps } from '../../forms/FormWorkflow';
+
+const UnsavedSSOChangesModal = ({
+ isOpen,
+ close,
+ exitWithoutSaving,
+}: UnsavedChangesModalProps) => (
+
+
+ Exit configuration?
+
+
+ Your in-progress data will not be saved.
+ Your SSO connection will not be active until you restart and complete the SSO configuration process.
+
+
+
+
+ Cancel
+
+
+ Exit
+
+
+
+
+);
+
+export default UnsavedSSOChangesModal;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
index 1d9fe823b2..6e62cb3524 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
@@ -1,38 +1,96 @@
-import React from 'react';
+import React, { useContext } from 'react';
+import { useParams } from 'react-router-dom';
import {
Alert, Form, Hyperlink, Button, Row,
} from '@edx/paragon';
import { Info, Download } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
+import { createSAMLURLs } from '../utils';
+import { SSOConfigContext } from '../SSOConfigContext';
+import { setFormFieldAction } from '../../../forms/data/actions';
+import { FormFieldValidation, useFormContext } from '../../../forms/FormContext';
-const handleCheck = () => null;
-
-const SSOConfigAuthorizeStep = () => (
- <>
- Authorize edX as a Service Provider
-
- Action required in a new window
- Return to this window after completing the following steps in a new window to finish configuring your integration.
-
-
-
- 1. Download the edX Service Provider metadata as an XML file:
-
-
-
- edX Service Provider Metadata
-
-
-
- 2. Launch a new window and upload the XML file to the list of
- authorized SAML Service Providers on your Identity Provider's portal or website.
-
-
- Return to this window and check the box once complete
-
-
- I have authorized edX as a Service Provider
-
- >
-);
+export const validations: FormFieldValidation[] = [
+ {
+ formFieldId: 'confirmAuthorizedEdxServiceProvider',
+ validator: (fields) => {
+ const ret = !fields.confirmAuthorizedEdxServiceProvider;
+ return ret;
+ },
+ },
+];
+
+// TODO: Move with SSOConfigContext
+type SSOProviderConfig = {
+ slug: string;
+};
+
+type SSOState = {
+ providerConfig?: SSOProviderConfig;
+};
+
+type SSOConfigContextValue = {
+ ssoState?: SSOState;
+};
+
+const SSOConfigAuthorizeStep = () => {
+ const configuration = getConfig();
+ const {
+ ssoState,
+ } = useContext(SSOConfigContext);
+ const {
+ dispatch,
+ formFields,
+ } = useFormContext();
+ const { enterpriseSlug } = useParams();
+
+ const idpSlug = ssoState?.providerConfig?.slug;
+ const learnerPortalEnabled = ssoState?.providerConfig?.slug;
+
+ const { testLink } = createSAMLURLs({
+ configuration, idpSlug, enterpriseSlug, learnerPortalEnabled,
+ });
+
+ const handleCheck = (event) => {
+ dispatch?.(
+ setFormFieldAction({ fieldId: 'confirmAuthorizedEdxServiceProvider', value: event.target.checked }),
+ );
+ };
+
+ return (
+ <>
+ Authorize edX as a Service Provider
+
+ Action required in a new window
+ Return to this window after completing the following steps
+ in a new window to finish configuring your integration.
+
+
+
+ 1. Download the edX Service Provider metadata as an XML file:
+
+
+
+ edX Service Provider Metadata
+
+
+
+
+ 2.
+
+ Launch a new window
+
+ {' '} and upload the XML file to the list of
+ authorized SAML Service Providers on your Identity Provider's portal or website.
+
+
+ Return to this window and check the box once complete
+
+
+ I have authorized edX as a Service Provider
+
+ >
+ );
+};
export default SSOConfigAuthorizeStep;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
index 50cf5b5ba2..30e347d1ef 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
@@ -4,18 +4,60 @@ import {
} from '@edx/paragon';
import ValidatedFormControl from '../../../forms/ValidatedFormControl';
+import { FormContext, FormFieldValidation, useFormContext } from '../../../forms/FormContext';
+import { urlValidation } from '../../../../utils';
+
+const isSAPConfig = (fields) => fields.identityProvider === 'sap_success_factors';
+
+export const validations: FormFieldValidation[] = [
+ {
+ formFieldId: 'sapsfOauthRootUrl',
+ validator: (fields) => isSAPConfig(fields) && (!fields.sapsfOauthRootUrl || !urlValidation(fields.sapsfOauthRootUrl)) && 'Please enter an OAuth Root URL.',
+ },
+ {
+ formFieldId: 'odataApiRootUrl',
+ validator: (fields) => isSAPConfig(fields) && (!fields.odataApiRootUrl || !urlValidation(fields.odataApiRootUrl)) && 'Please enter an API Root URL.',
+ },
+ {
+ formFieldId: 'sapsfPrivateKey',
+ validator: (fields) => isSAPConfig(fields) && !fields.sapsfPrivateKey && 'Please enter a Private Key.',
+ },
+ {
+ formFieldId: 'odataCompanyId',
+ validator: (fields) => isSAPConfig(fields) && !fields.odataCompanyId && 'Please enter a Company ID.',
+ },
+ {
+ formFieldId: 'oauthUserId',
+ validator: (fields) => isSAPConfig(fields) && !fields.oauthUserId && 'Please enter an OAuth User ID.',
+ },
+];
const SSOConfigConfigureStep = () => {
+ const {
+ formFields,
+ }: FormContext = useFormContext();
+ const usingSAP = formFields?.identityProvider === 'sap_success_factors';
+
const renderBaseFields = () => (
<>
- Enter user attributes
+
+ Enter user attributes
+
Please enter the SAML user attributes from your Identity Provider.
All attributes are space and case sensitive.
+
+
+ {
{
{
{
{
);
const renderSAPFields = () => (
<>
- Enable learner account auto-registration
+
+ Enable learner account auto-registration
+
{
{
{
{
{
- {/* TODO: Render SAP fields selectively once logic is in place */}
- {renderBaseFields()}
- {renderSAPFields()}
+ {usingSAP ? renderSAPFields() : renderBaseFields()}
);
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
index 4b66625d11..ac33ed1c18 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
@@ -1,14 +1,46 @@
-import React from 'react';
+import React, { useState } from 'react';
import { Container, Dropzone, Form } from '@edx/paragon';
import ValidatedFormRadio from '../../../forms/ValidatedFormRadio';
import ValidatedFormControl from '../../../forms/ValidatedFormControl';
-import { FormContext, useFormContext } from '../../../forms/FormContext';
+import { FormContext, FormFieldValidation, useFormContext } from '../../../forms/FormContext';
+import { setFormFieldAction } from '../../../forms/data/actions';
+import { urlValidation } from '../../../../utils';
+
+export const IDP_URL_SELECTION = 'idp_metadata_url';
+export const IDP_XML_SELECTION = 'idp_metadata_xml';
+const urlEntrySelected = (formFields) => formFields?.idpConnectOption === IDP_URL_SELECTION;
+const xmlEntrySelected = (formFields) => formFields?.idpConnectOption === IDP_XML_SELECTION;
+
+export const validations: FormFieldValidation[] = [
+ {
+ formFieldId: 'identityProvider',
+ validator: (fields) => !fields.identityProvider && 'Please select an SSO Identity Provider',
+ },
+ {
+ formFieldId: 'idpConnectOption',
+ validator: (fields) => !fields.idpConnectOption && 'Please select a connection method',
+ },
+ {
+ formFieldId: 'metadataUrl',
+ validator: (fields) => {
+ const error = urlEntrySelected(fields) && !urlValidation(fields.metadataUrl);
+ return error && 'Please enter an Identity Provider Metadata URL';
+ },
+ },
+ {
+ formFieldId: 'metadataXml',
+ validator: (fields) => {
+ const error = !fields.metadataXml && xmlEntrySelected(fields);
+ return error && 'Please upload an Identity Provider Metadata XML file';
+ },
+ },
+];
const SSOConfigConnectStep = () => {
const fiveGbInBytes = 5368709120;
const ssoIdpOptions = [
- ['Microsoft Azure Active Directory (Azure AD)', 'azure_ad'],
+ ['Microsoft Entra ID', 'microsoft_entra_id'],
['Google Workspace', 'google_workspace'],
['Okta', 'okta'],
['OneLogin', 'one_login'],
@@ -16,18 +48,25 @@ const SSOConfigConnectStep = () => {
['Other', 'other'],
];
const idpConnectOptions = [
- ['Enter identity Provider Metadata URL', 'idp_metadata_url'],
- ['Upload Identity Provider Metadata XML file', 'idp_metadata_xml'],
+ ['Enter identity Provider Metadata URL', IDP_URL_SELECTION],
+ ['Upload Identity Provider Metadata XML file', IDP_XML_SELECTION],
];
const {
- formFields,
+ formFields, dispatch, showErrors, errorMap,
}: FormContext = useFormContext();
- const showUrlEntry = formFields?.idpConnectOption === 'idp_metadata_url';
- const showXmlUpload = formFields?.idpConnectOption === 'idp_metadata_xml';
+ const [xmlUploadFileName, setXmlUploadFileName] = useState('');
+ const showUrlEntry = urlEntrySelected(formFields);
+ const showXmlUpload = xmlEntrySelected(formFields);
+ const xmlUploadError = errorMap?.metadataXml;
- // TODO: Store uploaded XML data
- const onUploadXml = () => null;
+ const onUploadXml = ({ fileData }) => {
+ const blob = fileData.get('file');
+ blob.text().then(xmlText => {
+ dispatch?.(setFormFieldAction({ fieldId: 'metadataXml', value: xmlText }));
+ setXmlUploadFileName(blob.name);
+ });
+ };
return (
@@ -38,13 +77,13 @@ const SSOConfigConnectStep = () => {
What is your organization's SSO Identity Provider?
- Connect edX to your Identity Provider
+ Connect edX to your Identity Provider
Select a method to connect edX to your Identity Provider
{
{showUrlEntry && (
{
{showXmlUpload
&& (
-
+ <>
+
+ {xmlUploadFileName && (
+
+ Uploaded{' '}
+ {xmlUploadFileName}
+
+ )}
+ {showErrors && xmlUploadError && {xmlUploadError} }
+ >
)}
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
index 4cc7cecc93..82fbe4109e 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
@@ -2,6 +2,7 @@ import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n';
+import Router from 'react-router';
import { Provider } from 'react-redux';
import NewSSOConfigForm from '../NewSSOConfigForm';
@@ -43,6 +44,14 @@ jest.mock('../hooks', () => {
useExistingSSOConfigs: () => [[{ hehe: 'haha' }], null, true],
};
});
+jest.mock('react-router', () => ({
+ ...jest.requireActual('react-router'),
+ useParams: jest.fn(),
+}));
+
+// const enterUrlText = 'Find the URL in your Identity Provider portal or website.';
+const enterUrlText = 'Identity Provider Metadata URL';
+const uploadXmlText = 'Drag and drop your file here or click to upload.';
const store = getMockStore({ ...initialStore });
@@ -71,11 +80,11 @@ const contextValue = {
setRefreshBool: jest.fn(),
};
-const setupNewSSOStepper = () => {
+const setupNewSSOStepper = (contextChanges = {}) => {
features.AUTH0_SELF_SERVICE_INTEGRATION = true;
return render(
-
+
,
@@ -322,8 +331,34 @@ describe('SAML Config Tab', () => {
expect(screen.getByText('Next')).not.toBeDisabled();
}, []);
});
- test('navigate through new sso workflow skeleton', async () => {
+ test('show correct metadata entry based on selection', async () => {
setupNewSSOStepper();
+ await waitFor(() => {
+ expect(getButtonElement('Next')).toBeInTheDocument();
+ }, []);
+
+ // Verify metadata selectors are hidden initially
+ expect(screen.queryByText(enterUrlText)).not.toBeInTheDocument();
+ expect(screen.queryByText(uploadXmlText)).not.toBeInTheDocument();
+
+ // Verify metadata selectors appear with their respective selections
+ userEvent.click(screen.getByText('Enter identity Provider Metadata URL'));
+ await waitFor(() => {
+ expect(screen.queryByText(enterUrlText)).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText(uploadXmlText)).not.toBeInTheDocument();
+
+ userEvent.click(screen.getByText('Upload Identity Provider Metadata XML file'));
+ await waitFor(() => {
+ expect(screen.queryByText(uploadXmlText)).toBeInTheDocument();
+ }, []);
+ expect(screen.queryByText(enterUrlText)).not.toBeInTheDocument();
+ });
+ test('navigate through new non-SAP sso workflow', async () => {
+ setupNewSSOStepper();
+ const mockCreateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'createEnterpriseSsoOrchestrationRecord');
+ mockCreateEnterpriseSsoOrchestrationRecord.mockResolvedValue({ data: { record: 'fakeuuid', sp_metadata_url: 'https://fake.url' } });
+ jest.spyOn(Router, 'useParams').mockReturnValue({ enterpriseSlug: 'testslug' });
// Connect Step
await waitFor(() => {
expect(getButtonElement('Next')).toBeInTheDocument();
@@ -331,6 +366,14 @@ describe('SAML Config Tab', () => {
expect(screen.queryByText('New SSO integration')).toBeInTheDocument();
expect(screen.queryByText('Connect')).toBeInTheDocument();
expect(screen.queryByText('Let\'s get started')).toBeInTheDocument();
+ // Click provider
+ userEvent.click(screen.getByText('Okta'));
+ // Click Enter identity Provider Metadata URL
+ userEvent.click(screen.getByText('Enter identity Provider Metadata URL'));
+ await waitFor(() => {
+ expect(screen.queryByText(enterUrlText)).toBeInTheDocument();
+ }, []);
+ userEvent.type(screen.queryByText(enterUrlText), 'https://unimportant.link');
userEvent.click(getButtonElement('Next'));
// Configure Step
@@ -338,13 +381,24 @@ describe('SAML Config Tab', () => {
expect(getButtonElement('Configure')).toBeInTheDocument();
}, []);
expect(screen.queryByText('Enter integration details')).toBeInTheDocument();
+ // Verify SAP field not present
+ expect(screen.queryByText('OAuth Root URL')).not.toBeInTheDocument();
+
userEvent.click(getButtonElement('Configure'));
// Authorize Step
await waitFor(() => {
expect(getButtonElement('Next')).toBeInTheDocument();
}, []);
- expect(screen.queryByText('Authorize edX as a Service Provider')).toBeInTheDocument();
+ // Click checkbox to advance
+ const getAuthorizedCheckbox = () => screen.queryByRole('checkbox');
+ await waitFor(() => {
+ expect(getAuthorizedCheckbox()).toBeInTheDocument();
+ }, []);
+ userEvent.click(getAuthorizedCheckbox());
+ await waitFor(() => {
+ expect(getAuthorizedCheckbox()).toBeChecked();
+ }, []);
userEvent.click(getButtonElement('Next'));
// Confirm and Test Step
@@ -353,81 +407,100 @@ describe('SAML Config Tab', () => {
}, []);
expect(screen.queryByText('Wait for SSO configuration confirmation')).toBeInTheDocument();
});
- test('show correct metadata entry based on selection', async () => {
+ test('cancel out of new SSO workflow', async () => {
setupNewSSOStepper();
+ // Connect Step Select an option to trigger cancel modal
+ userEvent.click(screen.getByText('Okta'));
+ userEvent.click(getButtonElement('Cancel'));
await waitFor(() => {
- expect(getButtonElement('Next')).toBeInTheDocument();
+ expect(getButtonElement('Exit')).toBeInTheDocument();
}, []);
+ userEvent.click(getButtonElement('Exit'));
- const enterUrlText = 'Find the URL in your Identity Provider portal or website.';
- const uploadXmlText = 'Drag and drop your file here or click to upload.';
-
- // Verify metadata selectors are hidden initially
- expect(screen.queryByText(enterUrlText)).not.toBeInTheDocument();
- expect(screen.queryByText(uploadXmlText)).not.toBeInTheDocument();
-
- // Verify metadata selectors appear with their respective selections
- userEvent.click(screen.getByText('Enter identity Provider Metadata URL'));
await waitFor(() => {
- expect(screen.queryByText(enterUrlText)).toBeInTheDocument();
+ expect(
+ screen.queryByText(
+ 'Connect to a SAML identity provider for single sign-on'
+ + ' to allow quick access to your organization\'s learning catalog.',
+ ),
+ ).toBeInTheDocument();
}, []);
- expect(screen.queryByText(uploadXmlText)).not.toBeInTheDocument();
-
- userEvent.click(screen.getByText('Upload Identity Provider Metadata XML file'));
+ });
+ test('new SSO workflow load existing metadata url config', async () => {
+ const testMetadataUrl = 'http://test.metadata';
+ const existingConfigContextValue = {
+ ssoState: {
+ providerConfig: {
+ uuid: 123,
+ metadataUrl: testMetadataUrl,
+ },
+ },
+ };
+ setupNewSSOStepper(existingConfigContextValue);
+ // Connect Step with metadata url selected
await waitFor(() => {
- expect(screen.queryByText(uploadXmlText)).toBeInTheDocument();
+ expect(
+ screen.queryByText(
+ 'Find the URL in your Identity Provider portal or website.',
+ ),
+ ).toBeInTheDocument();
}, []);
- expect(screen.queryByText(enterUrlText)).not.toBeInTheDocument();
+ screen.queryByText(testMetadataUrl);
});
- test('back button shown on pages after first page', async () => {
- const getBackButton = () => getButtonElement('Back');
+ // TODO: Test case where we go SAP route
+ test('navigate through new SAP sso workflow', async () => {
setupNewSSOStepper();
+ const mockCreateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'createEnterpriseSsoOrchestrationRecord');
+ mockCreateEnterpriseSsoOrchestrationRecord.mockResolvedValue({ data: { record: 'fakeuuid', sp_metadata_url: 'https://fake.url' } });
+ jest.spyOn(Router, 'useParams').mockReturnValue({ enterpriseSlug: 'testslug' });
// Connect Step
await waitFor(() => {
expect(getButtonElement('Next')).toBeInTheDocument();
}, []);
- expect(screen.queryByRole('button', { name: 'Back' })).not.toBeInTheDocument();
+ expect(screen.queryByText('New SSO integration')).toBeInTheDocument();
+ expect(screen.queryByText('Connect')).toBeInTheDocument();
+ expect(screen.queryByText('Let\'s get started')).toBeInTheDocument();
+ // Click SAP provider
+ userEvent.click(screen.getByText('SAP SuccessFactors'));
+ // Click Enter identity Provider Metadata URL
+ userEvent.click(screen.getByText('Enter identity Provider Metadata URL'));
+ await waitFor(() => {
+ expect(screen.queryByText(enterUrlText)).toBeInTheDocument();
+ }, []);
+ userEvent.type(screen.queryByText(enterUrlText), 'https://unimportant.link');
userEvent.click(getButtonElement('Next'));
// Configure Step
await waitFor(() => {
- expect(getButtonElement('Configure')).toBeInTheDocument();
+ expect(screen.queryByText('OAuth Root URL')).toBeInTheDocument();
}, []);
- expect(getBackButton()).toBeInTheDocument();
+ // Verify Configure does not advance until fields are filled out
+ userEvent.click(getButtonElement('Configure'));
+ expect(screen.queryByText('OAuth Root URL')).toBeInTheDocument();
+ const fieldEntries = [
+ ['OAuth Root URL', 'https://test'],
+ ['API Root URL', 'https://test'],
+ ['Company ID', 'test'],
+ ['Private Key', 'test'],
+ ['OAuth User ID', 'test'],
+ ];
+ fieldEntries.forEach(([fieldIdText, value]) => {
+ userEvent.type(screen.queryByText(fieldIdText), value);
+ });
userEvent.click(getButtonElement('Configure'));
-
- // Authorize Step
await waitFor(() => {
expect(getButtonElement('Next')).toBeInTheDocument();
}, []);
- expect(getBackButton()).toBeInTheDocument();
- userEvent.click(getButtonElement('Next'));
-
- // Back from Confirm and Test Step
- await waitFor(() => {
- expect(getButtonElement('Finish')).toBeInTheDocument();
- }, []);
- userEvent.click(getBackButton());
- await waitFor(() => {
- expect(screen.queryByText('Authorize edX as a Service Provider')).toBeInTheDocument();
- }, []);
});
test('cancel out of new SSO workflow', async () => {
setupNewSSOStepper();
- // Connect Step
- await waitFor(() => {
- expect(getButtonElement('Cancel')).toBeInTheDocument();
- }, []);
+ // Connect Step Select an option to trigger cancel modal
+ userEvent.click(screen.getByText('Okta'));
userEvent.click(getButtonElement('Cancel'));
await waitFor(() => {
- expect(getButtonElement('Cancel')).toBeInTheDocument();
- }, []);
-
- await waitFor(() => {
- const exitButton = getButtonElement('Exit without saving');
- expect(exitButton).toBeInTheDocument();
- userEvent.click(exitButton);
+ expect(getButtonElement('Exit')).toBeInTheDocument();
}, []);
+ userEvent.click(getButtonElement('Exit'));
await waitFor(() => {
expect(
From 179524d429846034a06655ae69a776f5642555d3 Mon Sep 17 00:00:00 2001
From: Marlon Keating <322346+marlonkeating@users.noreply.github.com>
Date: Tue, 7 Nov 2023 07:44:54 -0800
Subject: [PATCH 062/124] fix: disable creating new SSO config while
configuring prior config (#1084)
---
.../settings/SettingsSSOTab/NewExistingSSOConfigs.jsx | 5 ++---
src/components/settings/SettingsSSOTab/index.jsx | 6 +++++-
src/components/settings/SettingsSSOTab/utils.js | 9 ++++++++-
3 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
index 6474ef6a59..9ac1d82113 100644
--- a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
+++ b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
@@ -11,6 +11,7 @@ import { connect } from 'react-redux';
import LmsApiService from '../../../data/services/LmsApiService';
import NewSSOConfigAlerts from './NewSSOConfigAlerts';
import NewSSOConfigCard from './NewSSOConfigCard';
+import { isInProgressConfig } from './utils';
const FRESH_CONFIG_POLLING_INTERVAL = 30000;
const UPDATED_CONFIG_POLLING_INTERVAL = 2000;
@@ -66,9 +67,7 @@ const NewExistingSSOConfigs = ({
useEffect(() => {
const [active, inactive] = _.partition(configs, config => config.active);
- const inProgress = configs.filter(
- config => (config.submitted_at && !config.configured_at) || (config.configured_at < config.submitted_at),
- );
+ const inProgress = configs.filter(isInProgressConfig);
const untested = configs.filter(config => !config.validated_at);
const live = configs.filter(
config => (config.validated_at && config.active && config.validated_at > config.configured_at),
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index b1181c6817..a1f5599415 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -13,6 +13,7 @@ import NewSSOConfigForm from './NewSSOConfigForm';
import { SSOConfigContext, SSOConfigContextProvider } from './SSOConfigContext';
import LmsApiService from '../../../data/services/LmsApiService';
import { features } from '../../../config';
+import { isInProgressConfig } from './utils';
const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const {
@@ -51,6 +52,8 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
}, [AUTH0_SELF_SERVICE_INTEGRATION, existingConfigs, setHasSSOConfig]);
if (AUTH0_SELF_SERVICE_INTEGRATION) {
+ const newButtonVisible = existingConfigs?.length > 0 && (providerConfig === null);
+ const newButtonDisabled = existingConfigs.some(isInProgressConfig);
return (
{
Single Sign-On (SSO) Integrations
- {existingConfigs?.length > 0 && (providerConfig === null) && (
+ {newButtonVisible && (
New
diff --git a/src/components/settings/SettingsSSOTab/utils.js b/src/components/settings/SettingsSSOTab/utils.js
index d57a8d185c..e9fd44a082 100644
--- a/src/components/settings/SettingsSSOTab/utils.js
+++ b/src/components/settings/SettingsSSOTab/utils.js
@@ -26,4 +26,11 @@ function createSAMLURLs({
return { testLink, spMetadataLink };
}
-export { updateSamlProviderData, deleteSamlProviderData, createSAMLURLs };
+function isInProgressConfig(config) {
+ return (config.submitted_at && !config.configured_at)
+ || config.configured_at < config.submitted_at;
+}
+
+export {
+ updateSamlProviderData, deleteSamlProviderData, createSAMLURLs, isInProgressConfig,
+};
From 16373b9f4612796843d4c6bc5d46814335bdb2b2 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Tue, 7 Nov 2023 14:19:04 -0500
Subject: [PATCH 063/124] feat: assignment error AlertModal variants (#1082)
---
.../AssignmentRowActionTableCell.jsx | 2 +-
.../cards/BaseCourseCard.jsx | 2 +-
.../cards/CourseCard.test.jsx | 115 +++++++++++++++++-
.../CreateAllocationErrorAlertModals.jsx | 84 +++++++++++++
.../cards/NewAssignmentModalButton.jsx | 35 +++++-
.../cards/data/constants.js | 9 ++
.../cards/data/index.js | 3 +
.../cards/data/utils.js | 9 ++
.../ContentNotInCatalogErrorAlertModal.jsx | 46 +++++++
.../NotEnoughBalanceAlertModal.jsx | 54 ++++++++
.../status-modals/SystemErrorAlertModal.jsx | 47 +++++++
.../tests/CatalogSearchResults.test.jsx | 14 +++
12 files changed, 408 insertions(+), 12 deletions(-)
create mode 100644 src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
create mode 100644 src/components/learner-credit-management/cards/data/constants.js
create mode 100644 src/components/learner-credit-management/cards/data/index.js
create mode 100644 src/components/learner-credit-management/cards/data/utils.js
create mode 100644 src/components/learner-credit-management/cards/status-modals/ContentNotInCatalogErrorAlertModal.jsx
create mode 100644 src/components/learner-credit-management/cards/status-modals/NotEnoughBalanceAlertModal.jsx
create mode 100644 src/components/learner-credit-management/cards/status-modals/SystemErrorAlertModal.jsx
diff --git a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
index a180598d31..eec4589cdd 100644
--- a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
@@ -9,7 +9,7 @@ const AssignmentRowActionTableCell = ({ row }) => {
const isLearnerStateWaiting = row.original.learnerState === 'waiting';
const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
return (
-
+
{isLearnerStateWaiting && (
{
+ const mockAllocateContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'allocateContentAssignments');
+
+ // Helper function to find the assignment error modal after failed allocation attempt
+ const getAssignmentErrorModal = () => within(screen.queryAllByRole('dialog')[1]);
+
+ // Helper function to simulate clicking on "Try again" in error modal to retry allocation
+ const simulateClickErrorModalTryAgain = async (modalTitle, assignmentErrorModal) => {
+ const tryAgainCTA = getButtonElement('Try again', { screenOverride: assignmentErrorModal });
+ expect(tryAgainCTA).toBeInTheDocument();
+ userEvent.click(tryAgainCTA);
+ await waitFor(() => {
+ // Verify modal closes
+ expect(assignmentErrorModal.queryByText(modalTitle)).not.toBeInTheDocument();
+ });
+ expect(mockAllocateContentAssignments).toHaveBeenCalledTimes(2);
+ };
+
+ // Helper function to simulate clicking on "Exit and discard changes" in error modal to close ALL modals
+ const simulateClickErrorModalExit = async (assignmentErrorModal) => {
+ const exitCTA = getButtonElement('Exit and discard changes', { screenOverride: assignmentErrorModal });
+ userEvent.click(exitCTA);
+ await waitFor(() => {
+ // Verify all modals close (error modal + assignment modal)
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ });
+ expect(mockAllocateContentAssignments).toHaveBeenCalledTimes(1);
+ };
+
beforeEach(() => {
useSubsidyAccessPolicy.mockReturnValue({
data: mockSubsidyAccessPolicy,
@@ -176,13 +204,59 @@ describe('Course card works as expected', () => {
});
test.each([
- { shouldSubmitAssignments: true, hasAllocationException: true },
+ {
+ shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: 'content_not_in_catalog',
+ },
+ {
+ shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: 'not_enough_value_in_subsidy',
+ shouldRetryAfterError: false,
+ },
+ {
+ shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: 'not_enough_value_in_subsidy',
+ shouldRetryAfterError: true,
+ },
+ { shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: 'policy_spend_limit_reached',
+ shouldRetryAfterError: false,
+ },
+ { shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: 'policy_spend_limit_reached',
+ shouldRetryAfterError: true,
+ },
+ { shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: null,
+ shouldRetryAfterError: false,
+ },
+ { shouldSubmitAssignments: true,
+ hasAllocationException: true,
+ errorReason: null,
+ shouldRetryAfterError: true,
+ },
{ shouldSubmitAssignments: true, hasAllocationException: false },
{ shouldSubmitAssignments: false, hasAllocationException: false },
- ])('opens assignment modal, submits assignments successfully (%s)', async ({ shouldSubmitAssignments, hasAllocationException }) => {
- const mockAllocateContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'allocateContentAssignments');
+ ])('opens assignment modal, submits assignments successfully (%s)', async ({
+ shouldSubmitAssignments,
+ hasAllocationException,
+ errorReason,
+ shouldRetryAfterError,
+ }) => {
if (hasAllocationException) {
- mockAllocateContentAssignments.mockRejectedValue(new Error('oops'));
+ // mock Axios error
+ mockAllocateContentAssignments.mockRejectedValue({
+ customAttributes: {
+ httpErrorStatus: errorReason ? 422 : 500,
+ httpErrorResponseData: JSON.stringify([{ reason: errorReason }]),
+ },
+ });
} else {
mockAllocateContentAssignments.mockResolvedValue({
data: {
@@ -286,9 +360,40 @@ describe('Course card works as expected', () => {
}),
);
+ // Verify error states
if (hasAllocationException) {
- // Verify error state
expect(getButtonElement('Try again', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'false');
+
+ // Assert the correct error modal is displayed
+ if (errorReason === 'content_not_in_catalog') {
+ const assignmentErrorModal = getAssignmentErrorModal();
+ expect(assignmentErrorModal.getByText(`This course is not in your ${mockSubsidyAccessPolicy.displayName} budget's catalog`)).toBeInTheDocument();
+ const exitCTA = getButtonElement('Exit', { screenOverride: assignmentErrorModal });
+ userEvent.click(exitCTA);
+ await waitFor(() => {
+ // Verify all modals close (error modal + assignment modal)
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ });
+ } else if (['not_enough_value_in_subsidy', 'policy_spend_limit_reached'].includes(errorReason)) {
+ const assignmentErrorModal = getAssignmentErrorModal();
+ const errorModalTitle = 'Not enough balance';
+ expect(assignmentErrorModal.getByText(errorModalTitle)).toBeInTheDocument();
+ if (shouldRetryAfterError) {
+ await simulateClickErrorModalTryAgain(errorModalTitle, assignmentErrorModal);
+ } else {
+ await simulateClickErrorModalExit(assignmentErrorModal);
+ }
+ } else {
+ const assignmentErrorModal = getAssignmentErrorModal();
+ const errorModalTitle = 'Something went wrong';
+ expect(assignmentErrorModal.getByText(errorModalTitle)).toBeInTheDocument();
+ if (shouldRetryAfterError) {
+ await simulateClickErrorModalTryAgain(errorModalTitle, assignmentErrorModal);
+ } else {
+ await simulateClickErrorModalExit(assignmentErrorModal);
+ }
+ }
+
} else {
// Verify success state
expect(mockInvalidateQueries).toHaveBeenCalledTimes(1);
diff --git a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
new file mode 100644
index 0000000000..82d14eba94
--- /dev/null
+++ b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
@@ -0,0 +1,84 @@
+import React, { useCallback, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import { useToggle } from '@edx/paragon';
+import SystemErrorAlertModal from './status-modals/SystemErrorAlertModal';
+import ContentNotInCatalogErrorAlertModal from './status-modals/ContentNotInCatalogErrorAlertModal';
+import NotEnoughBalanceAlertModal from './status-modals/NotEnoughBalanceAlertModal';
+
+const CreateAllocationErrorAlertModals = ({
+ errorReason,
+ retry,
+ closeAssignmentModal,
+}) => {
+ const [isCatalogError, openCatalogErrorModal, closeCatalogErrorModal] = useToggle(false);
+ const [isSystemError, openSystemErrorModal, closeSystemErrorModal] = useToggle(false);
+ const [isBalanceError, openBalanceErrorModal, closeBalanceErrorModal] = useToggle(false);
+
+ /**
+ * Close all error modals.
+ */
+ const closeAllErrorModals = useCallback(() => {
+ const closeFns = [closeCatalogErrorModal, closeBalanceErrorModal, closeSystemErrorModal];
+ closeFns.forEach((closeFn) => {
+ closeFn();
+ });
+ }, [closeCatalogErrorModal, closeBalanceErrorModal, closeSystemErrorModal]);
+
+ /**
+ * Retry the original action that caused the error.
+ */
+ const handleErrorRetry = () => {
+ retry();
+ closeAllErrorModals();
+ };
+
+ /**
+ * Whenever the `errorReason` prop changes, open the associated error modal to
+ * surface the error messaging to the user. If no `errorReason` exists, close
+ * any error modals that may be previously open.
+ */
+ useEffect(() => {
+ // Always ensure any open error modal is closed before opening a new one, OR when
+ // there is error reason.
+ closeAllErrorModals();
+
+ // Open specific error modal based on error reason.
+ if (errorReason === 'content_not_in_catalog') {
+ openCatalogErrorModal();
+ } else if (['not_enough_value_in_subsidy', 'policy_spend_limit_reached'].includes(errorReason)) {
+ openBalanceErrorModal();
+ } else if (errorReason) {
+ openSystemErrorModal();
+ }
+ }, [errorReason, closeAllErrorModals, openCatalogErrorModal, openBalanceErrorModal, openSystemErrorModal]);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+CreateAllocationErrorAlertModals.propTypes = {
+ closeAssignmentModal: PropTypes.func.isRequired,
+ retry: PropTypes.func.isRequired,
+ errorReason: PropTypes.string,
+};
+
+export default CreateAllocationErrorAlertModals;
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index e23dbb5273..708d9972e0 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -15,6 +15,7 @@ import { snakeCaseObject } from '@edx/frontend-platform/utils';
import AssignmentModalContent from './AssignmentModalContent';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys, useBudgetId } from '../data';
+import CreateAllocationErrorAlertModals from './CreateAllocationErrorAlertModals';
const useAllocateContentAssignments = () => useMutation({
mutationFn: async ({
@@ -28,15 +29,20 @@ const NewAssignmentModalButton = ({ course, children }) => {
const routeMatch = useRouteMatch();
const queryClient = useQueryClient();
const { subsidyAccessPolicyId } = useBudgetId();
-
const [isOpen, open, close] = useToggle(false);
const [learnerEmails, setLearnerEmails] = useState([]);
const [assignButtonState, setAssignButtonState] = useState('default');
+ const [createAssignmentsErrorReason, setCreateAssignmentsErrorReason] = useState();
const { mutate } = useAllocateContentAssignments();
const pathToActivityTab = generatePath(routeMatch.path, { budgetId: subsidyAccessPolicyId, activeTabKey: 'activity' });
+ const handleCloseAssignmentModal = () => {
+ close();
+ setAssignButtonState('default');
+ };
+
const handleAllocateContentAssignments = () => {
const payload = snakeCaseObject({
contentPriceCents: course.normalizedMetadata.contentPrice * 100, // Convert to USD cents
@@ -48,16 +54,27 @@ const NewAssignmentModalButton = ({ course, children }) => {
payload,
};
setAssignButtonState('pending');
+ setCreateAssignmentsErrorReason(null);
mutate(mutationArgs, {
onSuccess: () => {
setAssignButtonState('complete');
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
});
- close();
+ handleCloseAssignmentModal();
history.push(pathToActivityTab);
},
- onError: () => {
+ onError: (err) => {
+ const {
+ httpErrorStatus,
+ httpErrorResponseData,
+ } = err.customAttributes;
+ if (httpErrorStatus === 422) {
+ const responseData = JSON.parse(httpErrorResponseData);
+ setCreateAssignmentsErrorReason(responseData[0].reason);
+ } else {
+ setCreateAssignmentsErrorReason('system_error');
+ }
setAssignButtonState('error');
},
});
@@ -70,7 +87,7 @@ const NewAssignmentModalButton = ({ course, children }) => {
className="bg-light-200 text-left"
title="Assign this course"
isOpen={isOpen}
- onClose={close}
+ onClose={handleCloseAssignmentModal}
footerNode={(
@@ -92,8 +109,16 @@ const NewAssignmentModalButton = ({ course, children }) => {
)}
>
-
+
+
>
);
};
diff --git a/src/components/learner-credit-management/cards/data/constants.js b/src/components/learner-credit-management/cards/data/constants.js
new file mode 100644
index 0000000000..01af1a5313
--- /dev/null
+++ b/src/components/learner-credit-management/cards/data/constants.js
@@ -0,0 +1,9 @@
+/* eslint-disable import/prefer-default-export */
+
+import PropTypes from 'prop-types';
+
+export const commonErrorAlertModalPropTypes = {
+ isErrorModalOpen: PropTypes.bool.isRequired,
+ closeErrorModal: PropTypes.func.isRequired,
+ closeAssignmentModal: PropTypes.func.isRequired,
+};
diff --git a/src/components/learner-credit-management/cards/data/index.js b/src/components/learner-credit-management/cards/data/index.js
new file mode 100644
index 0000000000..ed12c2ce9f
--- /dev/null
+++ b/src/components/learner-credit-management/cards/data/index.js
@@ -0,0 +1,3 @@
+export * from './constants';
+export * from './utils';
+export { default as useCourseCardMetadata } from './useCourseCardMetadata';
diff --git a/src/components/learner-credit-management/cards/data/utils.js b/src/components/learner-credit-management/cards/data/utils.js
new file mode 100644
index 0000000000..aa27d5fe37
--- /dev/null
+++ b/src/components/learner-credit-management/cards/data/utils.js
@@ -0,0 +1,9 @@
+/* eslint-disable import/prefer-default-export */
+
+export const getBudgetDisplayName = (subsidyAccessPolicy) => {
+ let budgetDisplayName = 'budget';
+ if (subsidyAccessPolicy.displayName) {
+ budgetDisplayName = `${subsidyAccessPolicy.displayName} ${budgetDisplayName}`;
+ }
+ return budgetDisplayName;
+};
diff --git a/src/components/learner-credit-management/cards/status-modals/ContentNotInCatalogErrorAlertModal.jsx b/src/components/learner-credit-management/cards/status-modals/ContentNotInCatalogErrorAlertModal.jsx
new file mode 100644
index 0000000000..5b44008065
--- /dev/null
+++ b/src/components/learner-credit-management/cards/status-modals/ContentNotInCatalogErrorAlertModal.jsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { AlertModal, ActionRow, Button } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
+import { commonErrorAlertModalPropTypes, getBudgetDisplayName } from '../data';
+import { useBudgetId, useSubsidyAccessPolicy } from '../../data';
+
+const ContentNotInCatalogErrorAlertModal = ({
+ isErrorModalOpen,
+ closeErrorModal,
+ closeAssignmentModal,
+}) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
+ const budgetDisplayName = getBudgetDisplayName(subsidyAccessPolicy);
+
+ const handleClose = () => {
+ closeErrorModal();
+ closeAssignmentModal();
+ };
+
+ return (
+
+ Exit
+
+ )}
+ >
+
+ This course is not included in the catalog for your {budgetDisplayName}. Please try again with another course.
+
+
+ );
+};
+
+ContentNotInCatalogErrorAlertModal.propTypes = { ...commonErrorAlertModalPropTypes };
+
+export default ContentNotInCatalogErrorAlertModal;
diff --git a/src/components/learner-credit-management/cards/status-modals/NotEnoughBalanceAlertModal.jsx b/src/components/learner-credit-management/cards/status-modals/NotEnoughBalanceAlertModal.jsx
new file mode 100644
index 0000000000..fe480d7320
--- /dev/null
+++ b/src/components/learner-credit-management/cards/status-modals/NotEnoughBalanceAlertModal.jsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { AlertModal, ActionRow, Button } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
+import { commonErrorAlertModalPropTypes, getBudgetDisplayName } from '../data';
+import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../../data';
+
+const NotEnoughBalanceAlertModal = ({
+ isErrorModalOpen,
+ closeErrorModal,
+ closeAssignmentModal,
+ retry,
+}) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
+ const budgetDisplayName = getBudgetDisplayName(subsidyAccessPolicy);
+
+ const handleClose = () => {
+ closeErrorModal();
+ closeAssignmentModal();
+ };
+
+ return (
+
+ Exit and discard changes
+ Try again
+
+ )}
+ >
+
+ The total assignment cost exceeds your {`${budgetDisplayName}'s`} available balance
+ of {formatPrice(subsidyAccessPolicy.aggregates.spendAvailableUsd)}. Please
+ remove learners and try again.
+
+
+ );
+};
+
+NotEnoughBalanceAlertModal.propTypes = {
+ ...commonErrorAlertModalPropTypes,
+ retry: PropTypes.func.isRequired,
+};
+
+export default NotEnoughBalanceAlertModal;
diff --git a/src/components/learner-credit-management/cards/status-modals/SystemErrorAlertModal.jsx b/src/components/learner-credit-management/cards/status-modals/SystemErrorAlertModal.jsx
new file mode 100644
index 0000000000..76ff98c0ff
--- /dev/null
+++ b/src/components/learner-credit-management/cards/status-modals/SystemErrorAlertModal.jsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { ActionRow, AlertModal, Button } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
+import { commonErrorAlertModalPropTypes } from '../data';
+
+const SystemErrorAlertModal = ({
+ isErrorModalOpen,
+ closeErrorModal,
+ closeAssignmentModal,
+ retry,
+}) => {
+ const handleClose = () => {
+ closeErrorModal();
+ closeAssignmentModal();
+ };
+
+ return (
+
+ Exit and discard changes
+ Try again
+
+ )}
+ >
+
+ We're sorry. Something went wrong behind the scenes. Please
+ try again, or reach out to customer support for help.
+
+
+ );
+};
+
+SystemErrorAlertModal.propTypes = {
+ ...commonErrorAlertModalPropTypes,
+ retry: PropTypes.func.isRequired,
+};
+
+export default SystemErrorAlertModal;
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
index 32807bbfdf..9d5ac8df71 100644
--- a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
@@ -12,6 +12,7 @@ import { QueryClientProvider } from '@tanstack/react-query';
import { BaseCatalogSearchResults } from '../search/CatalogSearchResults';
import { CONTENT_TYPE_COURSE } from '../data/constants';
import { queryClient } from '../../test/testUtils';
+import { useSubsidyAccessPolicy } from '../data';
// Mocking this connected component so as not to have to mock the algolia Api
const PAGINATE_ME = 'PAGINATE ME :)';
@@ -24,6 +25,19 @@ jest.mock('react-instantsearch-dom', () => ({
Index: () => Popular Courses
,
}));
+jest.mock('../data', () => ({
+ ...jest.requireActual('../data'),
+ useSubsidyAccessPolicy: jest.fn().mockReturnValue({
+ data: {
+ uuid: 'test-uuid',
+ displayName: 'Test Budget',
+ aggregates: {
+ spendAvailableUsd: 100,
+ },
+ },
+ }),
+}));
+
const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
From d518a4d0536cba8cd8d1175894676107376d66ff Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Tue, 7 Nov 2023 14:53:21 -0500
Subject: [PATCH 064/124] feat: display success toast on assignment allocation
(#1083)
---
.../BudgetDetailPageWrapper.jsx | 29 ++++-
.../cards/CourseCard.test.jsx | 37 +++++--
.../CreateAllocationErrorAlertModals.jsx | 6 +-
.../cards/NewAssignmentModalButton.jsx | 5 +-
.../data/hooks/index.js | 1 +
...seSuccessfulAssignmentToastContextValue.js | 35 ++++++
.../tests/BudgetDetailPageWrapper.test.jsx | 100 ++++++++++++++++++
.../tests/CatalogSearchResults.test.jsx | 12 ++-
8 files changed, 208 insertions(+), 17 deletions(-)
create mode 100644 src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js
create mode 100644 src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
diff --git a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
index 9d6d95943d..b7ae27b754 100644
--- a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
@@ -1,12 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
-import { Container } from '@edx/paragon';
+import { Container, Toast } from '@edx/paragon';
import Hero from '../Hero';
+import { useSuccessfulAssignmentToastContextValue } from './data';
const PAGE_TITLE = 'Learner Credit Management';
+export const BudgetDetailPageContext = React.createContext();
+
const BudgetDetailPageWrapper = ({
subsidyAccessPolicy,
includeHero,
@@ -16,14 +19,34 @@ const BudgetDetailPageWrapper = ({
// similar to the display name logic for budgets on the overview page route.
const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
const helmetPageTitle = budgetDisplayName ? `${budgetDisplayName} - ${PAGE_TITLE}` : PAGE_TITLE;
+
+ const successfulAssignmentToastContextValue = useSuccessfulAssignmentToastContextValue();
+ const {
+ isSuccessfulAssignmentAllocationToastOpen,
+ successfulAssignmentAllocationToastMessage,
+ closeToastForAssignmentAllocation,
+ } = successfulAssignmentToastContextValue;
+
return (
- <>
+
{includeHero && }
{children}
- >
+ {/**
+ Successful assignment allocation Toast notification. It is rendered here to guarantee that the
+ Toast component will not be unmounted when the user programmatically navigates to the "Activity"
+ tab, which will unmount the course cards that rendered the assignment modal. Thus, the Toast must
+ be rendered within the component tree that's common to both the "Activity" and "Overview" tabs.
+ */}
+
+ {successfulAssignmentAllocationToastMessage}
+
+
);
};
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index d845c3273a..247147b3ae 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -20,6 +20,7 @@ import {
import { getButtonElement, queryClient } from '../../test/testUtils';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
+import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
@@ -94,8 +95,17 @@ const mockSubsidyAccessPolicy = {
};
const mockLearnerEmails = ['hello@example.com', 'world@example.com'];
+const mockDisplaySuccessfulAssignmentToast = jest.fn();
+const defaultBudgetDetailPageContextValue = {
+ isSuccessfulAssignmentAllocationToastOpen: false,
+ totalLearnersAssigned: undefined,
+ displayToastForAssignmentAllocation: mockDisplaySuccessfulAssignmentToast,
+ closeToastForAssignmentAllocation: jest.fn(),
+};
+
const CourseCardWrapper = ({
initialState = initialStoreState,
+ budgetDetailPageContextValue = defaultBudgetDetailPageContextValue,
...rest
}) => {
const store = getMockStore({ ...initialState });
@@ -109,7 +119,9 @@ const CourseCardWrapper = ({
config: { ENTERPRISE_LEARNER_PORTAL_URL: mockLearnerPortal },
}}
>
-
+
+
+
@@ -221,22 +233,26 @@ describe('Course card works as expected', () => {
errorReason: 'not_enough_value_in_subsidy',
shouldRetryAfterError: true,
},
- { shouldSubmitAssignments: true,
+ {
+ shouldSubmitAssignments: true,
hasAllocationException: true,
errorReason: 'policy_spend_limit_reached',
shouldRetryAfterError: false,
},
- { shouldSubmitAssignments: true,
+ {
+ shouldSubmitAssignments: true,
hasAllocationException: true,
errorReason: 'policy_spend_limit_reached',
shouldRetryAfterError: true,
},
- { shouldSubmitAssignments: true,
+ {
+ shouldSubmitAssignments: true,
hasAllocationException: true,
errorReason: null,
shouldRetryAfterError: false,
},
- { shouldSubmitAssignments: true,
+ {
+ shouldSubmitAssignments: true,
hasAllocationException: true,
errorReason: null,
shouldRetryAfterError: true,
@@ -363,7 +379,7 @@ describe('Course card works as expected', () => {
// Verify error states
if (hasAllocationException) {
expect(getButtonElement('Try again', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'false');
-
+
// Assert the correct error modal is displayed
if (errorReason === 'content_not_in_catalog') {
const assignmentErrorModal = getAssignmentErrorModal();
@@ -393,7 +409,6 @@ describe('Course card works as expected', () => {
await simulateClickErrorModalExit(assignmentErrorModal);
}
}
-
} else {
// Verify success state
expect(mockInvalidateQueries).toHaveBeenCalledTimes(1);
@@ -401,9 +416,15 @@ describe('Course card works as expected', () => {
queryKey: learnerCreditManagementQueryKeys.budget(mockSubsidyAccessPolicy.uuid),
});
expect(getButtonElement('Assigned', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'true');
- // Verify modal closes
await waitFor(() => {
+ // Verify all modals close (error modal + assignment modal)
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+
+ // Verify toast notification was displayed
+ expect(mockDisplaySuccessfulAssignmentToast).toHaveBeenCalledTimes(1);
+ expect(mockDisplaySuccessfulAssignmentToast).toHaveBeenCalledWith({
+ totalLearnersAssigned: mockLearnerEmails.length,
+ });
});
}
} else {
diff --git a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
index 82d14eba94..dc5c92ebe6 100644
--- a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
+++ b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
@@ -13,9 +13,9 @@ const CreateAllocationErrorAlertModals = ({
const [isCatalogError, openCatalogErrorModal, closeCatalogErrorModal] = useToggle(false);
const [isSystemError, openSystemErrorModal, closeSystemErrorModal] = useToggle(false);
const [isBalanceError, openBalanceErrorModal, closeBalanceErrorModal] = useToggle(false);
-
+
/**
- * Close all error modals.
+ * Helper function to close all error modals.
*/
const closeAllErrorModals = useCallback(() => {
const closeFns = [closeCatalogErrorModal, closeBalanceErrorModal, closeSystemErrorModal];
@@ -25,7 +25,7 @@ const CreateAllocationErrorAlertModals = ({
}, [closeCatalogErrorModal, closeBalanceErrorModal, closeSystemErrorModal]);
/**
- * Retry the original action that caused the error.
+ * Retry the original action that caused the error and close all error modals.
*/
const handleErrorRetry = () => {
retry();
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 708d9972e0..fccc05b501 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useRouteMatch, useHistory, generatePath } from 'react-router-dom';
import {
@@ -16,6 +16,7 @@ import AssignmentModalContent from './AssignmentModalContent';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys, useBudgetId } from '../data';
import CreateAllocationErrorAlertModals from './CreateAllocationErrorAlertModals';
+import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
const useAllocateContentAssignments = () => useMutation({
mutationFn: async ({
@@ -33,6 +34,7 @@ const NewAssignmentModalButton = ({ course, children }) => {
const [learnerEmails, setLearnerEmails] = useState([]);
const [assignButtonState, setAssignButtonState] = useState('default');
const [createAssignmentsErrorReason, setCreateAssignmentsErrorReason] = useState();
+ const { displayToastForAssignmentAllocation } = useContext(BudgetDetailPageContext);
const { mutate } = useAllocateContentAssignments();
@@ -62,6 +64,7 @@ const NewAssignmentModalButton = ({ course, children }) => {
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
});
handleCloseAssignmentModal();
+ displayToastForAssignmentAllocation({ totalLearnersAssigned: learnerEmails.length });
history.push(pathToActivityTab);
},
onError: (err) => {
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index b7c35f509f..6da548a12b 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -7,3 +7,4 @@ export { default as useSubsidyAccessPolicy } from './useSubsidyAccessPolicy';
export { default as usePathToCatalogTab } from './usePathToCatalogTab';
export { default as useBudgetDetailActivityOverview } from './useBudgetDetailActivityOverview';
export { default as useIsLargeOrGreater } from './useIsLargeOrGreater';
+export { default as useSuccessfulAssignmentToastContextValue } from './useSuccessfulAssignmentToastContextValue';
diff --git a/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js b/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js
new file mode 100644
index 0000000000..8d52284152
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js
@@ -0,0 +1,35 @@
+import { useCallback, useMemo, useState } from 'react';
+
+const useSuccessfulAssignmentToastContextValue = () => {
+ const [isToastOpen, setIsToastOpen] = useState(false);
+ const [learnersAssignedCount, setLearnersAssignedCount] = useState();
+
+ const handleDisplayToast = useCallback(({ totalLearnersAssigned }) => {
+ setIsToastOpen(true);
+ setLearnersAssignedCount(totalLearnersAssigned);
+ }, []);
+
+ const handleCloseToast = useCallback(() => {
+ setIsToastOpen(false);
+ }, []);
+
+ const successfulAssignmentAllocationToastMessage = `Course successfully assigned to ${learnersAssignedCount} ${learnersAssignedCount === 1 ? 'learner' : 'learners'}.`;
+
+ const successfulAssignmentToastContextValue = useMemo(() => ({
+ isSuccessfulAssignmentAllocationToastOpen: isToastOpen,
+ displayToastForAssignmentAllocation: handleDisplayToast,
+ closeToastForAssignmentAllocation: handleCloseToast,
+ totalLearnersAssigned: learnersAssignedCount,
+ successfulAssignmentAllocationToastMessage,
+ }), [
+ isToastOpen,
+ handleDisplayToast,
+ handleCloseToast,
+ learnersAssignedCount,
+ successfulAssignmentAllocationToastMessage,
+ ]);
+
+ return successfulAssignmentToastContextValue;
+};
+
+export default useSuccessfulAssignmentToastContextValue;
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
new file mode 100644
index 0000000000..7207e8f8b4
--- /dev/null
+++ b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
@@ -0,0 +1,100 @@
+import { useContext } from 'react';
+import { Button } from '@edx/paragon';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import configureMockStore from 'redux-mock-store';
+import { Provider } from 'react-redux';
+import thunk from 'redux-thunk';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import '@testing-library/jest-dom/extend-expect';
+
+import BudgetDetailPageWrapper, { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
+import { getButtonElement } from '../../test/testUtils';
+
+const mockStore = configureMockStore([thunk]);
+const getMockStore = store => mockStore(store);
+const enterpriseSlug = 'test-enterprise';
+const enterpriseUUID = '1234';
+const defaultStoreState = {
+ portalConfiguration: {
+ enterpriseId: enterpriseUUID,
+ enterpriseSlug,
+ enableLearnerPortal: true,
+ enterpriseFeatures: {
+ topDownAssignmentRealTimeLcm: true,
+ },
+ },
+};
+
+const MockBudgetDetailPageWrapper = ({
+ initialStoreState = defaultStoreState,
+ children,
+}) => {
+ const store = getMockStore(initialStoreState);
+ return (
+
+
+
+ {children}
+
+
+
+ );
+};
+
+describe(' ', () => {
+ it('should render its children and display hero by default', () => {
+ render(hello world
);
+ // Verify children are rendered
+ expect(screen.getByText('hello world')).toBeInTheDocument();
+ // Verify Hero is rendered with the expected page title
+ expect(screen.getByText('Learner Credit Management')).toBeInTheDocument();
+ });
+
+ it.each([
+ { totalLearnersAssigned: 1, expectedLearnerString: 'learner' },
+ { totalLearnersAssigned: 2, expectedLearnerString: 'learners' },
+ ])('should render Toast notification for successful assignment allocation (%s)', async ({
+ totalLearnersAssigned,
+ expectedLearnerString,
+ }) => {
+ const ToastContextController = () => {
+ const {
+ displayToastForAssignmentAllocation,
+ closeToastForAssignmentAllocation,
+ } = useContext(BudgetDetailPageContext);
+
+ const handleDisplayToast = () => {
+ displayToastForAssignmentAllocation({ totalLearnersAssigned });
+ };
+
+ const handleCloseToast = () => {
+ closeToastForAssignmentAllocation();
+ };
+
+ return (
+
+ Open Toast
+ Close Toast
+
+ );
+ };
+ render( );
+
+ const expectedToastMessage = `Course successfully assigned to ${totalLearnersAssigned} ${expectedLearnerString}.`;
+
+ // Open Toast notification
+ userEvent.click(getButtonElement('Open Toast'));
+
+ // Verify Toast notification is rendered
+ expect(screen.getByText(expectedToastMessage)).toBeInTheDocument();
+
+ // Close Toast notification
+ userEvent.click(getButtonElement('Close Toast'));
+
+ // Verify Toast notification is no longer rendered
+ await waitFor(() => {
+ expect(screen.queryByText(expectedToastMessage)).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
index 9d5ac8df71..0143e8d5ba 100644
--- a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
@@ -12,7 +12,7 @@ import { QueryClientProvider } from '@tanstack/react-query';
import { BaseCatalogSearchResults } from '../search/CatalogSearchResults';
import { CONTENT_TYPE_COURSE } from '../data/constants';
import { queryClient } from '../../test/testUtils';
-import { useSubsidyAccessPolicy } from '../data';
+import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
// Mocking this connected component so as not to have to mock the algolia Api
const PAGINATE_ME = 'PAGINATE ME :)';
@@ -170,10 +170,18 @@ describe('Main Catalogs view works as expected', () => {
});
test('all courses rendered when search results available', async () => {
+ const budgetDetailPageContextValue = {
+ isSuccessfulAssignmentAllocationToastOpen: false,
+ totalLearnersAssigned: undefined,
+ displayToastForAssignmentAllocation: jest.fn(),
+ closeToastForAssignmentAllocation: jest.fn(),
+ };
renderWithRouter(
-
+
+
+
,
,
From ac1eb5cda12ee77faee1ca871a2b84d3934aaa65 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 9 Nov 2023 08:03:44 -0500
Subject: [PATCH 065/124] feat: add filtering to status column on assigned
table, ensure assigned table has ordering (#1085)
---
.../BudgetAssignmentsTable.jsx | 175 ++++++----
.../data/hooks/useBudgetContentAssignments.js | 36 ++
.../hooks/useBudgetContentAssignments.test.js | 152 +++++++++
.../tests/BudgetDetailPage.test.jsx | 307 +++++++++++++++---
4 files changed, 559 insertions(+), 111 deletions(-)
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index cfc6603800..69821935f9 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { DataTable } from '@edx/paragon';
+import { DataTable, CheckboxFilter } from '@edx/paragon';
import TableTextFilter from './TableTextFilter';
import CustomDataTableEmptyState from './CustomDataTableEmptyState';
import AssignmentDetailsTableCell from './AssignmentDetailsTableCell';
@@ -14,80 +14,119 @@ import AssignmentsTableRefreshAction from './AssignmentsTableRefreshAction';
const FilterStatus = (rest) => ;
+const getLearnerStateDisplayName = (learnerState) => {
+ if (learnerState === 'notifying') {
+ return 'Notifying learner';
+ }
+ if (learnerState === 'waiting') {
+ return 'Waiting for learner';
+ }
+ if (learnerState === 'failed') {
+ return 'Failed';
+ }
+
+ return undefined;
+};
+
const BudgetAssignmentsTable = ({
isLoading,
tableData,
fetchTableData,
-}) => (
- `-${formatPrice(row.original.contentQuantity / 100)}`,
- disableFilters: true,
- },
- {
- Header: 'Status',
- Cell: AssignmentStatusTableCell,
- disableFilters: true,
- },
- {
- Header: 'Recent action',
- Cell: AssignmentRecentActionTableCell,
- disableFilters: true,
- },
- ]}
- additionalColumns={[
- {
- Header: '',
- Cell: AssignmentRowActionTableCell,
- id: 'action',
- },
- ]}
- tableActions={[
- ,
- ]}
- initialTableOptions={{
- getRowId: row => row?.uuid?.toString(),
- }}
- initialState={{
- pageSize: PAGE_SIZE,
- pageIndex: DEFAULT_PAGE,
- sortBy: [],
- filters: [],
- }}
- fetchData={fetchTableData}
- data={tableData?.results || []}
- itemCount={tableData?.count || 0}
- pageCount={tableData?.numPages || 1}
- EmptyTableComponent={CustomDataTableEmptyState}
- bulkActions={[
- ,
- ,
- ]}
- />
-);
+}) => {
+ const statusFilterChoices = tableData.learnerStateCounts
+ .filter(({ learnerState }) => !!getLearnerStateDisplayName(learnerState))
+ .map(({ learnerState, count }) => ({
+ name: getLearnerStateDisplayName(learnerState),
+ number: count,
+ value: learnerState,
+ }));
+
+ return (
+ `-${formatPrice(row.original.contentQuantity / 100)}`,
+ disableFilters: true,
+ },
+ {
+ Header: 'Status',
+ accessor: 'learnerState',
+ Cell: AssignmentStatusTableCell,
+ Filter: CheckboxFilter,
+ filter: 'includesValue',
+ filterChoices: statusFilterChoices,
+ },
+ {
+ Header: 'Recent action',
+ accessor: 'recentAction',
+ Cell: AssignmentRecentActionTableCell,
+ disableFilters: true,
+ },
+ ]}
+ additionalColumns={[
+ {
+ Header: '',
+ Cell: AssignmentRowActionTableCell,
+ id: 'action',
+ },
+ ]}
+ tableActions={[
+ ,
+ ]}
+ initialTableOptions={{
+ getRowId: row => row?.uuid?.toString(),
+ }}
+ initialState={{
+ pageSize: PAGE_SIZE,
+ pageIndex: DEFAULT_PAGE,
+ sortBy: [{
+ id: 'recentAction',
+ desc: true,
+ }],
+ filters: [],
+ }}
+ fetchData={fetchTableData}
+ data={tableData.results || []}
+ itemCount={tableData.count || 0}
+ pageCount={tableData.numPages || 1}
+ EmptyTableComponent={CustomDataTableEmptyState}
+ bulkActions={[
+ ,
+ ,
+ ]}
+ />
+ );
+};
BudgetAssignmentsTable.propTypes = {
isLoading: PropTypes.bool.isRequired,
- tableData: PropTypes.shape().isRequired,
+ tableData: PropTypes.shape({
+ results: PropTypes.arrayOf(PropTypes.shape()),
+ learnerStateCounts: PropTypes.arrayOf(PropTypes.shape({
+ learnerState: PropTypes.string.isRequired,
+ count: PropTypes.number.isRequired,
+ })).isRequired,
+ count: PropTypes.number.isRequired,
+ numPages: PropTypes.number.isRequired,
+ }).isRequired,
fetchTableData: PropTypes.func.isRequired,
};
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
index 45031d520e..8bd33e9a07 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
@@ -9,6 +9,7 @@ const initialContentAssignmentsState = {
count: 0,
numPages: 0,
currentPage: 1,
+ learnerStateCounts: [],
};
const applyFiltersToOptions = (filters, options) => {
@@ -19,6 +20,40 @@ const applyFiltersToOptions = (filters, options) => {
if (searchQuery) {
Object.assign(options, { search: searchQuery });
}
+ const learnerStateFilter = filters.find(filter => filter.id === 'learnerState')?.value;
+ if (learnerStateFilter) {
+ Object.assign(options, { learnerState: learnerStateFilter.join(',') });
+ }
+};
+
+const applySortByToOptions = (sortBy, options) => {
+ if (!sortBy || sortBy.length === 0) {
+ return;
+ }
+ const apiFieldsForColumnAccessor = {
+ recentAction: { key: 'recent_action_time' },
+ learnerState: { key: 'learner_state_sort_order' },
+ amount: { key: 'content_quantity', isReversed: true },
+ };
+ const orderingStrings = sortBy.map(({ id, desc }) => {
+ const apiFieldForColumnAccessor = apiFieldsForColumnAccessor[id];
+ if (!apiFieldForColumnAccessor) {
+ return undefined;
+ }
+ const isApiFieldOrderingReversed = apiFieldForColumnAccessor.isReversed;
+ const apiFieldKey = apiFieldForColumnAccessor.key;
+ // Determine whether the API field ordering should be reversed based on the column accessor. This is
+ // necessary because the content_quantity field is a negative number, but if the column is sorted in a
+ // descending order, users would likely expect the larger contenr quantity to be at the top of the list,
+ // which is technically the smaller number since its negative.
+ if (isApiFieldOrderingReversed) {
+ return desc ? apiFieldKey : `-${apiFieldKey}`;
+ }
+ return desc ? `-${apiFieldKey}` : apiFieldKey;
+ }).filter(orderingString => !!orderingString);
+ Object.assign(options, {
+ ordering: orderingStrings.join(','),
+ });
};
const useBudgetContentAssignments = ({
@@ -40,6 +75,7 @@ const useBudgetContentAssignments = ({
pageSize: args.pageSize,
};
applyFiltersToOptions(args.filters, options);
+ applySortByToOptions(args.sortBy, options);
const assignmentsResponse = await EnterpriseAccessApiService.listContentAssignments(
assignmentConfigurationUUID,
options,
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
index a0715f22ef..27d7aef092 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
+++ b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
@@ -128,4 +128,156 @@ describe('useBudgetContentAssignments', () => {
},
);
});
+
+ it.each([
+ {
+ filters: [
+ {
+ id: 'learnerState',
+ value: ['waiting'],
+ },
+ ],
+ selectedLearnerStateQueryParam: 'waiting',
+ },
+ {
+ filters: [
+ {
+ id: 'learnerState',
+ value: ['waiting', 'notifying'],
+ },
+ ],
+ selectedLearnerStateQueryParam: 'waiting,notifying',
+ },
+ ])('handles learner state (status) filtering (%s)', async ({ filters, selectedLearnerStateQueryParam }) => {
+ const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
+ assignmentConfigurationUUID: '123',
+ isEnabled: true,
+ }));
+ const { fetchContentAssignments } = result.current;
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockListContentAssignments.mockResolvedValue({
+ data: {
+ results: [
+ {
+ id: 1,
+ name: 'test',
+ },
+ ],
+ count: 1,
+ numPages: 1,
+ currentPage: 1,
+ },
+ });
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ filters,
+ });
+
+ await waitForNextUpdate();
+
+ expect(mockListContentAssignments).toHaveBeenCalledWith(
+ '123',
+ {
+ page: 1,
+ pageSize: 10,
+ learnerState: selectedLearnerStateQueryParam,
+ },
+ );
+ });
+
+ it.each([
+ {
+ sortBy: [
+ {
+ id: 'learnerState',
+ desc: false,
+ },
+ ],
+ orderingQueryParam: 'learner_state_sort_order',
+ },
+ {
+ sortBy: [
+ {
+ id: 'learnerState',
+ desc: true,
+ },
+ ],
+ orderingQueryParam: '-learner_state_sort_order',
+ },
+ {
+ sortBy: [
+ {
+ id: 'recentAction',
+ desc: false,
+ },
+ ],
+ orderingQueryParam: 'recent_action_time',
+ },
+ {
+ sortBy: [
+ {
+ id: 'recentAction',
+ desc: true,
+ },
+ ],
+ orderingQueryParam: '-recent_action_time',
+ },
+ {
+ sortBy: [
+ {
+ id: 'amount',
+ desc: false,
+ },
+ ],
+ // Ordering is reversed for `content_quantity` field
+ orderingQueryParam: '-content_quantity',
+ },
+ {
+ sortBy: [
+ {
+ id: 'amount',
+ desc: true,
+ },
+ ],
+ // Ordering is reversed for `content_quantity` field
+ orderingQueryParam: 'content_quantity',
+ },
+ ])('handles ordering on appropriate columns (%s)', async ({ sortBy, orderingQueryParam }) => {
+ const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
+ assignmentConfigurationUUID: '123',
+ isEnabled: true,
+ }));
+ const { fetchContentAssignments } = result.current;
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockListContentAssignments.mockResolvedValue({
+ data: {
+ results: [
+ {
+ id: 1,
+ name: 'test',
+ },
+ ],
+ count: 1,
+ numPages: 1,
+ currentPage: 1,
+ },
+ });
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ sortBy,
+ });
+
+ await waitForNextUpdate();
+
+ expect(mockListContentAssignments).toHaveBeenCalledWith(
+ '123',
+ {
+ page: 1,
+ pageSize: 10,
+ ordering: orderingQueryParam,
+ },
+ );
+ });
});
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 5f3beec3a5..6f2574d25f 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -29,7 +29,7 @@ import {
mockSubsidyAccessPolicyUUID,
mockEnterpriseOfferId,
} from '../data/tests/constants';
-import { queryClient } from '../../test/testUtils';
+import { getButtonElement, queryClient } from '../../test/testUtils';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@@ -95,6 +95,18 @@ const mockFailedLinkedLearnerAction = {
actionType: 'learner_linked',
errorReason: 'internal_api_error',
};
+const mockLearnerContentAssignment = {
+ uuid: 'test-uuid',
+ learnerEmail: mockLearnerEmail,
+ contentKey: mockCourseKey,
+ contentTitle: mockContentTitle,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction],
+ errorReason: null,
+};
+
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
@@ -313,19 +325,8 @@ describe(' ', () => {
isLoading: false,
contentAssignments: {
count: 1,
- results: [
- {
- uuid: 'test-uuid',
- learnerEmail: mockLearnerEmail,
- contentKey: mockCourseKey,
- contentTitle: mockContentTitle,
- contentQuantity: -19900,
- learnerState: 'waiting',
- recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
- actions: [mockSuccessfulNotifiedAction],
- errorReason: null,
- },
- ],
+ results: [mockLearnerContentAssignment],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
numPages: 1,
currentPage: 1,
},
@@ -349,20 +350,258 @@ describe(' ', () => {
expect(assignedSection.getByText('Waiting for learner')).toBeInTheDocument();
expect(assignedSection.getByText(`Assigned: ${formatDate('2023-10-27')}`)).toBeInTheDocument();
- // Verify "Refresh" behavior
- const expectedRefreshArgs = {
+ const expectedTableFetchDataArgs = {
pageIndex: DEFAULT_PAGE,
pageSize: PAGE_SIZE,
filters: [],
- sortBy: [],
+ sortBy: [{ id: 'recentAction', desc: true }], // default table sort order
};
expect(mockFetchContentAssignments).toHaveBeenCalledTimes(1); // called once on initial render
- expect(mockFetchContentAssignments).toHaveBeenCalledWith(expect.objectContaining(expectedRefreshArgs));
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(expect.objectContaining(expectedTableFetchDataArgs));
+
+ // Verify "Refresh" behavior
const refreshCTA = assignedSection.getByText('Refresh', { selector: 'button' });
expect(refreshCTA).toBeInTheDocument();
userEvent.click(refreshCTA);
expect(mockFetchContentAssignments).toHaveBeenCalledTimes(2); // should be called again on refresh
- expect(mockFetchContentAssignments).toHaveBeenLastCalledWith(expect.objectContaining(expectedRefreshArgs));
+ expect(mockFetchContentAssignments).toHaveBeenLastCalledWith(expect.objectContaining(expectedTableFetchDataArgs));
+ });
+
+ it.each([
+ { sortByColumnHeader: 'Amount', expectedSortBy: [{ id: 'amount', desc: false }] },
+ ])('renders sortable assigned table data', async ({ sortByColumnHeader, expectedSortBy }) => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ const mockFetchContentAssignments = jest.fn();
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [mockLearnerContentAssignment],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: mockFetchContentAssignments,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
+ renderWithRouter( );
+
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ const expectedDefaultTableFetchDataArgs = {
+ pageIndex: DEFAULT_PAGE,
+ pageSize: PAGE_SIZE,
+ filters: [],
+ sortBy: [{ id: 'recentAction', desc: true }], // default table sort order
+ };
+ const expectedDefaultTableFetchDataArgsAfterSort = {
+ ...expectedDefaultTableFetchDataArgs,
+ sortBy: expectedSortBy,
+ };
+
+ expect(mockFetchContentAssignments).toHaveBeenCalledTimes(1); // called once on initial render
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedDefaultTableFetchDataArgs),
+ );
+
+ // Verify amount column sort
+ const amountColumnHeader = assignedSection.getByText(sortByColumnHeader);
+ userEvent.click(amountColumnHeader);
+
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedDefaultTableFetchDataArgsAfterSort),
+ );
+ });
+
+ it.each([
+ {
+ filterBy: {
+ field: 'status',
+ value: ['waiting'],
+ },
+ expectedFilters: [{ id: 'learnerState', value: ['waiting'] }],
+ },
+ {
+ filterBy: {
+ field: 'search',
+ value: mockLearnerEmail,
+ },
+ expectedFilters: [{ id: 'assignmentDetails', value: mockLearnerEmail }],
+ },
+ ])('renders filterable assigned table data (%s)', async ({
+ filterBy,
+ expectedFilters,
+ }) => {
+ const { field, value } = filterBy;
+
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ const mockFetchContentAssignments = jest.fn();
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [mockLearnerContentAssignment],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: mockFetchContentAssignments,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
+ renderWithRouter( );
+
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ const expectedDefaultTableFetchDataArgs = {
+ pageIndex: DEFAULT_PAGE,
+ pageSize: PAGE_SIZE,
+ filters: [],
+ sortBy: [{ id: 'recentAction', desc: true }], // default table sort order
+ };
+ const expectedTableFetchDataArgsAfterFilter = {
+ ...expectedDefaultTableFetchDataArgs,
+ filters: expectedFilters,
+ };
+ expect(mockFetchContentAssignments).toHaveBeenCalledTimes(1); // called once on initial render
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedDefaultTableFetchDataArgs),
+ );
+
+ if (field === 'status') {
+ const filtersButton = getButtonElement('Filters', { screenOverride: assignedSection });
+ userEvent.click(filtersButton);
+ const filtersDropdown = screen.getByRole('group', { name: 'Status' });
+ const filtersDropdownContainer = within(filtersDropdown);
+ if (value.includes('waiting')) {
+ const waitingForLearnerOption = filtersDropdownContainer.getByLabelText('Waiting for learner 1', { exact: false });
+ expect(waitingForLearnerOption).toBeInTheDocument();
+ userEvent.click(waitingForLearnerOption);
+
+ await waitFor(() => {
+ expect(waitingForLearnerOption).toBeChecked();
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedTableFetchDataArgsAfterFilter),
+ );
+ });
+ }
+ }
+
+ if (field === 'search') {
+ const assignmentDetailsInputField = assignedSection.getByLabelText('Search by assignment details');
+ userEvent.type(assignmentDetailsInputField, value);
+
+ await waitFor(() => {
+ expect(assignmentDetailsInputField).toHaveValue(value);
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedTableFetchDataArgsAfterFilter),
+ );
+ });
+ }
+ });
+
+ it.each([
+ {
+ columnHeader: 'Amount',
+ columnId: 'amount',
+ },
+ ])('renders sortable assigned table data (%s)', async ({ columnHeader, columnId }) => {
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ const mockFetchContentAssignments = jest.fn();
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [mockLearnerContentAssignment],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: mockFetchContentAssignments,
+ });
+ useOfferRedemptions.mockReturnValue({
+ isLoading: false,
+ offerRedemptions: mockEmptyOfferRedemptions,
+ fetchOfferRedemptions: jest.fn(),
+ });
+ renderWithRouter( );
+
+ const assignedSection = within(screen.getByText('Assigned').closest('section'));
+ const expectedDefaultTableFetchDataArgs = {
+ pageIndex: DEFAULT_PAGE,
+ pageSize: PAGE_SIZE,
+ filters: [],
+ sortBy: [{ id: 'recentAction', desc: true }], // default table sort order
+ };
+ const expectedTableFetchDataArgsAfterSortAsc = {
+ ...expectedDefaultTableFetchDataArgs,
+ sortBy: [{ id: columnId, desc: false }],
+ };
+ const expectedTableFetchDataArgsAfterSortDesc = {
+ ...expectedDefaultTableFetchDataArgs,
+ sortBy: [{ id: columnId, desc: true }],
+ };
+ expect(mockFetchContentAssignments).toHaveBeenCalledTimes(1); // called once on initial render
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedDefaultTableFetchDataArgs),
+ );
+
+ const orderedColumnHeader = assignedSection.getByText(columnHeader);
+ userEvent.click(orderedColumnHeader);
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedTableFetchDataArgsAfterSortAsc),
+ );
+ userEvent.click(orderedColumnHeader);
+ expect(mockFetchContentAssignments).toHaveBeenCalledWith(
+ expect.objectContaining(expectedTableFetchDataArgsAfterSortDesc),
+ );
});
it('renders with assigned table data "View Course" hyperlink default when content title is null', () => {
@@ -385,19 +624,8 @@ describe(' ', () => {
isLoading: false,
contentAssignments: {
count: 1,
- results: [
- {
- uuid: 'test-uuid',
- learnerEmail: mockLearnerEmail,
- contentKey: mockCourseKey,
- contentTitle: null,
- contentQuantity: -19900,
- learnerState: 'waiting',
- recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
- actions: [mockSuccessfulNotifiedAction],
- errorReason: null,
- },
- ],
+ results: [{ ...mockLearnerContentAssignment, contentTitle: null }],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
numPages: 1,
currentPage: 1,
},
@@ -513,16 +741,14 @@ describe(' ', () => {
count: 1,
results: [
{
- uuid: 'test-uuid',
+ ...mockLearnerContentAssignment,
learnerEmail: hasLearnerEmail ? mockLearnerEmail : null,
- contentKey: mockCourseKey,
- contentQuantity: -19900,
learnerState,
- recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions,
errorReason,
},
],
+ learnerStateCounts: [{ learnerState, count: 1 }],
numPages: 1,
currentPage: 1,
},
@@ -792,16 +1018,11 @@ describe(' ', () => {
count: 1,
results: [
{
- uuid: 'test-uuid',
- contentKey: mockCourseKey,
- contentQuantity: -19900,
+ ...mockLearnerContentAssignment,
learnerState,
- recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
- actions: [],
- errorReason: null,
- state: 'allocated',
},
],
+ learnerStateCounts: [{ learnerState, count: 1 }],
numPages: 1,
currentPage: 1,
},
From ca9b6649e1d10448856ff83ffe63b86091e9607a Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Tue, 7 Nov 2023 20:24:35 +0000
Subject: [PATCH 066/124] feat: implementing gated top down allocation table
subsidy service api usage
---
.../BudgetDetailRedemptions.jsx | 14 +-
.../data/hooks/useOfferRedemptions.js | 55 ++++--
.../data/hooks/useOfferRedemptions.test.js | 89 ----------
.../data/hooks/useOfferRedemptions.test.jsx | 165 ++++++++++++++++++
.../data/tests/constants.js | 2 +
.../learner-credit-management/data/utils.js | 10 ++
.../tests/BudgetDetailPage.test.jsx | 4 +-
.../services/EnterpriseSubsidyApiService.js | 20 ++-
.../tests/EnterpriseSubsidyApiService.test.js | 11 +-
9 files changed, 255 insertions(+), 115 deletions(-)
delete mode 100644 src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js
create mode 100644 src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index 67fd14f087..0ea2fb766a 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -5,14 +5,18 @@ import { connect } from 'react-redux';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { useBudgetId, useOfferRedemptions } from './data';
-const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
+const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
const {
isLoading,
offerRedemptions,
fetchOfferRedemptions,
- } = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);
-
+ } = useOfferRedemptions(
+ enterpriseUUID,
+ enterpriseOfferId,
+ subsidyAccessPolicyId,
+ enterpriseFeatures.topDownAssignmentRealTimeLcm,
+ );
return (
Spent
@@ -30,11 +34,15 @@ const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
};
const mapStateToProps = state => ({
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
enterpriseUUID: state.portalConfiguration.enterpriseId,
});
BudgetDetailRedemptions.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }).isRequired,
};
export default connect(mapStateToProps)(BudgetDetailRedemptions);
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
index 356c05404d..c5f824b8a1 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
@@ -10,8 +10,10 @@ import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import debounce from 'lodash.debounce';
import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
import { API_FIELDS_BY_TABLE_COLUMN_ACCESSOR } from '../constants';
-import { transformUtilizationTableResults } from '../utils';
+import { transformUtilizationTableResults, transformUtilizationTableSubsidyTransactionResults } from '../utils';
+import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
const applySortByToOptions = (sortBy, options) => {
const orderingStrings = sortBy.map(({ id, desc }) => {
@@ -29,19 +31,26 @@ const applySortByToOptions = (sortBy, options) => {
});
};
-const applyFiltersToOptions = (filters, options) => {
+const applyFiltersToOptions = (filters, options, shouldFetchSubsidyTransactions = false) => {
const courseProductLineSearchQuery = filters?.find(filter => filter.id === 'courseProductLine')?.value;
- const searchQuery = filters?.find(filter => filter.id.toLowerCase() === 'enrollment details')?.value;
+ const searchQuery = filters?.find(filter => filter.id === 'enrollmentDetails')?.value;
if (courseProductLineSearchQuery) {
Object.assign(options, { courseProductLine: courseProductLineSearchQuery });
}
if (searchQuery) {
- Object.assign(options, { searchAll: searchQuery });
+ const searchParams = {};
+ searchParams[shouldFetchSubsidyTransactions ? 'search' : 'searchAll'] = searchQuery;
+ Object.assign(options, searchParams);
}
};
-const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => {
+const useOfferRedemptions = (
+ enterpriseUUID,
+ offerId = null,
+ budgetId = null,
+ shouldFetchSubsidyTransactions = false,
+) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
const [offerRedemptions, setOfferRedemptions] = useState({
@@ -49,6 +58,7 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
pageCount: 0,
results: [],
});
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(budgetId);
const fetchOfferRedemptions = useCallback((args) => {
const fetch = async () => {
@@ -69,14 +79,26 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
applySortByToOptions(args.sortBy, options);
}
if (args.filters?.length > 0) {
- applyFiltersToOptions(args.filters, options);
+ applyFiltersToOptions(args.filters, options, shouldFetchSubsidyTransactions);
}
- const response = await EnterpriseDataApiService.fetchCourseEnrollments(
- enterpriseUUID,
- options,
- );
- const data = camelCaseObject(response.data);
- const transformedTableResults = transformUtilizationTableResults(data.results);
+ let data;
+ let transformedTableResults;
+ if (budgetId && shouldFetchSubsidyTransactions) {
+ const response = await SubsidyApiService.fetchCustomerTransactions(
+ subsidyAccessPolicy?.subsidyUuid,
+ options,
+ );
+ data = camelCaseObject(response.data);
+ transformedTableResults = transformUtilizationTableSubsidyTransactionResults(data.results);
+ } else {
+ const response = await EnterpriseDataApiService.fetchCourseEnrollments(
+ enterpriseUUID,
+ options,
+ );
+ data = camelCaseObject(response.data);
+ transformedTableResults = transformUtilizationTableResults(data.results);
+ }
+
setOfferRedemptions({
itemCount: data.count,
pageCount: data.numPages,
@@ -104,7 +126,14 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
if (offerId || budgetId) {
fetch();
}
- }, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents]);
+ }, [
+ enterpriseUUID,
+ offerId,
+ budgetId,
+ shouldTrackFetchEvents,
+ shouldFetchSubsidyTransactions,
+ subsidyAccessPolicy?.subsidyUuid,
+ ]);
const debouncedFetchOfferRedemptions = useMemo(() => debounce(fetchOfferRedemptions, 300), [fetchOfferRedemptions]);
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js
deleted file mode 100644
index cfe7affd2d..0000000000
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import { act, renderHook } from '@testing-library/react-hooks/dom';
-import { camelCaseObject } from '@edx/frontend-platform/utils';
-
-import useOfferRedemptions from './useOfferRedemptions';
-import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
-
-const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
-const TEST_ENTERPRISE_OFFER_ID = 1;
-
-const mockOfferEnrollments = [{
- user_email: 'edx@example.com',
- course_title: 'Test Course Title',
- course_list_price: '100.00',
- enrollment_date: '2022-01-01',
-}];
-
-const mockOfferEnrollmentsResponse = {
- count: 100,
- current_page: 1,
- num_pages: 5,
- results: mockOfferEnrollments,
-};
-
-const mockEnterpriseOffer = {
- id: TEST_ENTERPRISE_OFFER_ID,
-};
-
-jest.mock('../../../../data/services/EnterpriseDataApiService');
-
-describe('useOfferRedemptions', () => {
- it('should fetch enrollment/redemptions metadata for enterprise offer', async () => {
- EnterpriseDataApiService.fetchCourseEnrollments.mockResolvedValueOnce({ data: mockOfferEnrollmentsResponse });
- const budgetId = 'test-budget-id';
- const { result, waitForNextUpdate } = renderHook(() => useOfferRedemptions(
- TEST_ENTERPRISE_UUID,
- mockEnterpriseOffer.id,
- budgetId,
- ));
-
- expect(result.current).toMatchObject({
- offerRedemptions: {
- itemCount: 0,
- pageCount: 0,
- results: [],
- },
- isLoading: true,
- fetchOfferRedemptions: expect.any(Function),
- });
- act(() => {
- result.current.fetchOfferRedemptions({
- pageIndex: 0, // `DataTable` uses zero-based indexing
- pageSize: 20,
- sortBy: [
- { id: 'enrollmentDate', desc: true },
- ],
- filters: [
- { id: 'Enrollment Details', value: mockOfferEnrollments[0].user_email },
- ],
- });
- });
-
- await waitForNextUpdate();
-
- const expectedApiOptions = {
- page: 1,
- pageSize: 20,
- offerId: mockEnterpriseOffer.id,
- ordering: '-enrollment_date', // default sort order
- searchAll: mockOfferEnrollments[0].user_email,
- ignoreNullCourseListPrice: true,
- budgetId,
- };
- expect(EnterpriseDataApiService.fetchCourseEnrollments).toHaveBeenCalledWith(
- TEST_ENTERPRISE_UUID,
- expectedApiOptions,
- );
- expect(result.current).toMatchObject({
- offerRedemptions: {
- itemCount: 100,
- pageCount: 5,
- results: camelCaseObject(mockOfferEnrollments),
- },
- isLoading: false,
- fetchOfferRedemptions: expect.any(Function),
- });
-
- expect(expectedApiOptions.budgetId).toBe(budgetId);
- });
-});
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
new file mode 100644
index 0000000000..9b5d1c3c9f
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
@@ -0,0 +1,165 @@
+import { QueryClientProvider } from '@tanstack/react-query';
+import { act, renderHook } from '@testing-library/react-hooks/dom';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+
+import useOfferRedemptions from './useOfferRedemptions';
+import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
+import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
+import { queryClient } from '../../../test/testUtils';
+
+const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
+const TEST_ENTERPRISE_OFFER_ID = 1;
+const subsidyUuid = 'test-subsidy-uuid';
+const courseTitle = 'Test Course Title';
+const userEmail = 'edx@example.com';
+
+const mockOfferEnrollments = [{
+ user_email: userEmail,
+ course_title: courseTitle,
+ course_list_price: '100.00',
+ enrollment_date: '2022-01-01',
+}];
+
+const mockOfferEnrollmentsResponse = {
+ count: 100,
+ current_page: 1,
+ num_pages: 5,
+ results: mockOfferEnrollments,
+};
+
+const mockSubsidyTransactionResponse = {
+ count: 100,
+ current_page: 1,
+ num_pages: 5,
+ results: [{
+ uuid: subsidyUuid,
+ state: 'committed',
+ idempotency_key: '5d00d319-fe46-41f7-b14e-966534da9f72',
+ lms_user_id: 999,
+ lms_user_email: userEmail,
+ content_key: 'course-v1:edX+test+course.1',
+ content_title: courseTitle,
+ quantity: -1000,
+ unit: 'usd_cents',
+ }],
+};
+
+const mockEnterpriseOffer = {
+ id: TEST_ENTERPRISE_OFFER_ID,
+};
+
+jest.mock('./useSubsidyAccessPolicy');
+jest.mock('../../../../data/services/EnterpriseDataApiService');
+jest.mock('../../../../data/services/EnterpriseSubsidyApiService');
+
+const wrapper = ({ children }) => (
+ {children}
+);
+
+describe('useOfferRedemptions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it.each([
+ {
+ budgetId: 'test-budget-id',
+ offerId: undefined,
+ shouldFetchSubsidyTransactions: true,
+ },
+ {
+ budgetId: 'test-budget-id',
+ offerId: undefined,
+ shouldFetchSubsidyTransactions: false,
+ },
+ {
+ budgetId: undefined,
+ offerId: mockEnterpriseOffer.id,
+ shouldFetchSubsidyTransactions: false,
+ },
+ ])('should fetch enrollment/redemptions metadata for enterprise offer', async ({
+ budgetId,
+ offerId,
+ shouldFetchSubsidyTransactions,
+ }) => {
+ EnterpriseDataApiService.fetchCourseEnrollments.mockResolvedValueOnce({ data: mockOfferEnrollmentsResponse });
+ SubsidyApiService.fetchCustomerTransactions.mockResolvedValueOnce({ data: mockSubsidyTransactionResponse });
+ useSubsidyAccessPolicy.mockReturnValue({ data: { subsidyUuid } });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useOfferRedemptions(TEST_ENTERPRISE_UUID, offerId, budgetId, shouldFetchSubsidyTransactions),
+ { wrapper },
+ );
+
+ expect(result.current).toMatchObject({
+ offerRedemptions: {
+ itemCount: 0,
+ pageCount: 0,
+ results: [],
+ },
+ isLoading: true,
+ fetchOfferRedemptions: expect.any(Function),
+ });
+ act(() => {
+ result.current.fetchOfferRedemptions({
+ pageIndex: 0, // `DataTable` uses zero-based indexing
+ pageSize: 20,
+ sortBy: [
+ { id: 'enrollmentDate', desc: true },
+ ],
+ filters: [
+ { id: 'enrollmentDetails', value: mockOfferEnrollments[0].user_email },
+ ],
+ });
+ });
+
+ await waitForNextUpdate();
+
+ if (budgetId && shouldFetchSubsidyTransactions) {
+ const expectedApiOptions = {
+ page: 1,
+ pageSize: 20,
+ offerId,
+ ordering: '-enrollment_date', // default sort order
+ search: mockOfferEnrollments[0].user_email,
+ ignoreNullCourseListPrice: true,
+ budgetId,
+ };
+ expect(SubsidyApiService.fetchCustomerTransactions).toHaveBeenCalledWith(
+ subsidyUuid,
+ expectedApiOptions,
+ );
+ } else {
+ const expectedApiOptions = {
+ page: 1,
+ pageSize: 20,
+ offerId,
+ ordering: '-enrollment_date', // default sort order
+ searchAll: mockOfferEnrollments[0].user_email,
+ ignoreNullCourseListPrice: true,
+ budgetId,
+ };
+ expect(EnterpriseDataApiService.fetchCourseEnrollments).toHaveBeenCalledWith(
+ TEST_ENTERPRISE_UUID,
+ expectedApiOptions,
+ );
+ }
+
+ const mockExpectedResultsObj = shouldFetchSubsidyTransactions ? [{
+ courseListPrice: 10,
+ courseTitle,
+ userEmail,
+ }] : camelCaseObject(mockOfferEnrollments);
+
+ expect(result.current).toMatchObject({
+ offerRedemptions: {
+ itemCount: 100,
+ pageCount: 5,
+ results: mockExpectedResultsObj,
+ },
+ isLoading: false,
+ fetchOfferRedemptions: expect.any(Function),
+ });
+ });
+});
diff --git a/src/components/learner-credit-management/data/tests/constants.js b/src/components/learner-credit-management/data/tests/constants.js
index a72d643f63..285a0e7e82 100644
--- a/src/components/learner-credit-management/data/tests/constants.js
+++ b/src/components/learner-credit-management/data/tests/constants.js
@@ -12,6 +12,7 @@ export const mockAssignableSubsidyAccessPolicy = {
spendAvailableUsd: 10000,
},
isAssignable: true,
+ subsidyUuid: 'mock-subsidy-uuid',
};
export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
@@ -22,4 +23,5 @@ export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
spendAvailableUsd: 10000,
},
isAssignable: false,
+ subsidyUuid: 'mock-subsidy-uuid',
};
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 61122ad402..5780e28eb2 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -95,6 +95,16 @@ export const transformUtilizationTableResults = results => results.map(result =>
courseKey: result.courseKey,
}));
+export const transformUtilizationTableSubsidyTransactionResults = results => results.map(result => ({
+ created: result.created,
+ enterpriseEnrollmentId: result.fulfillmentIdentifier,
+ userEmail: result.lmsUserEmail,
+ courseTitle: result.contentTitle,
+ courseListPrice: result.unit === 'usd_cents' ? -1 * (result.quantity / 100) : -1 * results.quantity,
+ uuid: result.uuid,
+ courseKey: result.contentKey,
+}));
+
/**
* Gets appropriate color variant for the annotated progress bar.
*
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 6f2574d25f..d8729c61b5 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -209,11 +209,11 @@ describe(' ', () => {
it.each([
{
budgetId: mockEnterpriseOfferId,
- expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null],
+ expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null, true],
},
{
budgetId: mockSubsidyAccessPolicyUUID,
- expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID],
+ expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID, true],
},
])('displays spend table in "Activity" tab with empty results (%s)', async ({
budgetId,
diff --git a/src/data/services/EnterpriseSubsidyApiService.js b/src/data/services/EnterpriseSubsidyApiService.js
index f1769424bf..84550170fa 100644
--- a/src/data/services/EnterpriseSubsidyApiService.js
+++ b/src/data/services/EnterpriseSubsidyApiService.js
@@ -4,19 +4,29 @@ import { snakeCaseObject } from '@edx/frontend-platform';
import { configuration } from '../../config';
class SubsidyApiService {
- static baseUrl = `${configuration.ENTERPRISE_SUBSIDY_BASE_URL}/api/v1`;
+ static baseUrl = `${configuration.ENTERPRISE_SUBSIDY_BASE_URL}/api`;
+
+ static baseUrlV1 = `${this.baseUrl}/v1`;
+
+ static baseUrlV2 = `${this.baseUrl}/v2`;
static apiClient = getAuthenticatedHttpClient;
+ static fetchCustomerTransactions(subsidyUuid, options = {}) {
+ const queryParams = new URLSearchParams({
+ ...snakeCaseObject(options),
+ });
+ const url = `${SubsidyApiService.baseUrlV2}/subsidies/${subsidyUuid}/transactions/?${queryParams.toString()}`;
+ return SubsidyApiService.apiClient().get(url);
+ }
+
static getSubsidyByCustomerUUID(uuid, options = {}) {
const queryParams = new URLSearchParams({
enterprise_customer_uuid: uuid,
...snakeCaseObject(options),
});
- const url = `${SubsidyApiService.baseUrl}/subsidies/?${queryParams.toString()}`;
- return SubsidyApiService.apiClient({
- useCache: configuration.USE_API_CACHE,
- }).get(url, { clearCacheEntry: true });
+ const url = `${SubsidyApiService.baseUrlV1}/subsidies/?${queryParams.toString()}`;
+ return SubsidyApiService.apiClient().get(url);
}
}
diff --git a/src/data/services/tests/EnterpriseSubsidyApiService.test.js b/src/data/services/tests/EnterpriseSubsidyApiService.test.js
index 18797031f6..6c894bc1f8 100644
--- a/src/data/services/tests/EnterpriseSubsidyApiService.test.js
+++ b/src/data/services/tests/EnterpriseSubsidyApiService.test.js
@@ -15,11 +15,16 @@ describe('EnterpriseSubsidyApiService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
-
+ test('fetchCustomerTransactions calls the API to fetch transactions by enterprise subsidy', () => {
+ const mockSubsidyUUID = 'test-subsidy-uuid';
+ const expectedUrl = `${SubsidyApiService.baseUrlV2}/subsidies/${mockSubsidyUUID}/transactions/?`;
+ SubsidyApiService.fetchCustomerTransactions(mockSubsidyUUID);
+ expect(axios.get).toBeCalledWith(expectedUrl);
+ });
test('getSubsidyByCustomerUUID calls the API to fetch subsides by enterprise customer UUID', () => {
const mockCustomerUUID = 'test-customer-uuid';
- const expectedUrl = `${SubsidyApiService.baseUrl}/subsidies/?enterprise_customer_uuid=${mockCustomerUUID}`;
+ const expectedUrl = `${SubsidyApiService.baseUrlV1}/subsidies/?enterprise_customer_uuid=${mockCustomerUUID}`;
SubsidyApiService.getSubsidyByCustomerUUID(mockCustomerUUID);
- expect(axios.get).toBeCalledWith(expectedUrl, { clearCacheEntry: true });
+ expect(axios.get).toBeCalledWith(expectedUrl);
});
});
From 9157b00d6aa867707691524cb436ad132bde14d0 Mon Sep 17 00:00:00 2001
From: Marlon Keating <322346+marlonkeating@users.noreply.github.com>
Date: Thu, 9 Nov 2023 09:06:22 -0800
Subject: [PATCH 067/124] feat: Add loading spinner to FormWorkflow next button
(#1086)
fix: unit tests
chore: remove commented out line
---
src/components/forms/FormWorkflow.tsx | 21 +++++++++++++++++----
src/components/forms/_FormWorkflow.scss | 4 ++++
2 files changed, 21 insertions(+), 4 deletions(-)
create mode 100644 src/components/forms/_FormWorkflow.scss
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index 80288c075a..d17d131548 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import type { Dispatch } from 'react';
import {
- ActionRow, Button, FullscreenModal, Stepper, useToggle,
+ ActionRow, Button, FullscreenModal, Spinner, Stepper, useToggle,
} from '@edx/paragon';
import { Launch } from '@edx/paragon/icons';
@@ -19,6 +19,7 @@ import { HELP_CENTER_LINK, SUBMIT_TOAST_MESSAGE } from '../settings/data/constan
import ConfigErrorModal from '../settings/ConfigErrorModal';
import { channelMapping, pollAsync } from '../../utils';
import HelpCenterButton from '../settings/HelpCenterButton';
+import './_FormWorkflow.scss';
export const WAITING_FOR_ASYNC_OPERATION = 'WAITING FOR ASYNC OPERATION';
@@ -107,6 +108,7 @@ const FormWorkflow = ({
closeSavedChangesModal,
] = useToggle(false);
const [helpCenterLink, setHelpCenterLink] = useState(HELP_CENTER_LINK);
+ const [nextInProgress, setNextInProgress] = useState(false);
const nextButtonConfig = step?.nextButtonConfig(formFields);
const awaitingAsyncAction = stateMap && stateMap[WAITING_FOR_ASYNC_OPERATION];
@@ -131,6 +133,7 @@ const FormWorkflow = ({
} else {
let advance = true;
if (nextButtonConfig && nextButtonConfig.onClick) {
+ setNextInProgress(true);
const newFormFields: FormConfigData = await nextButtonConfig.onClick({
formFields,
errHandler: setFormError,
@@ -140,6 +143,7 @@ const FormWorkflow = ({
if (newFormFields) {
dispatch(updateFormFieldsAction({ formFields: newFormFields }));
}
+ setNextInProgress(false);
if (nextButtonConfig?.awaitSuccess) {
advance = await pollAsync(
() => nextButtonConfig.awaitSuccess?.awaitCondition?.({
@@ -211,6 +215,16 @@ const FormWorkflow = ({
const showBackButton = (step?.index !== undefined) && (step.index > 0) && step.showBackButton;
// Show cancel button by default
const showCancelButton = step?.showCancelButton === undefined || step?.showCancelButton;
+ let nextButtonContents = nextButtonConfig && (
+ <>
+ {nextButtonConfig.buttonText}
+ {nextButtonConfig.opensNewWindow && }
+ >
+ );
+ if (nextInProgress) {
+ // show spinner if Next button operation is ongoing
+ nextButtonContents = ;
+ }
return (
<>
({
{showCancelButton && Cancel }
{showBackButton && Back }
{nextButtonConfig && (
-
- {nextButtonConfig.buttonText}
- {nextButtonConfig.opensNewWindow && }
+
+ {nextButtonContents}
)}
diff --git a/src/components/forms/_FormWorkflow.scss b/src/components/forms/_FormWorkflow.scss
new file mode 100644
index 0000000000..86e8a9828e
--- /dev/null
+++ b/src/components/forms/_FormWorkflow.scss
@@ -0,0 +1,4 @@
+.next-button {
+ min-width: 60px;
+ min-height: 40px;
+}
\ No newline at end of file
From df749dcd021efd6c0b54f55e50eca88fe31e3820 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Tue, 14 Nov 2023 15:19:21 -0500
Subject: [PATCH 068/124] feat: assignment modal summary after user input
(#1088)
---
.../cards/AssignmentModalContent.jsx | 84 ++++++++++++++-----
.../cards/AssignmentModalSummary.jsx | 59 +++++++++++++
.../AssignmentModalSummaryEmptyState.jsx | 10 +++
.../AssignmentModalSummaryLearnerList.jsx | 73 ++++++++++++++++
.../cards/BaseCourseCard.jsx | 4 +-
.../cards/CourseCard.test.jsx | 27 +++++-
.../CreateAllocationErrorAlertModals.jsx | 6 +-
.../ContentNotInCatalogErrorAlertModal.jsx | 0
.../NotEnoughBalanceAlertModal.jsx | 0
.../SystemErrorAlertModal.jsx | 0
.../cards/data/constants.js | 6 +-
.../cards/data/useCourseCardMetadata.js | 4 +-
.../cards/data/utils.js | 6 +-
13 files changed, 247 insertions(+), 32 deletions(-)
create mode 100644 src/components/learner-credit-management/cards/AssignmentModalSummary.jsx
create mode 100644 src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx
create mode 100644 src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx
rename src/components/learner-credit-management/cards/{status-modals => assignment-allocation-status-modals}/ContentNotInCatalogErrorAlertModal.jsx (100%)
rename src/components/learner-credit-management/cards/{status-modals => assignment-allocation-status-modals}/NotEnoughBalanceAlertModal.jsx (100%)
rename src/components/learner-credit-management/cards/{status-modals => assignment-allocation-status-modals}/SystemErrorAlertModal.jsx (100%)
diff --git a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
index e13e317d58..b85e2ba6d5 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
@@ -1,5 +1,8 @@
-import React, { useState } from 'react';
+import React, {
+ useCallback, useEffect, useMemo, useState,
+} from 'react';
import PropTypes from 'prop-types';
+import debounce from 'lodash.debounce';
import {
Container,
Stack,
@@ -12,19 +15,45 @@ import {
import BaseCourseCard from './BaseCourseCard';
import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../data';
import { ImpactOnYourLearnerCreditBudget, ManagingThisAssignment, NextStepsForAssignedLearners } from './Collapsibles';
+import AssignmentModalSummary from './AssignmentModalSummary';
+import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from './data';
const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
- const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const [learnerEmails, setLearnerEmails] = useState([]);
+ const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
+
const handleEmailAddressInputChange = (e) => {
const inputValue = e.target.value;
- const emailAddresses = inputValue.split('\n').filter((email) => email.trim().length > 0);
setEmailAddressesInputValue(inputValue);
- onEmailAddressesChange(emailAddresses);
};
+ const handleEmailAddressesChanged = useCallback((value) => {
+ if (!value) {
+ setLearnerEmails([]);
+ onEmailAddressesChange([]);
+ }
+ const emails = value.split('\n').filter((email) => email.trim().length > 0);
+ setLearnerEmails(emails);
+ onEmailAddressesChange(emails);
+ }, [onEmailAddressesChange]);
+
+ const debouncedHandleEmailAddressesChanged = useMemo(
+ () => debounce(handleEmailAddressesChanged, EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY),
+ [handleEmailAddressesChanged],
+ );
+
+ useEffect(() => {
+ debouncedHandleEmailAddressesChanged(emailAddressesInputValue);
+ }, [emailAddressesInputValue, debouncedHandleEmailAddressesChanged]);
+
+ const hasLearnerEmails = learnerEmails.length > 0;
+ const spendAvailable = subsidyAccessPolicy.aggregates.spendAvailableUsd;
+ const costToAssignLearners = learnerEmails.length * course.normalizedMetadata.contentPrice;
+ const remainingBalanceAfterAssignment = spendAvailable - costToAssignLearners;
+
return (
@@ -35,7 +64,7 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
-
+
Assign to
{
Pay by Learner Credit
- Summary
-
-
- You haven't entered any learners yet.
- Add learner emails to get started.
-
-
-
+
+
Learner Credit Budget: {subsidyAccessPolicy.displayName ?? 'Overview'}
-
-
- Available balance
- {formatPrice(subsidyAccessPolicy.aggregates.spendAvailableUsd)}
-
-
+
+
+
+
+
+ Available balance
+ {formatPrice(spendAvailable)}
+
+ {hasLearnerEmails && (
+
+ Total cost
+ -{formatPrice(costToAssignLearners)}
+
+ )}
+
+
+
+ {hasLearnerEmails && (
+
+
+ Remaining after assignment
+ {formatPrice(remainingBalanceAfterAssignment)}
+
+
+ )}
+
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx
new file mode 100644
index 0000000000..3baa98da0f
--- /dev/null
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Card, Stack } from '@edx/paragon';
+
+import { formatPrice } from '../data';
+import AssignmentModalSummaryEmptyState from './AssignmentModalSummaryEmptyState';
+import AssignmentModalSummaryLearnerList from './AssignmentModalSummaryLearnerList';
+
+const AssignmentModalSummary = ({
+ course,
+ learnerEmails,
+}) => {
+ const learnerEmailsCount = learnerEmails.length;
+ const hasLearnerEmails = learnerEmailsCount > 0;
+ const totalAssignmentCost = learnerEmailsCount * course.normalizedMetadata.contentPrice;
+
+ let summaryHeading = 'Summary';
+ if (hasLearnerEmails) {
+ summaryHeading = `${summaryHeading} (${learnerEmailsCount})`;
+ }
+ return (
+ <>
+ {summaryHeading}
+
+
+
+ {hasLearnerEmails ? (
+
+ ) : (
+
+ )}
+
+
+ {hasLearnerEmails && (
+
+
+ Total assignment cost
+ {formatPrice(totalAssignmentCost)}
+
+
+ )}
+
+ >
+ );
+};
+
+AssignmentModalSummary.propTypes = {
+ course: PropTypes.shape({
+ normalizedMetadata: PropTypes.shape({
+ contentPrice: PropTypes.number.isRequired,
+ }).isRequired,
+ }).isRequired,
+ learnerEmails: PropTypes.arrayOf(PropTypes.string).isRequired,
+};
+
+export default AssignmentModalSummary;
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx
new file mode 100644
index 0000000000..694c046f23
--- /dev/null
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+
+const AssignmentModalSummaryEmptyState = () => (
+ <>
+ You haven't entered any learners yet.
+ Add learner emails to get started.
+ >
+);
+
+export default AssignmentModalSummaryEmptyState;
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx
new file mode 100644
index 0000000000..fdad32369a
--- /dev/null
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx
@@ -0,0 +1,73 @@
+import React, { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+import { v4 as uuidv4 } from 'uuid';
+import {
+ Button, Stack, Icon,
+} from '@edx/paragon';
+import { Person } from '@edx/paragon/icons';
+
+import { MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT, hasLearnerEmailsSummaryListTruncation } from './data';
+
+const AssignmentModalSummaryLearnerList = ({
+ course,
+ learnerEmails,
+}) => {
+ const [isTruncated, setIsTruncated] = useState(hasLearnerEmailsSummaryListTruncation(learnerEmails));
+ const truncatedLearnerEmails = learnerEmails.slice(0, MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT - 1);
+ const displayedLearnerEmails = isTruncated ? truncatedLearnerEmails : learnerEmails;
+
+ useEffect(() => {
+ setIsTruncated(hasLearnerEmailsSummaryListTruncation(learnerEmails));
+ }, [learnerEmails]);
+
+ const expandCollapseMessage = isTruncated
+ ? `Show ${learnerEmails.length - MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT} more`
+ : 'Show less';
+
+ return (
+
+
+ {displayedLearnerEmails.map((emailAddress) => (
+
+
+
+
+
+
+ {emailAddress}
+
+
+
+
+ {course.formattedPrice}
+
+
+
+ ))}
+
+ {hasLearnerEmailsSummaryListTruncation(learnerEmails) && (
+ setIsTruncated(prevState => !prevState)}
+ >
+ {expandCollapseMessage}
+
+ )}
+
+ );
+};
+
+AssignmentModalSummaryLearnerList.propTypes = {
+ course: PropTypes.shape({
+ formattedPrice: PropTypes.string.isRequired,
+ }).isRequired,
+ learnerEmails: PropTypes.arrayOf(PropTypes.string).isRequired,
+};
+
+export default AssignmentModalSummaryLearnerList;
diff --git a/src/components/learner-credit-management/cards/BaseCourseCard.jsx b/src/components/learner-credit-management/cards/BaseCourseCard.jsx
index c7a74b4339..137dd08217 100644
--- a/src/components/learner-credit-management/cards/BaseCourseCard.jsx
+++ b/src/components/learner-credit-management/cards/BaseCourseCard.jsx
@@ -32,7 +32,7 @@ const BaseCourseCard = ({
logoAlt,
title,
subtitle,
- price,
+ formattedPrice,
isExecEdCourseType,
courseEnrollmentInfo,
execEdEnrollmentInfo,
@@ -53,7 +53,7 @@ const BaseCourseCard = ({
subtitle={subtitle}
actions={(
- {price}
+ {formattedPrice}
{PRICE.subText}
)}
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index 247147b3ae..09c1bcd3c2 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -21,6 +21,7 @@ import { getButtonElement, queryClient } from '../../test/testUtils';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
+import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from './data';
jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
@@ -323,14 +324,12 @@ describe('Course card works as expected', () => {
expect(modalCourseCard.queryByText('View course', { selector: 'a' })).not.toBeInTheDocument();
expect(getButtonElement('Assign', { screenOverride: modalCourseCard, isQueryByRole: true })).not.toBeInTheDocument();
- // Verify empty state and textarea can accept emails
+ // Verify empty state
expect(assignmentModal.getByText('Assign to')).toBeInTheDocument();
const textareaInputLabel = assignmentModal.getByLabelText('Learner email addresses');
expect(textareaInputLabel).toBeInTheDocument();
const textareaInput = textareaInputLabel.closest('textarea');
expect(textareaInput).toBeInTheDocument();
- userEvent.type(textareaInput, 'hello@example.com{enter}world@example.com');
- expect(textareaInput).toHaveValue('hello@example.com\nworld@example.com');
expect(assignmentModal.getByText('To add more than one learner, enter one email address per line.')).toBeInTheDocument();
expect(assignmentModal.getByText('Pay by Learner Credit')).toBeInTheDocument();
expect(assignmentModal.getByText('Summary')).toBeInTheDocument();
@@ -364,6 +363,28 @@ describe('Course card works as expected', () => {
expect(submitAssignmentCTA).toBeInTheDocument();
if (shouldSubmitAssignments) {
+ // Verify textarea receives input
+ userEvent.type(textareaInput, mockLearnerEmails.join('{enter}'));
+ expect(textareaInput).toHaveValue(mockLearnerEmails.join('\n'));
+
+ // Verify assignment summary UI updates
+ await waitFor(() => {
+ expect(assignmentModal.getByText(`Summary (${mockLearnerEmails.length})`)).toBeInTheDocument();
+ }, { timeout: EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY + 1000 });
+ expect(assignmentModal.queryByText('You haven\'t entered any learners yet.')).not.toBeInTheDocument();
+ expect(assignmentModal.queryByText('Add learner emails to get started.')).not.toBeInTheDocument();
+ mockLearnerEmails.forEach((learnerEmail) => {
+ expect(assignmentModal.getByText(learnerEmail)).toBeInTheDocument();
+ });
+ expect(assignmentModal.getByText('Total assignment cost')).toBeInTheDocument();
+ const expectedAssignmentCost = mockLearnerEmails.length * defaultProps.original.normalized_metadata.content_price;
+ expect(assignmentModal.getByText(formatPrice(expectedAssignmentCost))).toBeInTheDocument();
+ expect(assignmentModal.getByText('Remaining after assignment')).toBeInTheDocument();
+ const expectedBalanceAfterAssignment = (
+ mockSubsidyAccessPolicy.aggregates.spendAvailableUsd - expectedAssignmentCost
+ );
+ expect(assignmentModal.getByText(formatPrice(expectedBalanceAfterAssignment))).toBeInTheDocument();
+
// Verify assignment is submitted successfully
userEvent.click(submitAssignmentCTA);
await waitFor(() => expect(mockAllocateContentAssignments).toHaveBeenCalledTimes(1));
diff --git a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
index dc5c92ebe6..6bec89834a 100644
--- a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
+++ b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
@@ -1,9 +1,9 @@
import React, { useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useToggle } from '@edx/paragon';
-import SystemErrorAlertModal from './status-modals/SystemErrorAlertModal';
-import ContentNotInCatalogErrorAlertModal from './status-modals/ContentNotInCatalogErrorAlertModal';
-import NotEnoughBalanceAlertModal from './status-modals/NotEnoughBalanceAlertModal';
+import SystemErrorAlertModal from './assignment-allocation-status-modals/SystemErrorAlertModal';
+import ContentNotInCatalogErrorAlertModal from './assignment-allocation-status-modals/ContentNotInCatalogErrorAlertModal';
+import NotEnoughBalanceAlertModal from './assignment-allocation-status-modals/NotEnoughBalanceAlertModal';
const CreateAllocationErrorAlertModals = ({
errorReason,
diff --git a/src/components/learner-credit-management/cards/status-modals/ContentNotInCatalogErrorAlertModal.jsx b/src/components/learner-credit-management/cards/assignment-allocation-status-modals/ContentNotInCatalogErrorAlertModal.jsx
similarity index 100%
rename from src/components/learner-credit-management/cards/status-modals/ContentNotInCatalogErrorAlertModal.jsx
rename to src/components/learner-credit-management/cards/assignment-allocation-status-modals/ContentNotInCatalogErrorAlertModal.jsx
diff --git a/src/components/learner-credit-management/cards/status-modals/NotEnoughBalanceAlertModal.jsx b/src/components/learner-credit-management/cards/assignment-allocation-status-modals/NotEnoughBalanceAlertModal.jsx
similarity index 100%
rename from src/components/learner-credit-management/cards/status-modals/NotEnoughBalanceAlertModal.jsx
rename to src/components/learner-credit-management/cards/assignment-allocation-status-modals/NotEnoughBalanceAlertModal.jsx
diff --git a/src/components/learner-credit-management/cards/status-modals/SystemErrorAlertModal.jsx b/src/components/learner-credit-management/cards/assignment-allocation-status-modals/SystemErrorAlertModal.jsx
similarity index 100%
rename from src/components/learner-credit-management/cards/status-modals/SystemErrorAlertModal.jsx
rename to src/components/learner-credit-management/cards/assignment-allocation-status-modals/SystemErrorAlertModal.jsx
diff --git a/src/components/learner-credit-management/cards/data/constants.js b/src/components/learner-credit-management/cards/data/constants.js
index 01af1a5313..4080ee7c98 100644
--- a/src/components/learner-credit-management/cards/data/constants.js
+++ b/src/components/learner-credit-management/cards/data/constants.js
@@ -1,5 +1,3 @@
-/* eslint-disable import/prefer-default-export */
-
import PropTypes from 'prop-types';
export const commonErrorAlertModalPropTypes = {
@@ -7,3 +5,7 @@ export const commonErrorAlertModalPropTypes = {
closeErrorModal: PropTypes.func.isRequired,
closeAssignmentModal: PropTypes.func.isRequired,
};
+
+export const MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT = 15;
+
+export const EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY = 1000;
diff --git a/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js b/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js
index cfb4312e20..b49ece6cf4 100644
--- a/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js
+++ b/src/components/learner-credit-management/cards/data/useCourseCardMetadata.js
@@ -26,7 +26,7 @@ const useCourseCardMetadata = ({
partners,
title,
} = course;
- const price = (normalizedMetadata.contentPrice || normalizedMetadata.contentPrice === 0) ? formatPrice(normalizedMetadata.contentPrice) : 'N/A';
+ const formattedPrice = (normalizedMetadata.contentPrice || normalizedMetadata.contentPrice === 0) ? formatPrice(normalizedMetadata.contentPrice) : 'N/A';
const imageSrc = cardImageUrl || cardFallbackImg;
let logoSrc;
@@ -57,7 +57,7 @@ const useCourseCardMetadata = ({
return {
...course,
subtitle: partners.map(partner => partner.name).join(', '),
- price,
+ formattedPrice,
imageSrc,
altText,
logoSrc,
diff --git a/src/components/learner-credit-management/cards/data/utils.js b/src/components/learner-credit-management/cards/data/utils.js
index aa27d5fe37..61bb225a60 100644
--- a/src/components/learner-credit-management/cards/data/utils.js
+++ b/src/components/learner-credit-management/cards/data/utils.js
@@ -1,4 +1,4 @@
-/* eslint-disable import/prefer-default-export */
+import { MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT } from './constants';
export const getBudgetDisplayName = (subsidyAccessPolicy) => {
let budgetDisplayName = 'budget';
@@ -7,3 +7,7 @@ export const getBudgetDisplayName = (subsidyAccessPolicy) => {
}
return budgetDisplayName;
};
+
+export const hasLearnerEmailsSummaryListTruncation = (learnerEmails) => (
+ learnerEmails.length > MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT
+);
From ec348a69a1c963944f44816747ce2fc7917fa190 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Wed, 15 Nov 2023 14:44:41 -0500
Subject: [PATCH 069/124] feat: input validation on assignment modal (#1090)
---
.../cards/AssignmentModalContent.jsx | 47 ++++--
.../cards/AssignmentModalSummary.jsx | 96 +++++++++---
.../AssignmentModalSummaryEmptyState.jsx | 4 +-
.../AssignmentModalSummaryErrorState.jsx | 15 ++
.../AssignmentModalSummaryLearnerList.jsx | 3 +-
.../cards/CourseCard.test.jsx | 139 +++++++++++++++---
.../cards/NewAssignmentModalButton.jsx | 17 ++-
.../cards/data/utils.js | 78 ++++++++++
.../styles/index.scss | 8 +
9 files changed, 342 insertions(+), 65 deletions(-)
create mode 100644 src/components/learner-credit-management/cards/AssignmentModalSummaryErrorState.jsx
diff --git a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
index b85e2ba6d5..0dab3dc809 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
@@ -16,14 +16,18 @@ import BaseCourseCard from './BaseCourseCard';
import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../data';
import { ImpactOnYourLearnerCreditBudget, ManagingThisAssignment, NextStepsForAssignedLearners } from './Collapsibles';
import AssignmentModalSummary from './AssignmentModalSummary';
-import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from './data';
+import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY, isEmailAddressesInputValueValid } from './data';
const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const spendAvailable = subsidyAccessPolicy.aggregates.spendAvailableUsd;
const [learnerEmails, setLearnerEmails] = useState([]);
const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
+ const [assignmentAllocationMetadata, setAssignmentAllocationMetadata] = useState({});
+
+ const { contentPrice } = course.normalizedMetadata;
const handleEmailAddressInputChange = (e) => {
const inputValue = e.target.value;
@@ -34,10 +38,10 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
if (!value) {
setLearnerEmails([]);
onEmailAddressesChange([]);
+ return;
}
const emails = value.split('\n').filter((email) => email.trim().length > 0);
setLearnerEmails(emails);
- onEmailAddressesChange(emails);
}, [onEmailAddressesChange]);
const debouncedHandleEmailAddressesChanged = useMemo(
@@ -49,10 +53,20 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
debouncedHandleEmailAddressesChanged(emailAddressesInputValue);
}, [emailAddressesInputValue, debouncedHandleEmailAddressesChanged]);
- const hasLearnerEmails = learnerEmails.length > 0;
- const spendAvailable = subsidyAccessPolicy.aggregates.spendAvailableUsd;
- const costToAssignLearners = learnerEmails.length * course.normalizedMetadata.contentPrice;
- const remainingBalanceAfterAssignment = spendAvailable - costToAssignLearners;
+ // Validate the learner emails emails from user input whenever it changes
+ useEffect(() => {
+ const allocationMetadata = isEmailAddressesInputValueValid({
+ learnerEmails,
+ remainingBalance: spendAvailable,
+ contentPrice,
+ });
+ setAssignmentAllocationMetadata(allocationMetadata);
+ if (allocationMetadata.canAllocate) {
+ onEmailAddressesChange(learnerEmails, { canAllocate: true });
+ } else {
+ onEmailAddressesChange([]);
+ }
+ }, [onEmailAddressesChange, learnerEmails, contentPrice, spendAvailable]);
return (
@@ -75,9 +89,15 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
rows={10}
data-hj-suppress
/>
-
- To add more than one learner, enter one email address per line.
-
+ {assignmentAllocationMetadata.validationError ? (
+
+ {assignmentAllocationMetadata.validationError.message}
+
+ ) : (
+
+ To add more than one learner, enter one email address per line.
+
+ )}
How assigning this course works
@@ -91,6 +111,7 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
@@ -104,20 +125,20 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
Available balance
{formatPrice(spendAvailable)}
- {hasLearnerEmails && (
+ {assignmentAllocationMetadata.canAllocate && (
Total cost
- -{formatPrice(costToAssignLearners)}
+ -{formatPrice(assignmentAllocationMetadata.totalAssignmentCost)}
)}
- {hasLearnerEmails && (
+ {assignmentAllocationMetadata.canAllocate && (
Remaining after assignment
- {formatPrice(remainingBalanceAfterAssignment)}
+ {formatPrice(assignmentAllocationMetadata.remainingBalanceAfterAssignment)}
)}
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx
index 3baa98da0f..681428bd53 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummary.jsx
@@ -1,18 +1,46 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Card, Stack } from '@edx/paragon';
+import classNames from 'classnames';
+import { Card, Stack, Icon } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
import { formatPrice } from '../data';
import AssignmentModalSummaryEmptyState from './AssignmentModalSummaryEmptyState';
import AssignmentModalSummaryLearnerList from './AssignmentModalSummaryLearnerList';
+import AssignmentModalSummaryErrorState from './AssignmentModalSummaryErrorState';
+
+const AssignmentModalSummaryContents = ({
+ hasLearnerEmails,
+ learnerEmails,
+ course,
+ hasInputValidationError,
+}) => {
+ if (hasLearnerEmails) {
+ return (
+
+ );
+ }
+ if (hasInputValidationError) {
+ return ;
+ }
+ return ;
+};
const AssignmentModalSummary = ({
course,
learnerEmails,
+ assignmentAllocationMetadata,
}) => {
- const learnerEmailsCount = learnerEmails.length;
- const hasLearnerEmails = learnerEmailsCount > 0;
- const totalAssignmentCost = learnerEmailsCount * course.normalizedMetadata.contentPrice;
+ const {
+ isValidInput,
+ learnerEmailsCount,
+ totalAssignmentCost,
+ hasEnoughBalanceForAssigment,
+ } = assignmentAllocationMetadata;
+ const hasLearnerEmails = learnerEmailsCount > 0 && isValidInput;
let summaryHeading = 'Summary';
if (hasLearnerEmails) {
@@ -22,23 +50,36 @@ const AssignmentModalSummary = ({
<>
{summaryHeading}
-
-
- {hasLearnerEmails ? (
-
- ) : (
-
- )}
+
+
+
{hasLearnerEmails && (
-
-
- Total assignment cost
- {formatPrice(totalAssignmentCost)}
+
+
+
+ {!hasEnoughBalanceForAssigment && }
+
+ Total assignment cost
+ {formatPrice(totalAssignmentCost)}
+
+
)}
@@ -47,13 +88,22 @@ const AssignmentModalSummary = ({
);
};
+AssignmentModalSummaryContents.propTypes = {
+ hasLearnerEmails: PropTypes.bool.isRequired,
+ learnerEmails: PropTypes.arrayOf(PropTypes.string).isRequired,
+ course: PropTypes.shape().isRequired, // pass-thru prop to child component(s)
+ hasInputValidationError: PropTypes.bool.isRequired,
+};
+
AssignmentModalSummary.propTypes = {
- course: PropTypes.shape({
- normalizedMetadata: PropTypes.shape({
- contentPrice: PropTypes.number.isRequired,
- }).isRequired,
- }).isRequired,
+ course: PropTypes.shape().isRequired, // pass-thru prop to child component(s)
learnerEmails: PropTypes.arrayOf(PropTypes.string).isRequired,
+ assignmentAllocationMetadata: PropTypes.shape({
+ isValidInput: PropTypes.bool,
+ learnerEmailsCount: PropTypes.number,
+ totalAssignmentCost: PropTypes.number,
+ hasEnoughBalanceForAssigment: PropTypes.bool,
+ }).isRequired,
};
export default AssignmentModalSummary;
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx
index 694c046f23..dc8d4f1be3 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummaryEmptyState.jsx
@@ -2,8 +2,8 @@ import React from 'react';
const AssignmentModalSummaryEmptyState = () => (
<>
- You haven't entered any learners yet.
- Add learner emails to get started.
+ You haven't entered any learners yet.
+ Add learner emails to get started.
>
);
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummaryErrorState.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummaryErrorState.jsx
new file mode 100644
index 0000000000..392dcccce3
--- /dev/null
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummaryErrorState.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { Stack, Icon } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+
+const AssignmentModalSummaryErrorState = () => (
+
+
+
+
Learners can't be assigned as entered.
+
Please check your learner emails and try again.
+
.
+
+);
+
+export default AssignmentModalSummaryErrorState;
diff --git a/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx b/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx
index fdad32369a..7719678d30 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalSummaryLearnerList.jsx
@@ -30,13 +30,14 @@ const AssignmentModalSummaryLearnerList = ({
{displayedLearnerEmails.map((emailAddress) => (
-
+
{emailAddress}
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index 09c1bcd3c2..d522cb2ad6 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -160,11 +160,11 @@ describe('Course card works as expected', () => {
};
beforeEach(() => {
+ useBudgetId.mockReturnValue({ subsidyAccessPolicyId: mockSubsidyAccessPolicy.uuid });
useSubsidyAccessPolicy.mockReturnValue({
data: mockSubsidyAccessPolicy,
isLoading: false,
});
- useBudgetId.mockReturnValue({ subsidyAccessPolicyId: mockSubsidyAccessPolicy.uuid });
});
afterEach(() => {
@@ -220,58 +220,59 @@ describe('Course card works as expected', () => {
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: 'content_not_in_catalog',
+ allocationExceptionReason: 'content_not_in_catalog',
+ shouldRetryAllocationAfterException: false, // no ability to retry after this error
},
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: 'not_enough_value_in_subsidy',
- shouldRetryAfterError: false,
+ allocationExceptionReason: 'not_enough_value_in_subsidy',
+ shouldRetryAllocationAfterException: false,
},
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: 'not_enough_value_in_subsidy',
- shouldRetryAfterError: true,
+ allocationExceptionReason: 'not_enough_value_in_subsidy',
+ shouldRetryAllocationAfterException: true,
},
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: 'policy_spend_limit_reached',
- shouldRetryAfterError: false,
+ allocationExceptionReason: 'policy_spend_limit_reached',
+ shouldRetryAllocationAfterException: false,
},
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: 'policy_spend_limit_reached',
- shouldRetryAfterError: true,
+ allocationExceptionReason: 'policy_spend_limit_reached',
+ shouldRetryAllocationAfterException: true,
},
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: null,
- shouldRetryAfterError: false,
+ allocationExceptionReason: null,
+ shouldRetryAllocationAfterException: false,
},
{
shouldSubmitAssignments: true,
hasAllocationException: true,
- errorReason: null,
- shouldRetryAfterError: true,
+ allocationExceptionReason: null,
+ shouldRetryAllocationAfterException: true,
},
{ shouldSubmitAssignments: true, hasAllocationException: false },
{ shouldSubmitAssignments: false, hasAllocationException: false },
- ])('opens assignment modal, submits assignments successfully (%s)', async ({
+ ])('opens assignment modal, fills out information, and submits assignments accordingly - with success or with an exception (%s)', async ({
shouldSubmitAssignments,
hasAllocationException,
- errorReason,
- shouldRetryAfterError,
+ allocationExceptionReason,
+ shouldRetryAllocationAfterException,
}) => {
if (hasAllocationException) {
// mock Axios error
mockAllocateContentAssignments.mockRejectedValue({
customAttributes: {
- httpErrorStatus: errorReason ? 422 : 500,
- httpErrorResponseData: JSON.stringify([{ reason: errorReason }]),
+ httpErrorStatus: allocationExceptionReason ? 422 : 500,
+ httpErrorResponseData: JSON.stringify([{ reason: allocationExceptionReason }]),
},
});
} else {
@@ -295,7 +296,6 @@ describe('Course card works as expected', () => {
},
});
}
- useBudgetId.mockReturnValue({ subsidyAccessPolicyId: mockSubsidyAccessPolicy.uuid });
const mockInvalidateQueries = jest.fn();
useQueryClient.mockReturnValue({
invalidateQueries: mockInvalidateQueries,
@@ -402,7 +402,7 @@ describe('Course card works as expected', () => {
expect(getButtonElement('Try again', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'false');
// Assert the correct error modal is displayed
- if (errorReason === 'content_not_in_catalog') {
+ if (allocationExceptionReason === 'content_not_in_catalog') {
const assignmentErrorModal = getAssignmentErrorModal();
expect(assignmentErrorModal.getByText(`This course is not in your ${mockSubsidyAccessPolicy.displayName} budget's catalog`)).toBeInTheDocument();
const exitCTA = getButtonElement('Exit', { screenOverride: assignmentErrorModal });
@@ -411,11 +411,11 @@ describe('Course card works as expected', () => {
// Verify all modals close (error modal + assignment modal)
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
- } else if (['not_enough_value_in_subsidy', 'policy_spend_limit_reached'].includes(errorReason)) {
+ } else if (['not_enough_value_in_subsidy', 'policy_spend_limit_reached'].includes(allocationExceptionReason)) {
const assignmentErrorModal = getAssignmentErrorModal();
const errorModalTitle = 'Not enough balance';
expect(assignmentErrorModal.getByText(errorModalTitle)).toBeInTheDocument();
- if (shouldRetryAfterError) {
+ if (shouldRetryAllocationAfterException) {
await simulateClickErrorModalTryAgain(errorModalTitle, assignmentErrorModal);
} else {
await simulateClickErrorModalExit(assignmentErrorModal);
@@ -424,7 +424,7 @@ describe('Course card works as expected', () => {
const assignmentErrorModal = getAssignmentErrorModal();
const errorModalTitle = 'Something went wrong';
expect(assignmentErrorModal.getByText(errorModalTitle)).toBeInTheDocument();
- if (shouldRetryAfterError) {
+ if (shouldRetryAllocationAfterException) {
await simulateClickErrorModalTryAgain(errorModalTitle, assignmentErrorModal);
} else {
await simulateClickErrorModalExit(assignmentErrorModal);
@@ -454,4 +454,95 @@ describe('Course card works as expected', () => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
}
});
+
+ it.each([
+ {
+ learnerEmails: ['a@a.com', 'b@bcom', 'c@c.com'],
+ spendAvailableUsd: 1000,
+ expectedValidationMessage: 'b@bcom is not a valid email.',
+ },
+ {
+ learnerEmails: ['a@a.com', 'b@b.com', 'c@c.com', 'b@b.com'],
+ spendAvailableUsd: 1000,
+ expectedValidationMessage: 'b@b.com has been entered more than once.',
+ },
+ {
+ learnerEmails: ['a@a.com', 'b@bcom', 'c@c.com', 'a@a.com'],
+ spendAvailableUsd: 1000,
+ expectedValidationMessage: 'b@bcom is not a valid email.',
+ },
+ {
+ learnerEmails: ['a@a.com', 'b@b.com', 'c@c.com'],
+ spendAvailableUsd: 100, // assignment allocation will exceed available spend
+ expectedValidationMessage: 'The total assignment cost exceeds your available Learner Credit budget balance of $100. Please remove learners and try again.',
+ },
+ {
+ learnerEmails: ['a@a.com', 'b@b.com', 'c@c.com'],
+ spendAvailableUsd: 1000,
+ expectedValidationMessage: undefined, // no validation error
+ },
+ ])('opens assignment modal, fills out information, and handles client-side validation (%s)', async ({
+ learnerEmails,
+ spendAvailableUsd,
+ expectedValidationMessage,
+ }) => {
+ useSubsidyAccessPolicy.mockReturnValue({
+ data: {
+ ...mockSubsidyAccessPolicy,
+ aggregates: {
+ ...mockSubsidyAccessPolicy.aggregates,
+ spendAvailableUsd,
+ },
+ },
+ isLoading: false,
+ });
+
+ renderWithRouter( );
+ const assignCourseCTA = getButtonElement('Assign');
+ expect(assignCourseCTA).toBeInTheDocument();
+ userEvent.click(assignCourseCTA);
+
+ const assignmentModal = within(screen.getByRole('dialog'));
+
+ // Verify "Assign" CTA is disabled
+ expect(getButtonElement('Assign', { screenOverride: assignmentModal })).toBeDisabled();
+
+ // Verify textarea receives input
+ const textareaInputLabel = assignmentModal.getByLabelText('Learner email addresses');
+ expect(textareaInputLabel).toBeInTheDocument();
+ const textareaInput = textareaInputLabel.closest('textarea');
+ expect(textareaInput).toBeInTheDocument();
+ userEvent.type(textareaInput, learnerEmails.join('{enter}'));
+ expect(textareaInput).toHaveValue(learnerEmails.join('\n'));
+
+ await waitFor(() => {
+ if (expectedValidationMessage) {
+ expect(assignmentModal.getByText(expectedValidationMessage)).toBeInTheDocument();
+
+ // Verify assigment modal summary contents handle the input validation errors, based on whether
+ // the validation error relates to the email addresses entered or having sufficient available spend.
+ const assignmentAllocationCost = learnerEmails.length * originalData.normalized_metadata.content_price;
+ if (assignmentAllocationCost <= spendAvailableUsd) {
+ const assignmentSummaryCard = assignmentModal.getByText('Learners can\'t be assigned as entered.').closest('.assignment-modal-summary-card');
+ expect(assignmentSummaryCard).toBeInTheDocument();
+ expect(assignmentSummaryCard).toHaveClass('invalid');
+ expect(assignmentModal.getByText('Please check your learner emails and try again.')).toBeInTheDocument();
+ expect(assignmentModal.queryByText('You haven\'t entered any learners yet.')).not.toBeInTheDocument();
+ expect(assignmentModal.queryByText('Add learner emails to get started.')).not.toBeInTheDocument();
+ expect(assignmentModal.queryByText('Total assignment cost')).not.toBeInTheDocument();
+ } else {
+ const totalAssignmentCostCard = assignmentModal.getByText('Total assignment cost').closest('.assignment-modal-total-assignment-cost-card');
+ expect(totalAssignmentCostCard).toBeInTheDocument();
+ expect(totalAssignmentCostCard).toHaveClass('invalid');
+ }
+ expect(assignmentModal.queryByText('Remaining after assignment')).not.toBeInTheDocument();
+
+ // Verify "Assign" CTA is still disabled
+ expect(getButtonElement('Assign', { screenOverride: assignmentModal })).toBeDisabled();
+ } else {
+ // Verify "Assign" CTA is enabled
+ expect(getButtonElement('Assign', { screenOverride: assignmentModal })).not.toBeDisabled();
+ }
+ }, { timeout: EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY + 1000 });
+ });
});
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index fccc05b501..2dee3ff00a 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -1,4 +1,4 @@
-import React, { useContext, useState } from 'react';
+import React, { useCallback, useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useRouteMatch, useHistory, generatePath } from 'react-router-dom';
import {
@@ -32,6 +32,7 @@ const NewAssignmentModalButton = ({ course, children }) => {
const { subsidyAccessPolicyId } = useBudgetId();
const [isOpen, open, close] = useToggle(false);
const [learnerEmails, setLearnerEmails] = useState([]);
+ const [canAllocateAssignments, setCanAllocateAssignments] = useState(false);
const [assignButtonState, setAssignButtonState] = useState('default');
const [createAssignmentsErrorReason, setCreateAssignmentsErrorReason] = useState();
const { displayToastForAssignmentAllocation } = useContext(BudgetDetailPageContext);
@@ -45,6 +46,17 @@ const NewAssignmentModalButton = ({ course, children }) => {
setAssignButtonState('default');
};
+ // Callback function for when emails are changed in the
+ // child AssignmentModalContent component. Must be memoized as
+ // the function is used within a `useEffect`'s dependency array.
+ const handleEmailAddressesChanged = useCallback((
+ value,
+ { canAllocate = false } = {},
+ ) => {
+ setLearnerEmails(value);
+ setCanAllocateAssignments(canAllocate);
+ }, []);
+
const handleAllocateContentAssignments = () => {
const payload = snakeCaseObject({
contentPriceCents: course.normalizedMetadata.contentPrice * 100, // Convert to USD cents
@@ -107,6 +119,7 @@ const NewAssignmentModalButton = ({ course, children }) => {
}}
variant="primary"
state={assignButtonState}
+ disabled={!canAllocateAssignments}
onClick={handleAllocateContentAssignments}
/>
@@ -114,7 +127,7 @@ const NewAssignmentModalButton = ({ course, children }) => {
>
{
let budgetDisplayName = 'budget';
if (subsidyAccessPolicy.displayName) {
@@ -8,6 +16,76 @@ export const getBudgetDisplayName = (subsidyAccessPolicy) => {
return budgetDisplayName;
};
+/**
+ * Determine whether the number of learner emails exceeds a certain
+ * threshold, whereby the list of emails should be truncated.
+ * @param {Array} learnerEmails List of learner emails.
+ * @returns True is learner emails list should be truncated; otherwise, false.
+ */
export const hasLearnerEmailsSummaryListTruncation = (learnerEmails) => (
learnerEmails.length > MAX_INITIAL_LEARNER_EMAILS_DISPLAYED_COUNT
);
+
+/**
+ * Determine the validity of the learner emails user input. The input is valid if
+ * all emails are valid and there are no duplicates. Invalid emails and duplicate
+ * emails are returned.
+ *
+ * @param {Object} args Arguments.
+ * @param {Array} learnerEmails List of learner emails.
+ * @param {Number} contentPrice Price of the content (USD).
+ * @param {Array} remainingBalance Amount remaining in the budget (USD).
+ *
+ * @returns Object containing various properties about the validity of the learner emails
+ * input, including a validation error when appropriate, and whether the assignment allocation
+ * should proceed.
+ */
+export const isEmailAddressesInputValueValid = ({
+ learnerEmails,
+ remainingBalance,
+ contentPrice,
+}) => {
+ let validationError;
+
+ const learnerEmailsCount = learnerEmails.length;
+ const totalAssignmentCost = contentPrice * learnerEmailsCount;
+ const remainingBalanceAfterAssignment = remainingBalance - totalAssignmentCost;
+ const hasEnoughBalanceForAssigment = remainingBalanceAfterAssignment >= 0;
+
+ const invalidEmails = learnerEmails.filter((email) => !isEmail(email));
+ const duplicateEmails = learnerEmails.filter((email, index) => learnerEmails.indexOf(email) !== index);
+
+ const isValidInput = invalidEmails.length === 0 && duplicateEmails.length === 0;
+ const canAllocate = learnerEmailsCount > 0 && hasEnoughBalanceForAssigment && isValidInput;
+
+ const ensureValidationErrorObjectExists = () => {
+ if (!validationError) {
+ validationError = {};
+ }
+ };
+
+ if (!isValidInput) {
+ ensureValidationErrorObjectExists();
+ if (invalidEmails.length > 0) {
+ validationError.reason = 'invalid_email';
+ validationError.message = `${invalidEmails[0]} is not a valid email.`;
+ } else if (duplicateEmails.length > 0) {
+ validationError.reason = 'duplicate_email';
+ validationError.message = `${duplicateEmails[0]} has been entered more than once.`;
+ }
+ } else if (!hasEnoughBalanceForAssigment) {
+ ensureValidationErrorObjectExists();
+ validationError.reason = 'insufficient_funds';
+ validationError.message = `The total assignment cost exceeds your available Learner Credit budget balance of ${formatPrice(remainingBalance)}. Please remove learners and try again.`;
+ }
+
+ return {
+ canAllocate,
+ learnerEmailsCount,
+ isValidInput,
+ validationError,
+ totalAssignmentCost,
+ remainingBalanceAfterAssignment,
+ hasEnoughBalanceForAssigment,
+ };
+};
diff --git a/src/components/learner-credit-management/styles/index.scss b/src/components/learner-credit-management/styles/index.scss
index 3db792b691..be434a5adb 100644
--- a/src/components/learner-credit-management/styles/index.scss
+++ b/src/components/learner-credit-management/styles/index.scss
@@ -29,3 +29,11 @@
.assignment-modal-collapsible-trigger {
text-decoration: underline;
}
+
+.assignment-modal-summary-card,
+.assignment-modal-total-assignment-cost-card {
+ &.invalid {
+ background-color: $danger-100;
+ border: 1px solid $danger;
+ }
+}
From 5702148e3f0131d55c93be3b1d160ae6b9651d8a Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Tue, 14 Nov 2023 16:34:35 +0000
Subject: [PATCH 070/124] feat: network error handling for sso orchestrator
self service
---
.../SettingsSSOTab/NewExistingSSOConfigs.jsx | 44 +++++++--
.../SettingsSSOTab/NewSSOConfigCard.jsx | 13 ++-
.../settings/SettingsSSOTab/NewSSOStepper.jsx | 33 ++++---
.../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 4 +-
.../settings/SettingsSSOTab/SsoErrorPage.jsx | 70 ++++++++++++++
.../settings/SettingsSSOTab/index.jsx | 71 +++++++-------
.../tests/NewExistingSSOConfigs.test.jsx | 96 +++++++++++++++++++
.../tests/SettingsSSOTab.test.jsx | 19 ++++
src/components/settings/data/constants.js | 3 +
src/data/images/SomethingWentWrong.svg | 19 ++++
10 files changed, 315 insertions(+), 57 deletions(-)
create mode 100644 src/components/settings/SettingsSSOTab/SsoErrorPage.jsx
create mode 100644 src/data/images/SomethingWentWrong.svg
diff --git a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
index 9ac1d82113..638f06f7a4 100644
--- a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
+++ b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
@@ -1,9 +1,11 @@
import _ from 'lodash';
import {
+ Alert,
CardGrid,
Skeleton,
useToggle,
} from '@edx/paragon';
+import { Info } from '@edx/paragon/icons';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
@@ -17,7 +19,7 @@ const FRESH_CONFIG_POLLING_INTERVAL = 30000;
const UPDATED_CONFIG_POLLING_INTERVAL = 2000;
const NewExistingSSOConfigs = ({
- configs, refreshBool, setRefreshBool, enterpriseId,
+ configs, refreshBool, setRefreshBool, enterpriseId, setPollingNetworkError,
}) => {
const [inactiveConfigs, setInactiveConfigs] = useState([]);
const [activeConfigs, setActiveConfigs] = useState([]);
@@ -30,6 +32,7 @@ const NewExistingSSOConfigs = ({
const [intervalMs, setIntervalMs] = React.useState(FRESH_CONFIG_POLLING_INTERVAL);
const [loading, setLoading] = useState(false);
const [showAlerts, openAlerts, closeAlerts] = useToggle(false);
+ const [updateError, setUpdateError] = useState(null);
const queryClient = useQueryClient();
@@ -50,13 +53,32 @@ const NewExistingSSOConfigs = ({
}}
>
{configList.map((config) => (
-
+
+
+ {updateError?.config === config.uuid && (
+
+
(setUpdateError(null))}
+ >
+ Something went wrong behind the scenes
+
+ We were unable to {updateError?.action} your SSO configuration due to an internal error. Please
+ {' '}try again in a couple of minutes. If the problem persists, contact enterprise customer
+ {' '}support.
+
+
+
+ )}
+
))}
@@ -106,7 +128,10 @@ const NewExistingSSOConfigs = ({
useQuery({
queryKey: ['ssoOrchestratorConfigPoll'],
queryFn: async () => {
- const res = await LmsApiService.listEnterpriseSsoOrchestrationRecords(enterpriseId);
+ const res = await LmsApiService.listEnterpriseSsoOrchestrationRecords(enterpriseId).catch(() => {
+ setPollingNetworkError(true);
+ return { data: [] };
+ });
const inProgress = res.data.filter(
config => (config.submitted_at && !config.configured_at) || (config.configured_at < config.submitted_at),
);
@@ -171,6 +196,7 @@ NewExistingSSOConfigs.propTypes = {
refreshBool: PropTypes.bool.isRequired,
setRefreshBool: PropTypes.func.isRequired,
enterpriseId: PropTypes.string.isRequired,
+ setPollingNetworkError: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
index e7f94d0239..984162305b 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
@@ -14,6 +14,7 @@ const NewSSOConfigCard = ({
setLoading,
setRefreshBool,
refreshBool,
+ setUpdateError,
}) => {
const VALIDATED = config.validated_at;
const ENABLED = config.active;
@@ -32,6 +33,9 @@ const NewSSOConfigCard = ({
setLoading(true);
LmsApiService.deleteEnterpriseSsoOrchestrationRecord(deletedConfig.uuid).then(() => {
setRefreshBool(!refreshBool);
+ }).catch(() => {
+ setUpdateError({ config: config.uuid, action: 'delete' });
+ setLoading(false);
});
};
@@ -39,6 +43,9 @@ const NewSSOConfigCard = ({
setLoading(true);
LmsApiService.updateEnterpriseSsoOrchestrationRecord({ active: false }, disabledConfig.uuid).then(() => {
setRefreshBool(!refreshBool);
+ }).catch(() => {
+ setUpdateError({ config: config.uuid, action: 'disable' });
+ setLoading(false);
});
};
@@ -46,6 +53,9 @@ const NewSSOConfigCard = ({
setLoading(true);
LmsApiService.updateEnterpriseSsoOrchestrationRecord({ active: true }, enabledConfig.uuid).then(() => {
setRefreshBool(!refreshBool);
+ }).catch(() => {
+ setUpdateError({ config: config.uuid, action: 'enable' });
+ setLoading(false);
});
};
@@ -81,7 +91,7 @@ const NewSSOConfigCard = ({
{renderKeyOffIcon('existing-sso-config-card-off-not-validated-icon')}
)}
- {(!ENABLED || !CONFIGURED) && (
+ {(!ENABLED || !CONFIGURED) && VALIDATED && (
<>
{renderKeyOffIcon('existing-sso-config-card-off-icon')}
>
@@ -213,6 +223,7 @@ NewSSOConfigCard.propTypes = {
setLoading: PropTypes.func.isRequired,
setRefreshBool: PropTypes.func.isRequired,
refreshBool: PropTypes.bool.isRequired,
+ setUpdateError: PropTypes.func.isRequired,
};
export default NewSSOConfigCard;
diff --git a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
index 80f23b4d69..1e928fb8d4 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
@@ -6,6 +6,7 @@ import { connect } from 'react-redux';
import FormContextWrapper from '../../forms/FormContextWrapper';
import { SSOConfigContext } from './SSOConfigContext';
import SSOFormWorkflowConfig from './SSOFormWorkflowConfig';
+import SsoErrorPage from './SsoErrorPage';
import { camelCaseDict } from '../../../utils';
import UnsavedSSOChangesModal from './UnsavedSSOChangesModal';
import { IDP_URL_SELECTION, IDP_XML_SELECTION } from './steps/NewSSOConfigConnectStep';
@@ -16,6 +17,7 @@ const NewSSOStepper = ({ enterpriseId }) => {
} = useContext(SSOConfigContext);
const providerConfigCamelCase = camelCaseDict(providerConfig || {});
const [isStepperOpen, setIsStepperOpen] = useState(true);
+ const [configureError, setConfigureError] = useState(null);
const handleCloseWorkflow = () => {
setProviderConfig?.(null);
setIsStepperOpen(false);
@@ -27,19 +29,24 @@ const NewSSOStepper = ({ enterpriseId }) => {
: IDP_XML_SELECTION;
}
- return (isStepperOpen
- && (
-
-
-
- )
+ return (
+ <>
+ {isStepperOpen && !configureError && (
+
+
+
+ )}
+ {isStepperOpen && configureError && (
+
+ )}
+ >
);
};
diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
index b27a8df687..2d3cd7c4a1 100644
--- a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
+++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
@@ -77,7 +77,7 @@ type SSOConfigFormControlVariables = {
type SSOConfigFormContextData = SSOConfigCamelCase & SSOConfigFormControlVariables;
-export const SSOFormWorkflowConfig = ({ enterpriseId }) => {
+export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
const placeHolderButton = (buttonName?: string) => () => ({
buttonText: buttonName || 'Next',
opensNewWindow: false,
@@ -106,6 +106,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId }) => {
updatedFormFields = updateResponse.data;
} catch (error) {
err = handleErrors(error);
+ setConfigureError(error);
}
} else {
try {
@@ -114,6 +115,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId }) => {
updatedFormFields.spMetadataUrl = createResponse.data.sp_metadata_url;
} catch (error) {
err = handleErrors(error);
+ setConfigureError(error);
}
}
if (err && errHandler) {
diff --git a/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx b/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx
new file mode 100644
index 0000000000..49450619d4
--- /dev/null
+++ b/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import {
+ Container,
+ FullscreenModal,
+ Image,
+ Stack,
+} from '@edx/paragon';
+import { configuration } from '../../../config';
+import { ssoStepperNetworkErrorText, ssoLPNetworkErrorText, HELP_CENTER_SAML_LINK } from '../data/constants';
+import cardImage from '../../../data/images/SomethingWentWrong.svg';
+
+const SsoErrorPage = ({
+ isOpen,
+ stepperError,
+}) => {
+ const stepperText = stepperError ? ssoStepperNetworkErrorText : ssoLPNetworkErrorText;
+ return (
+
{ /* FullscreenModal requires an onClose prop despite hasCloseButton is false */ }}
+ title={(
+
+ )}
+ >
+
+
+
+ We're sorry.{' '}
+ Something went wrong.
+
+
+
+
+ {stepperText}{' '}
+ Please close this window and try again in a couple of minutes. If the problem persists, contact enterprise
+ customer support.
+
+
+ Helpful link:{' '}
+ Enterprise Help Center: Single Sign-On
+
+
+
+
+ );
+};
+
+SsoErrorPage.propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ stepperError: PropTypes.bool,
+};
+
+SsoErrorPage.defaultProps = {
+ stepperError: false,
+};
+
+export default SsoErrorPage;
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index a1f5599415..14e0175e74 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -7,6 +7,7 @@ import { Add, WarningFilled } from '@edx/paragon/icons';
import { HELP_CENTER_SAML_LINK } from '../data/constants';
import { useExistingSSOConfigs, useExistingProviderData } from './hooks';
import NoSSOCard from './NoSSOCard';
+import SsoErrorPage from './SsoErrorPage';
import ExistingSSOConfigs from './ExistingSSOConfigs';
import NewExistingSSOConfigs from './NewExistingSSOConfigs';
import NewSSOConfigForm from './NewSSOConfigForm';
@@ -27,6 +28,7 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const [showNoSSOCard, setShowNoSSOCard] = useState(false);
const { AUTH0_SELF_SERVICE_INTEGRATION } = features;
const [isOpen, open, close] = useToggle(false);
+ const [pollingNetworkError, setPollingNetworkError] = useState(false);
const newConfigurationButtonOnClick = async () => {
Promise.all(existingConfigs.map(config => LmsApiService.updateEnterpriseSsoOrchestrationRecord(
@@ -115,41 +117,44 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
{(!isLoading || !pdIsLoading) && (
-
- {/* providerConfig represents the currently selected config to edit/create, if there are
- existing configs but no providerConfig then we can safely render the listings page */}
- {existingConfigs?.length > 0 && (providerConfig === null) && (
-
+ <>
+ {!error && (
+
+ {/* providerConfig represents the currently selected config to edit/create, if there are
+ existing configs but no providerConfig then we can safely render the listings page */}
+ {existingConfigs?.length > 0 && (providerConfig === null) && (
+
+ )}
+ {/* Nothing found so guide user to creation/edit form */}
+ {showNoSSOCard && (
+
+ )}
+ {/* Since we found a selected providerConfig we know we are in editing mode and can safely
+ render the create/edit form */}
+ {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && (
)}
+ {pdError && (
+
+ An error occurred loading the SAML data: {pdError?.message}
+
+ )}
+
setInfoMessage(null)}
+ show={infoMessage?.length > 0}
+ >
+ {infoMessage}
+
+
)}
- {/* Nothing found so guide user to creation/edit form */}
- {showNoSSOCard &&
}
- {/* Since we found a selected providerConfig we know we are in editing mode and can safely
- render the create/edit form */}
- {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && (
)}
- {error && (
-
- An error occurred loading the SAML configs: {error?.message}
-
+ {(error || pollingNetworkError) && (
+
)}
- {pdError && (
-
- An error occurred loading the SAML data: {pdError?.message}
-
- )}
- {infoMessage && (
-
setInfoMessage(null)}
- show={infoMessage.length > 0}
- >
- {infoMessage}
-
- )}
-
+ >
)}
{(isLoading || pdIsLoading) && }
diff --git a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
index 5ac24d2601..2a0b1950ed 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
@@ -140,6 +140,8 @@ const contextValue = {
setRefreshBool: jest.fn(),
};
+const mockSetPollingNetworkError = jest.fn();
+
const setupNewExistingSSOConfigs = (configs) => {
features.AUTH0_SELF_SERVICE_INTEGRATION = true;
return render(
@@ -152,6 +154,7 @@ const setupNewExistingSSOConfigs = (configs) => {
configs={configs}
refreshBool={false}
setRefreshBool={mockSetRefreshBool}
+ setPollingNetworkError={mockSetPollingNetworkError}
/>
@@ -166,6 +169,11 @@ describe('New Existing SSO Configs tests', () => {
jest.clearAllMocks();
});
test('checks and sets in progress configs', async () => {
+ LmsApiService.listEnterpriseSsoOrchestrationRecords.mockImplementation(() => Promise.resolve({
+ data: [
+ { submitted_at: '2022-04-12T19:51:25Z', configured_at: undefined },
+ ],
+ }));
setupNewExistingSSOConfigs(inProgressConfig);
expect(
screen.queryByText(
@@ -174,6 +182,11 @@ describe('New Existing SSO Configs tests', () => {
).toBeInTheDocument();
});
test('checks and sets not configured configs', async () => {
+ LmsApiService.listEnterpriseSsoOrchestrationRecords.mockImplementation(() => Promise.resolve({
+ data: [
+ { submitted_at: '2022-04-12T19:51:25Z', configured_at: undefined },
+ ],
+ }));
setupNewExistingSSOConfigs(notConfiguredConfig);
expect(
screen.queryByText(
@@ -220,6 +233,9 @@ describe('New Existing SSO Configs tests', () => {
expect(mockSetRefreshBool).toHaveBeenCalledTimes(2);
});
test('enabling config sets loading and renders skeleton', async () => {
+ LmsApiService.listEnterpriseSsoOrchestrationRecords.mockImplementation(() => Promise.resolve({
+ data: [{}],
+ }));
const spy = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
spy.mockImplementation(() => Promise.resolve({}));
setupNewExistingSSOConfigs(inactiveConfig);
@@ -234,4 +250,84 @@ describe('New Existing SSO Configs tests', () => {
),
).toBeInTheDocument());
});
+ test('config card enable action network error alert', async () => {
+ LmsApiService.listEnterpriseSsoOrchestrationRecords.mockImplementation(() => Promise.resolve({
+ data: [{}],
+ }));
+ const spy = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
+ spy.mockRejectedValue({});
+
+ setupNewExistingSSOConfigs(inactiveConfig);
+ const button = screen.getByTestId('existing-sso-config-card-enable-button');
+ act(() => {
+ userEvent.click(button);
+ });
+ expect(spy).toBeCalledTimes(1);
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'Something went wrong behind the scenes',
+ ),
+ ).toBeInTheDocument());
+
+ const dismissAlertButton = screen.getByText('Dismiss');
+ act(() => {
+ userEvent.click(dismissAlertButton);
+ });
+ expect(
+ screen.queryByText(
+ 'Something went wrong behind the scenes',
+ ),
+ ).not.toBeInTheDocument();
+ });
+ test('config card disable action network error alert', async () => {
+ LmsApiService.listEnterpriseSsoOrchestrationRecords.mockImplementation(() => Promise.resolve({
+ data: [{}],
+ }));
+ const spy = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
+ spy.mockRejectedValue({});
+
+ setupNewExistingSSOConfigs(activeConfig);
+ const kebobButton = screen.getByTestId('existing-sso-config-card-dropdown');
+ act(() => {
+ userEvent.click(kebobButton);
+ });
+ const button = screen.getByTestId('existing-sso-config-disable-dropdown');
+ act(() => {
+ userEvent.click(button);
+ });
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'Something went wrong behind the scenes',
+ ),
+ ).toBeInTheDocument());
+ });
+ test('config card delete action network error alert', async () => {
+ LmsApiService.listEnterpriseSsoOrchestrationRecords.mockImplementation(() => Promise.resolve({
+ data: [{}],
+ }));
+ const spy = jest.spyOn(LmsApiService, 'deleteEnterpriseSsoOrchestrationRecord');
+ spy.mockRejectedValue({});
+
+ setupNewExistingSSOConfigs(inactiveConfig);
+ const kebobButton = screen.getByTestId('existing-sso-config-card-dropdown');
+ act(() => {
+ userEvent.click(kebobButton);
+ });
+ const button = screen.getByTestId('existing-sso-config-delete-dropdown');
+ act(() => {
+ userEvent.click(button);
+ });
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'Something went wrong behind the scenes',
+ ),
+ ).toBeInTheDocument());
+ });
+ test('polling network error sets network error state', async () => {
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockRejectedValue({});
+ setupNewExistingSSOConfigs(inProgressConfig);
+ await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
+ expect(mockSetPollingNetworkError).toHaveBeenCalledTimes(1);
+ });
});
diff --git a/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx b/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx
index 852d2c292a..41d6df6d8b 100644
--- a/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/SettingsSSOTab.test.jsx
@@ -106,4 +106,23 @@ describe('SAML Config Tab', () => {
'Great news! Your test was successful and your new SSO integration is live and ready to use.',
)).toBeInTheDocument();
});
+ test('network errors trigger sso error page', async () => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = true;
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockRejectedValue({});
+ await waitFor(() => render(
+
+
+
+
+ ,
+
+ ,
+ ));
+ await waitFor(() => expect(
+ screen.getByTestId(
+ 'sso-network-error-image',
+ ),
+ ).toBeInTheDocument());
+ });
});
diff --git a/src/components/settings/data/constants.js b/src/components/settings/data/constants.js
index 117636d3df..23915bb12b 100644
--- a/src/components/settings/data/constants.js
+++ b/src/components/settings/data/constants.js
@@ -57,6 +57,9 @@ export const INVALID_ODATA_API_TIMEOUT_INTERVAL = 'OData API timeout interval mu
export const MAX_UNIVERSAL_LINKS = 100;
+export const ssoStepperNetworkErrorText = 'We were unable to configure your SSO due to an internal error.';
+export const ssoLPNetworkErrorText = 'We were unable to load your SSO details due to an internal error.';
+
/**
* Used as tab values and in router params
*/
diff --git a/src/data/images/SomethingWentWrong.svg b/src/data/images/SomethingWentWrong.svg
new file mode 100644
index 0000000000..751c4cd9cd
--- /dev/null
+++ b/src/data/images/SomethingWentWrong.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 5cc1b8d0eeafb43530fa4cfeaaacab7392339a1a Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 17 Nov 2023 09:05:20 -0500
Subject: [PATCH 071/124] feat: display refunds in spent table when backed by
transactions API (#1091)
---
.../AssignmentDetailsTableCell.jsx | 6 +--
.../LearnerCreditAllocationTable.jsx | 7 ++-
.../SpendTableAmountContents.jsx | 32 +++++++++++
.../SpendTableEnrollmentDetails.jsx | 19 +++++--
.../useBudgetDetailActivityOverview.test.jsx | 39 ++++++++++----
.../data/hooks/useOfferRedemptions.js | 13 +++--
.../data/hooks/useOfferRedemptions.test.jsx | 20 ++++---
.../learner-credit-management/data/utils.js | 54 +++++++++++++------
.../tests/BudgetDetailPage.test.jsx | 49 +++++++++++++++--
9 files changed, 180 insertions(+), 59 deletions(-)
create mode 100644 src/components/learner-credit-management/SpendTableAmountContents.jsx
diff --git a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
index b04f3b0a9b..00b20f2419 100644
--- a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import { Hyperlink } from '@edx/paragon';
+import { Stack, Hyperlink } from '@edx/paragon';
import { configuration } from '../../config';
import EmailAddressTableCell from './EmailAddressTableCell';
@@ -9,7 +9,7 @@ import EmailAddressTableCell from './EmailAddressTableCell';
const AssignmentDetailsTableCell = ({ row, enterpriseSlug }) => {
const { ENTERPRISE_LEARNER_PORTAL_URL } = configuration;
return (
- <>
+
{
{row.original.contentTitle || 'View Course'}
- >
+
);
};
diff --git a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
index 8b412b066a..c5a471326d 100644
--- a/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
+++ b/src/components/learner-credit-management/LearnerCreditAllocationTable.jsx
@@ -9,8 +9,8 @@ import {
PAGE_SIZE,
DEFAULT_PAGE,
formatDate,
- formatPrice,
} from './data';
+import SpendTableAmountContents from './SpendTableAmountContents';
const FilterStatus = (rest) => ;
@@ -45,7 +45,7 @@ const LearnerCreditAllocationTable = ({
{
Header: 'Amount',
accessor: 'courseListPrice',
- Cell: ({ row }) => formatPrice(row.values.courseListPrice),
+ Cell: SpendTableAmountContents,
disableFilters: true,
},
]}
@@ -73,10 +73,9 @@ LearnerCreditAllocationTable.propTypes = {
tableData: PropTypes.shape({
results: PropTypes.arrayOf(PropTypes.shape({
userEmail: PropTypes.string,
- courseTitle: PropTypes.string.isRequired,
+ courseTitle: PropTypes.string,
courseListPrice: PropTypes.number.isRequired,
enrollmentDate: PropTypes.string.isRequired,
- courseProductLine: PropTypes.string.isRequired,
})),
itemCount: PropTypes.number.isRequired,
pageCount: PropTypes.number.isRequired,
diff --git a/src/components/learner-credit-management/SpendTableAmountContents.jsx b/src/components/learner-credit-management/SpendTableAmountContents.jsx
new file mode 100644
index 0000000000..2764439f63
--- /dev/null
+++ b/src/components/learner-credit-management/SpendTableAmountContents.jsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Stack } from '@edx/paragon';
+
+import { formatPrice } from './data';
+
+const SpendTableAmountContents = ({ row }) => {
+ const formattedContentPrice = formatPrice(row.original.courseListPrice);
+ return (
+
+ {row.original.reversal && (
+ +{formattedContentPrice}
+ )}
+ -{formattedContentPrice}
+
+ );
+};
+
+const rowPropType = PropTypes.shape({
+ original: PropTypes.shape({
+ courseListPrice: PropTypes.number.isRequired,
+ reversal: PropTypes.shape({
+ created: PropTypes.string,
+ }),
+ }).isRequired,
+}).isRequired;
+
+SpendTableAmountContents.propTypes = {
+ row: rowPropType,
+};
+
+export default SpendTableAmountContents;
diff --git a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
index a0d61f8dda..8ec52fd7cb 100644
--- a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
+++ b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
@@ -1,17 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import { Hyperlink } from '@edx/paragon';
+import { Stack, Hyperlink } from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform';
import EmailAddressTableCell from './EmailAddressTableCell';
+import { formatDate } from './data';
const SpendTableEnrollmentDetailsContents = ({
row,
enableLearnerPortal,
enterpriseSlug,
}) => (
- <>
+
+ {row.original.reversal && (
+
+ Refunded on {formatDate(row.original.reversal.created)}
+
+ )}
- {row.original.courseTitle}
+ {row.original.courseTitle || 'View course'}
) : (
{row.original.courseTitle}
)}
- >
+
);
const rowPropType = PropTypes.shape({
original: PropTypes.shape({
courseKey: PropTypes.string.isRequired,
- courseTitle: PropTypes.string.isRequired,
+ courseTitle: PropTypes.string,
userEmail: PropTypes.string,
enterpriseEnrollmentId: PropTypes.number,
fulfillmentIdentifier: PropTypes.string,
+ reversal: PropTypes.shape({
+ created: PropTypes.string,
+ }),
}).isRequired,
}).isRequired;
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
index 3290ef90dc..351f8b5cf4 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
@@ -13,6 +13,7 @@ import {
mockSubsidyAccessPolicyUUID,
} from '../tests/constants';
import { queryClient } from '../../../test/testUtils';
+import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
jest.mock('./useBudgetId');
jest.mock('./useSubsidyAccessPolicy');
@@ -102,26 +103,44 @@ describe('useBudgetDetailActivityOverview', () => {
},
});
}
+ const mockFetchCustomerTransactions = jest.spyOn(SubsidyApiService, 'fetchCustomerTransactions');
const mockFetchCourseEnrollments = jest.spyOn(EnterpriseDataApiService, 'fetchCourseEnrollments');
- mockFetchCourseEnrollments.mockResolvedValue({
- data: {
- count: 1,
- results: [{ id: 'mock-course-enrollment-id' }],
- },
- });
+ const mockSubsidyTransaction = { uuid: 'mock-transaction-uuid' };
+ const mockAnalyticsApiRedemption = { id: 'mock-course-enrollment-id' };
+
+ if (isTopDownAssignmentEnabled) {
+ mockFetchCustomerTransactions.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [mockSubsidyTransaction],
+ },
+ });
+ } else {
+ mockFetchCourseEnrollments.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [mockAnalyticsApiRedemption],
+ },
+ });
+ }
const { result, waitForNextUpdate } = renderHook(
() => useBudgetDetailActivityOverview({
enterpriseUUID: mockEnterpriseUUID,
- isTopDownAssignmentEnabled: true,
+ isTopDownAssignmentEnabled,
}),
{ wrapper },
);
expect(useSubsidyAccessPolicy).toHaveBeenCalledWith(mockSubsidyAccessPolicyUUID);
- expect(mockFetchCourseEnrollments).toHaveBeenCalledTimes(1);
- if (hasAssignableBudget) {
+ if (isTopDownAssignmentEnabled) {
+ expect(mockFetchCustomerTransactions).toHaveBeenCalledTimes(1);
+ } else {
+ expect(mockFetchCourseEnrollments).toHaveBeenCalledTimes(1);
+ }
+
+ if (isTopDownAssignmentEnabled && hasAssignableBudget) {
expect(mockListContentAssignments).toHaveBeenCalledTimes(1);
} else {
expect(mockListContentAssignments).not.toHaveBeenCalled();
@@ -132,7 +151,7 @@ describe('useBudgetDetailActivityOverview', () => {
const expectedData = {
spentTransactions: {
count: 1,
- results: [{ id: 'mock-course-enrollment-id' }],
+ results: [isTopDownAssignmentEnabled ? mockSubsidyTransaction : mockAnalyticsApiRedemption],
},
};
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
index c5f824b8a1..2898577a0a 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
@@ -49,7 +49,7 @@ const useOfferRedemptions = (
enterpriseUUID,
offerId = null,
budgetId = null,
- shouldFetchSubsidyTransactions = false,
+ isTopDownAssignmentEnabled = false,
) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
@@ -63,14 +63,17 @@ const useOfferRedemptions = (
const fetchOfferRedemptions = useCallback((args) => {
const fetch = async () => {
try {
+ const shouldFetchSubsidyTransactions = budgetId && isTopDownAssignmentEnabled;
setIsLoading(true);
const options = {
page: args.pageIndex + 1, // `DataTable` uses zero-indexed array
pageSize: args.pageSize,
- ignoreNullCourseListPrice: true,
};
+ if (!shouldFetchSubsidyTransactions) {
+ options.ignoreNullCourseListPrice = true;
+ }
if (budgetId !== null) {
- options.budgetId = budgetId;
+ options[shouldFetchSubsidyTransactions ? 'subsidyAccessPolicyUuid' : 'budgetId'] = budgetId;
}
if (offerId !== null) {
options.offerId = offerId;
@@ -83,7 +86,7 @@ const useOfferRedemptions = (
}
let data;
let transformedTableResults;
- if (budgetId && shouldFetchSubsidyTransactions) {
+ if (shouldFetchSubsidyTransactions) {
const response = await SubsidyApiService.fetchCustomerTransactions(
subsidyAccessPolicy?.subsidyUuid,
options,
@@ -131,7 +134,7 @@ const useOfferRedemptions = (
offerId,
budgetId,
shouldTrackFetchEvents,
- shouldFetchSubsidyTransactions,
+ isTopDownAssignmentEnabled,
subsidyAccessPolicy?.subsidyUuid,
]);
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
index 9b5d1c3c9f..0738b5bf1a 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
@@ -66,29 +66,29 @@ describe('useOfferRedemptions', () => {
{
budgetId: 'test-budget-id',
offerId: undefined,
- shouldFetchSubsidyTransactions: true,
+ isTopDownAssignmentEnabled: true,
},
{
budgetId: 'test-budget-id',
offerId: undefined,
- shouldFetchSubsidyTransactions: false,
+ isTopDownAssignmentEnabled: false,
},
{
budgetId: undefined,
offerId: mockEnterpriseOffer.id,
- shouldFetchSubsidyTransactions: false,
+ isTopDownAssignmentEnabled: false,
},
- ])('should fetch enrollment/redemptions metadata for enterprise offer', async ({
+ ])('should fetch enrollment/redemptions metadata for budget (%s)', async ({
budgetId,
offerId,
- shouldFetchSubsidyTransactions,
+ isTopDownAssignmentEnabled,
}) => {
EnterpriseDataApiService.fetchCourseEnrollments.mockResolvedValueOnce({ data: mockOfferEnrollmentsResponse });
SubsidyApiService.fetchCustomerTransactions.mockResolvedValueOnce({ data: mockSubsidyTransactionResponse });
useSubsidyAccessPolicy.mockReturnValue({ data: { subsidyUuid } });
const { result, waitForNextUpdate } = renderHook(
- () => useOfferRedemptions(TEST_ENTERPRISE_UUID, offerId, budgetId, shouldFetchSubsidyTransactions),
+ () => useOfferRedemptions(TEST_ENTERPRISE_UUID, offerId, budgetId, isTopDownAssignmentEnabled),
{ wrapper },
);
@@ -116,15 +116,13 @@ describe('useOfferRedemptions', () => {
await waitForNextUpdate();
- if (budgetId && shouldFetchSubsidyTransactions) {
+ if (budgetId && isTopDownAssignmentEnabled) {
const expectedApiOptions = {
page: 1,
pageSize: 20,
- offerId,
ordering: '-enrollment_date', // default sort order
search: mockOfferEnrollments[0].user_email,
- ignoreNullCourseListPrice: true,
- budgetId,
+ subsidyAccessPolicyUuid: budgetId,
};
expect(SubsidyApiService.fetchCustomerTransactions).toHaveBeenCalledWith(
subsidyUuid,
@@ -146,7 +144,7 @@ describe('useOfferRedemptions', () => {
);
}
- const mockExpectedResultsObj = shouldFetchSubsidyTransactions ? [{
+ const mockExpectedResultsObj = isTopDownAssignmentEnabled ? [{
courseListPrice: 10,
courseTitle,
userEmail,
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 5780e28eb2..a055fd567c 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -1,6 +1,7 @@
import { v4 as uuidv4 } from 'uuid';
import dayjs from 'dayjs';
-import { camelCaseObject } from '@edx/frontend-platform';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+import { logInfo } from '@edx/frontend-platform/logging';
import {
LOW_REMAINING_BALANCE_PERCENT_THRESHOLD,
@@ -10,6 +11,7 @@ import {
import { BUDGET_STATUSES } from '../../EnterpriseApp/data/constants';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import EnterpriseDataApiService from '../../../data/services/EnterpriseDataApiService';
+import SubsidyApiService from '../../../data/services/EnterpriseSubsidyApiService';
/**
* Transforms offer summary from API for display in the UI, guarding
@@ -90,14 +92,15 @@ export const transformUtilizationTableResults = results => results.map(result =>
courseTitle: result.courseTitle,
courseListPrice: result.courseListPrice,
enrollmentDate: result.enrollmentDate,
- courseProductLine: result.courseProductLine,
uuid: uuidv4(),
courseKey: result.courseKey,
}));
export const transformUtilizationTableSubsidyTransactionResults = results => results.map(result => ({
created: result.created,
- enterpriseEnrollmentId: result.fulfillmentIdentifier,
+ enrollmentDate: result.created,
+ fulfillmentIdentifier: result.fulfillmentIdentifier,
+ reversal: result.reversal,
userEmail: result.lmsUserEmail,
courseTitle: result.contentTitle,
courseListPrice: result.unit === 'usd_cents' ? -1 * (result.quantity / 100) : -1 * results.quantity,
@@ -249,25 +252,43 @@ export async function fetchContentAssignments(assignmentConfigurationUUID, optio
*/
export async function fetchSpentTransactions({
enterpriseUUID,
- subsidyAccessPolicyId,
- enterpriseOfferId,
+ subsidyAccessPolicy,
+ budgetId,
+ isTopDownAssignmentEnabled,
}) {
const options = {
page: 1,
pageSize: 25,
- ignoreNullCourseListPrice: true,
};
- if (subsidyAccessPolicyId) {
- options.budgetId = subsidyAccessPolicyId;
- } else if (enterpriseOfferId) {
- options.offerId = enterpriseOfferId;
+ let response;
+ const shouldFetchSubsidyTransactions = !!subsidyAccessPolicy && isTopDownAssignmentEnabled;
+ if (shouldFetchSubsidyTransactions) {
+ options.subsidyAccessPolicyUuid = budgetId;
+ // Feature flag is enabled and the budget is a subsidy access policy, so pull from
+ // the `transactions` API via enterprise-subsidy.
+ response = await SubsidyApiService.fetchCustomerTransactions(
+ subsidyAccessPolicy.subsidyUuid,
+ options,
+ );
+ } else {
+ // Feature flag disabled or budget is not a subsidy access policy; continue to call analytics API.
+ if (subsidyAccessPolicy) {
+ options.budgetId = budgetId;
+ } else {
+ options.offerId = budgetId;
+ }
+ options.ignoreNullCourseListPrice = true;
+ response = await EnterpriseDataApiService.fetchCourseEnrollments(
+ enterpriseUUID,
+ options,
+ );
+ }
+
+ if (!response) {
+ logInfo('[fetchSpentTransactions] Spent transactions were not fetched from API. No budget identifier provided.');
}
- const response = await EnterpriseDataApiService.fetchCourseEnrollments(
- enterpriseUUID,
- options,
- );
return camelCaseObject(response.data);
}
@@ -295,8 +316,9 @@ export async function retrieveBudgetDetailActivityOverview({
const promisesToFulfill = [
fetchSpentTransactions({
enterpriseUUID,
- subsidyAccessPolicyId: subsidyAccessPolicy?.uuid,
- enterpriseOfferId: budgetId,
+ subsidyAccessPolicy,
+ budgetId,
+ isTopDownAssignmentEnabled,
}),
];
if (isBudgetAssignable) {
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index d8729c61b5..62f31fd0b4 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -21,6 +21,7 @@ import {
formatDate,
DEFAULT_PAGE,
PAGE_SIZE,
+ formatPrice,
} from '../data';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
import {
@@ -61,6 +62,7 @@ const initialStoreState = {
};
const mockLearnerEmail = 'edx@example.com';
+const mockSecondLearnerEmail = 'edx001@example.com';
const mockCourseKey = 'edX+DemoX';
const mockContentTitle = 'edx Demo';
@@ -106,6 +108,26 @@ const mockLearnerContentAssignment = {
actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction],
errorReason: null,
};
+const mockEnrollmentTransactionReversal = {
+ uuid: 'test-transaction-reversal-uuid',
+ created: '2023-10-31',
+};
+const mockEnrollmentTransaction = {
+ uuid: 'test-transaction-uuid',
+ enrollmentDate: '2023-10-28',
+ courseKey: mockCourseKey,
+ courseTitle: mockContentTitle,
+ userEmail: mockLearnerEmail,
+ fulfillmentIdentifier: 'test-fulfillment-identifier',
+ courseListPrice: 100,
+ reversal: null,
+};
+const mockEnrollmentTransactionWithReversal = {
+ ...mockEnrollmentTransaction,
+ uuid: 'test-transaction-with-reversal-uuid',
+ userEmail: mockSecondLearnerEmail,
+ reversal: mockEnrollmentTransactionReversal,
+};
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
@@ -230,8 +252,8 @@ describe(' ', () => {
useBudgetDetailActivityOverview.mockReturnValue({
isLoading: false,
data: {
- contentAssignments: undefined,
- spentTransactions: { count: 1 },
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
},
});
useBudgetContentAssignments.mockReturnValue({
@@ -263,7 +285,7 @@ describe(' ', () => {
expect(spentSection.getByText('No results found')).toBeInTheDocument();
});
- it('renders with assigned table empty state with spent table and catalog tab available for assignable budgets', () => {
+ it('renders with assigned table empty state with spent table and catalog tab available for assignable budgets', async () => {
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
@@ -276,7 +298,7 @@ describe(' ', () => {
isLoading: false,
data: {
contentAssignments: { count: 0 },
- spentTransactions: { count: 1 },
+ spentTransactions: { count: 2 },
},
});
useBudgetContentAssignments.mockReturnValue({
@@ -290,7 +312,11 @@ describe(' ', () => {
});
useOfferRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
+ offerRedemptions: {
+ itemCount: 2,
+ pageCount: 1,
+ results: [mockEnrollmentTransaction, mockEnrollmentTransactionWithReversal],
+ },
fetchOfferRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -302,6 +328,19 @@ describe(' ', () => {
// Catalog tab exists and is NOT active
expect(screen.getByText('Catalog').getAttribute('aria-selected')).toBe('false');
+
+ // Spend table renders rows of data
+ const spentSection = within(screen.getByText('Spent').closest('section'));
+ expect(spentSection.queryByText('No results found')).not.toBeInTheDocument();
+ expect(spentSection.getByText(mockLearnerEmail)).toBeInTheDocument();
+ expect(spentSection.getByText(mockSecondLearnerEmail)).toBeInTheDocument();
+ expect(spentSection.queryAllByText(mockContentTitle, { selector: 'a' })).toHaveLength(2);
+ expect(spentSection.queryAllByText(`-${formatPrice(mockEnrollmentTransaction.courseListPrice)}`)).toHaveLength(2);
+
+ // Includes reversal messaging on table row, when appropriate
+ const transactionRowWithReversal = within(spentSection.getByText(mockSecondLearnerEmail).closest('tr'));
+ expect(transactionRowWithReversal.getByText(`Refunded on ${formatDate(mockEnrollmentTransactionReversal.created)}`)).toBeInTheDocument();
+ expect(transactionRowWithReversal.getByText(`+${formatPrice(mockEnrollmentTransaction.courseListPrice)}`)).toBeInTheDocument();
});
it('renders with assigned table data and handles table refresh', () => {
From bd8455be163df4946f5c26d1ed094dcd744a3991 Mon Sep 17 00:00:00 2001
From: Alex Dusenbery
Date: Mon, 20 Nov 2023 11:19:55 -0500
Subject: [PATCH 072/124] fix: list transactions from the admin-tx view
ENT-7999
---
src/data/services/EnterpriseSubsidyApiService.js | 2 +-
src/data/services/tests/EnterpriseSubsidyApiService.test.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/data/services/EnterpriseSubsidyApiService.js b/src/data/services/EnterpriseSubsidyApiService.js
index 84550170fa..1b631a86fe 100644
--- a/src/data/services/EnterpriseSubsidyApiService.js
+++ b/src/data/services/EnterpriseSubsidyApiService.js
@@ -16,7 +16,7 @@ class SubsidyApiService {
const queryParams = new URLSearchParams({
...snakeCaseObject(options),
});
- const url = `${SubsidyApiService.baseUrlV2}/subsidies/${subsidyUuid}/transactions/?${queryParams.toString()}`;
+ const url = `${SubsidyApiService.baseUrlV2}/subsidies/${subsidyUuid}/admin/transactions/?${queryParams.toString()}`;
return SubsidyApiService.apiClient().get(url);
}
diff --git a/src/data/services/tests/EnterpriseSubsidyApiService.test.js b/src/data/services/tests/EnterpriseSubsidyApiService.test.js
index 6c894bc1f8..a8f04eeae9 100644
--- a/src/data/services/tests/EnterpriseSubsidyApiService.test.js
+++ b/src/data/services/tests/EnterpriseSubsidyApiService.test.js
@@ -17,7 +17,7 @@ describe('EnterpriseSubsidyApiService', () => {
});
test('fetchCustomerTransactions calls the API to fetch transactions by enterprise subsidy', () => {
const mockSubsidyUUID = 'test-subsidy-uuid';
- const expectedUrl = `${SubsidyApiService.baseUrlV2}/subsidies/${mockSubsidyUUID}/transactions/?`;
+ const expectedUrl = `${SubsidyApiService.baseUrlV2}/subsidies/${mockSubsidyUUID}/admin/transactions/?`;
SubsidyApiService.fetchCustomerTransactions(mockSubsidyUUID);
expect(axios.get).toBeCalledWith(expectedUrl);
});
From 461ccc770f39a3fec3d5dff2cfee15c27eb734c5 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Mon, 20 Nov 2023 15:41:41 -0500
Subject: [PATCH 073/124] fix: only request committed transactions for spent
table (#1095)
---
src/data/services/EnterpriseSubsidyApiService.js | 1 +
src/data/services/tests/EnterpriseSubsidyApiService.test.js | 4 +++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/data/services/EnterpriseSubsidyApiService.js b/src/data/services/EnterpriseSubsidyApiService.js
index 1b631a86fe..316b3d471c 100644
--- a/src/data/services/EnterpriseSubsidyApiService.js
+++ b/src/data/services/EnterpriseSubsidyApiService.js
@@ -14,6 +14,7 @@ class SubsidyApiService {
static fetchCustomerTransactions(subsidyUuid, options = {}) {
const queryParams = new URLSearchParams({
+ state: 'committed',
...snakeCaseObject(options),
});
const url = `${SubsidyApiService.baseUrlV2}/subsidies/${subsidyUuid}/admin/transactions/?${queryParams.toString()}`;
diff --git a/src/data/services/tests/EnterpriseSubsidyApiService.test.js b/src/data/services/tests/EnterpriseSubsidyApiService.test.js
index a8f04eeae9..09a67c8e1b 100644
--- a/src/data/services/tests/EnterpriseSubsidyApiService.test.js
+++ b/src/data/services/tests/EnterpriseSubsidyApiService.test.js
@@ -15,12 +15,14 @@ describe('EnterpriseSubsidyApiService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
+
test('fetchCustomerTransactions calls the API to fetch transactions by enterprise subsidy', () => {
const mockSubsidyUUID = 'test-subsidy-uuid';
- const expectedUrl = `${SubsidyApiService.baseUrlV2}/subsidies/${mockSubsidyUUID}/admin/transactions/?`;
+ const expectedUrl = `${SubsidyApiService.baseUrlV2}/subsidies/${mockSubsidyUUID}/admin/transactions/?state=committed`;
SubsidyApiService.fetchCustomerTransactions(mockSubsidyUUID);
expect(axios.get).toBeCalledWith(expectedUrl);
});
+
test('getSubsidyByCustomerUUID calls the API to fetch subsides by enterprise customer UUID', () => {
const mockCustomerUUID = 'test-customer-uuid';
const expectedUrl = `${SubsidyApiService.baseUrlV1}/subsidies/?enterprise_customer_uuid=${mockCustomerUUID}`;
From c0e0bc11f5704563b8025965e3d98794637ba898 Mon Sep 17 00:00:00 2001
From: Alex Dusenbery
Date: Mon, 20 Nov 2023 16:52:57 -0500
Subject: [PATCH 074/124] feat: query for learner_state__in
related to API change https://github.com/openedx/enterprise-access/pull/334
ENT-7809
---
.../services/EnterpriseAccessApiService.js | 13 +++++++++----
.../tests/EnterpriseAccessApiService.test.js | 18 +++++++++++++++++-
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index b115f194fb..d0035b3e63 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -147,15 +147,20 @@ class EnterpriseAccessApiService {
* List content assignments for a specific AssignmentConfiguration.
*/
static listContentAssignments(assignmentConfigurationUUID, options = {}) {
- const params = new URLSearchParams({
+ const { learnerState, ...optionsRest } = options;
+ const params = {
page: 1,
page_size: 25,
// Only include assignments with allocated or errored states. The table should NOT
// include assignments in the canceled or accepted states.
state__in: 'allocated,errored',
- ...snakeCaseObject(options),
- });
- const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/?${params.toString()}`;
+ ...snakeCaseObject(optionsRest),
+ };
+ if (learnerState) {
+ params.learner_state__in = learnerState;
+ }
+ const urlParams = new URLSearchParams(params);
+ const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/?${urlParams.toString()}`;
return EnterpriseAccessApiService.apiClient().get(url);
}
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index 7fd27b22a1..a6a5aeec7f 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -134,7 +134,23 @@ describe('EnterpriseAccessApiService', () => {
});
});
- test('listContentAssignments calls enterprise-access to fetch content assignments', () => {
+ test('listContentAssignments calls enterprise-access to fetch content assignments with learner state filter', () => {
+ const options = {
+ learnerState: ['notifying', 'waiting'],
+ };
+ EnterpriseAccessApiService.listContentAssignments(mockAssignmentConfigurationUUID, options);
+ const expectedParams = new URLSearchParams({
+ page: 1,
+ page_size: 25,
+ state__in: 'allocated,errored',
+ learner_state__in: 'notifying,waiting',
+ }).toString();
+ expect(axios.get).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/?${expectedParams}`,
+ );
+ });
+
+ test('listContentAssignments calls enterprise-access to fetch content assignments without learner state filter', () => {
EnterpriseAccessApiService.listContentAssignments(mockAssignmentConfigurationUUID);
const expectedParams = new URLSearchParams({
page: 1,
From 15b95a008593029ca0b37e414a20213b449e3ec1 Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Fri, 17 Nov 2023 22:20:25 +0000
Subject: [PATCH 075/124] fix: cleaning up sso self service wording and
behaviors
---
.../SettingsSSOTab/NewExistingSSOConfigs.jsx | 4 +-
.../SettingsSSOTab/NewSSOConfigCard.jsx | 11 +-
.../SettingsSSOTab/NewSSOConfigForm.jsx | 64 +++++---
.../settings/SettingsSSOTab/NewSSOStepper.jsx | 7 +-
.../settings/SettingsSSOTab/index.jsx | 43 ++++--
.../SettingsSSOTab/steps/SSOConfigIDPStep.jsx | 2 +-
.../tests/ExistingSSOConfigs.test.jsx | 52 +++++--
.../tests/NewExistingSSOConfigs.test.jsx | 1 +
.../tests/NewSSOConfigCard.test.jsx | 87 ++++++++---
.../tests/NewSSOConfigForm.test.jsx | 146 +++++++++++++-----
10 files changed, 294 insertions(+), 123 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
index 638f06f7a4..bdfc1b910f 100644
--- a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
+++ b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
@@ -19,7 +19,7 @@ const FRESH_CONFIG_POLLING_INTERVAL = 30000;
const UPDATED_CONFIG_POLLING_INTERVAL = 2000;
const NewExistingSSOConfigs = ({
- configs, refreshBool, setRefreshBool, enterpriseId, setPollingNetworkError,
+ configs, refreshBool, setRefreshBool, enterpriseId, setPollingNetworkError, setIsStepperOpen,
}) => {
const [inactiveConfigs, setInactiveConfigs] = useState([]);
const [activeConfigs, setActiveConfigs] = useState([]);
@@ -60,6 +60,7 @@ const NewExistingSSOConfigs = ({
setRefreshBool={setRefreshBool}
refreshBool={refreshBool}
setUpdateError={setUpdateError}
+ setIsStepperOpen={setIsStepperOpen}
/>
{updateError?.config === config.uuid && (
@@ -197,6 +198,7 @@ NewExistingSSOConfigs.propTypes = {
setRefreshBool: PropTypes.func.isRequired,
enterpriseId: PropTypes.string.isRequired,
setPollingNetworkError: PropTypes.func.isRequired,
+ setIsStepperOpen: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
index 984162305b..bcdacdaa2c 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
@@ -15,6 +15,7 @@ const NewSSOConfigCard = ({
setRefreshBool,
refreshBool,
setUpdateError,
+ setIsStepperOpen,
}) => {
const VALIDATED = config.validated_at;
const ENABLED = config.active;
@@ -23,6 +24,11 @@ const NewSSOConfigCard = ({
const { setProviderConfig } = useContext(SSOConfigContext);
+ const onConfigureClick = (selectedConfig) => {
+ setProviderConfig(selectedConfig);
+ setIsStepperOpen(true);
+ };
+
const convertToReadableDate = (date) => {
const dateObj = new Date(date);
const options = { year: 'numeric', month: 'short', day: 'numeric' };
@@ -127,7 +133,7 @@ const NewSSOConfigCard = ({
{!VALIDATED && CONFIGURED && (
setProviderConfig(config)}
+ onClick={() => onConfigureClick(config)}
variant="outline-primary"
data-testid="existing-sso-config-card-configure-button"
>
@@ -181,7 +187,7 @@ const NewSSOConfigCard = ({
{VALIDATED && (
setProviderConfig(config)}
+ onClick={() => onConfigureClick(config)}
>
Configure
@@ -224,6 +230,7 @@ NewSSOConfigCard.propTypes = {
setRefreshBool: PropTypes.func.isRequired,
refreshBool: PropTypes.bool.isRequired,
setUpdateError: PropTypes.func.isRequired,
+ setIsStepperOpen: PropTypes.func.isRequired,
};
export default NewSSOConfigCard;
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx
index 6cbcab4c14..0dba3b3413 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigForm.jsx
@@ -1,4 +1,5 @@
import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
import { Alert, Hyperlink } from '@edx/paragon';
import { WarningFilled } from '@edx/paragon/icons';
import { SSOConfigContext } from './SSOConfigContext';
@@ -7,38 +8,51 @@ import { HELP_CENTER_SAML_LINK } from '../data/constants';
import { features } from '../../../config';
import NewSSOStepper from './NewSSOStepper';
-const NewSSOConfigForm = () => {
+const NewSSOConfigForm = ({ setIsStepperOpen, isStepperOpen }) => {
const { ssoState: { currentError } } = useContext(SSOConfigContext);
const { AUTH0_SELF_SERVICE_INTEGRATION } = features;
+
return (
-
- Connect to a SAML identity provider for single sign-on
- to allow quick access to your organization's learning catalog.
-
- {AUTH0_SELF_SERVICE_INTEGRATION ?
:
}
+ {!AUTH0_SELF_SERVICE_INTEGRATION && (
+
+ Connect to a SAML identity provider for single sign-on
+ to allow quick access to your organization's learning catalog.
+
+ )}
+ {AUTH0_SELF_SERVICE_INTEGRATION ? (
+
+ ) : (
+
+ )}
{currentError && (
-
- Help Center
- ,
- ]}
- >
- Something went wrong.
-
- {currentError}
-
-
+
+ Help Center
+ ,
+ ]}
+ >
+ Something went wrong.
+
+ {currentError}
+
+
)}
);
};
+
+NewSSOConfigForm.propTypes = {
+ setIsStepperOpen: PropTypes.func.isRequired,
+ isStepperOpen: PropTypes.bool.isRequired,
+};
+
export default NewSSOConfigForm;
diff --git a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
index 1e928fb8d4..dabed9ccff 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOStepper.jsx
@@ -11,12 +11,11 @@ import { camelCaseDict } from '../../../utils';
import UnsavedSSOChangesModal from './UnsavedSSOChangesModal';
import { IDP_URL_SELECTION, IDP_XML_SELECTION } from './steps/NewSSOConfigConnectStep';
-const NewSSOStepper = ({ enterpriseId }) => {
+const NewSSOStepper = ({ enterpriseId, isStepperOpen, setIsStepperOpen }) => {
const {
- setProviderConfig, refreshBool, setRefreshBool, ssoState: { providerConfig },
+ setProviderConfig, setRefreshBool, ssoState: { providerConfig, refreshBool },
} = useContext(SSOConfigContext);
const providerConfigCamelCase = camelCaseDict(providerConfig || {});
- const [isStepperOpen, setIsStepperOpen] = useState(true);
const [configureError, setConfigureError] = useState(null);
const handleCloseWorkflow = () => {
setProviderConfig?.(null);
@@ -52,6 +51,8 @@ const NewSSOStepper = ({ enterpriseId }) => {
NewSSOStepper.propTypes = {
enterpriseId: PropTypes.string.isRequired,
+ isStepperOpen: PropTypes.bool.isRequired,
+ setIsStepperOpen: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index 14e0175e74..52ac8817ec 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -29,6 +29,7 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const { AUTH0_SELF_SERVICE_INTEGRATION } = features;
const [isOpen, open, close] = useToggle(false);
const [pollingNetworkError, setPollingNetworkError] = useState(false);
+ const [isStepperOpen, setIsStepperOpen] = useState(true);
const newConfigurationButtonOnClick = async () => {
Promise.all(existingConfigs.map(config => LmsApiService.updateEnterpriseSsoOrchestrationRecord(
@@ -129,6 +130,7 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
refreshBool={refreshBool}
setRefreshBool={setRefreshBool}
setPollingNetworkError={setPollingNetworkError}
+ setIsStepperOpen={setIsStepperOpen}
/>
)}
{/* Nothing found so guide user to creation/edit form */}
@@ -137,11 +139,16 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
)}
{/* Since we found a selected providerConfig we know we are in editing mode and can safely
render the create/edit form */}
- {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && ( )}
+ {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && (
+
+ )}
{pdError && (
-
- An error occurred loading the SAML data: {pdError?.message}
-
+
+ An error occurred loading the SAML data: {pdError?.message}
+
)}
setInfoMessage(null)}
@@ -178,27 +185,29 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
existing configs but no providerConfig then we can safely render the listings page */}
{existingConfigs?.length > 0 && (providerConfig === null)
&& (
-
+
)}
{/* Nothing found so guide user to creation/edit form */}
{showNoSSOCard && }
{/* Since we found a selected providerConfig we know we are in editing mode and can safely
render the create/edit form */}
- {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && ( )}
+ {((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && (
+
+ )}
{error && (
-
- An error occurred loading the SAML configs: {error?.message}
-
+
+ An error occurred loading the SAML configs: {error?.message}
+
)}
{pdError && (
-
- An error occurred loading the SAML data: {pdError?.message}
-
+
+ An error occurred loading the SAML data: {pdError?.message}
+
)}
{infoMessage && (
{
const {
metadataURL, entityID, handleMetadataURLUpdate, handleEntityIDUpdate,
} = useIdpState();
- const { refreshBool } = useContext(SSOConfigContext);
+ const { ssoState: { refreshBool } } = useContext(SSOConfigContext);
const [existingProviderData] = useExistingProviderData(enterpriseId, refreshBool);
const TITLE = 'First provide your Identity Provider Metadata and fill out the corresponding fields.';
diff --git a/src/components/settings/SettingsSSOTab/tests/ExistingSSOConfigs.test.jsx b/src/components/settings/SettingsSSOTab/tests/ExistingSSOConfigs.test.jsx
index b2a3a31a7b..c43ec303a1 100644
--- a/src/components/settings/SettingsSSOTab/tests/ExistingSSOConfigs.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/ExistingSSOConfigs.test.jsx
@@ -1,5 +1,10 @@
import React from 'react';
-import { render, screen, waitFor } from '@testing-library/react';
+import {
+ act,
+ render,
+ screen,
+ waitFor,
+} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import configureMockStore from 'redux-mock-store';
@@ -104,12 +109,15 @@ describe(' ', () => {
);
expect(screen.getByText('nacho cheese')).toBeInTheDocument();
expect(screen.getByText('Inactive')).toBeInTheDocument();
-
- userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${inactiveConfig[0].id}`));
+ act(() => {
+ userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${inactiveConfig[0].id}`));
+ });
expect(screen.getByText('Enable')).toBeInTheDocument();
expect(screen.getByText('Edit')).toBeInTheDocument();
- userEvent.click(screen.getByText('Enable'));
+ act(() => {
+ userEvent.click(screen.getByText('Enable'));
+ });
const data = new FormData();
data.append('enabled', true);
data.append('enterprise_customer_uuid', enterpriseId);
@@ -129,7 +137,9 @@ describe(' ', () => {
);
expect(screen.getByText('bbq')).toBeInTheDocument();
expect(screen.getByText('Incomplete')).toBeInTheDocument();
- userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
+ act(() => {
+ userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
+ });
await waitFor(() => {
expect(screen.getByText('Delete')).toBeInTheDocument();
expect(screen.getByText('Edit')).toBeInTheDocument();
@@ -162,8 +172,12 @@ describe(' ', () => {
/>
,
);
- userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
- userEvent.click(screen.getByText('Delete'));
+ act(() => {
+ userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
+ });
+ act(() => {
+ userEvent.click(screen.getByText('Delete'));
+ });
expect(LmsApiService.deleteProviderConfig).toHaveBeenCalledWith(incompleteConfig[0].id, enterpriseId);
});
it('properly handles errors when deleting provider data', () => {
@@ -182,8 +196,12 @@ describe(' ', () => {
/>
,
);
- userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
- userEvent.click(screen.getByText('Delete'));
+ act(() => {
+ userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
+ });
+ act(() => {
+ userEvent.click(screen.getByText('Delete'));
+ });
expect(handleErrors).toHaveBeenCalled();
});
it('properly handles errors when deleting provider configs', () => {
@@ -202,8 +220,12 @@ describe(' ', () => {
/>
,
);
- userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
- userEvent.click(screen.getByText('Delete'));
+ act(() => {
+ userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
+ });
+ act(() => {
+ userEvent.click(screen.getByText('Delete'));
+ });
expect(handleErrors).toHaveBeenCalled();
});
it('properly displays error message when deleting provider configs', async () => {
@@ -223,8 +245,12 @@ describe(' ', () => {
/>
,
);
- userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
- userEvent.click(screen.getByText('Delete'));
+ act(() => {
+ userEvent.click(screen.getByTestId(`existing-sso-config-card-dropdown-${incompleteConfig[0].id}`));
+ });
+ act(() => {
+ userEvent.click(screen.getByText('Delete'));
+ });
await waitFor(() => {
expect(screen.getByText(
'We were unable to delete your configuration. Please try removing again or contact support for help.',
diff --git a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
index 2a0b1950ed..6e0fcece64 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
@@ -155,6 +155,7 @@ const setupNewExistingSSOConfigs = (configs) => {
refreshBool={false}
setRefreshBool={mockSetRefreshBool}
setPollingNetworkError={mockSetPollingNetworkError}
+ setIsStepperOpen={jest.fn()}
/>
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
index 07398e464a..887ade767c 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigCard.test.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import userEvent from '@testing-library/user-event';
import { act, render, screen } from '@testing-library/react';
+import { SSOConfigContext, SSO_INITIAL_STATE } from '../SSOConfigContext';
import NewSSOConfigCard from '../NewSSOConfigCard';
import LmsApiService from '../../../../data/services/LmsApiService';
@@ -18,6 +19,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-08-06T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -41,6 +44,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-08-05T15:00:00Z',
submitted_at: '2021-08-04T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -64,6 +69,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-08-05T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -87,6 +94,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: null,
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -108,6 +117,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-09-05T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -120,27 +131,59 @@ describe('New SSO Config Card Tests', () => {
).toBeInTheDocument();
});
test('displays configure button properly', async () => {
+ const mockSetIsStepperOpen = jest.fn();
+ const mockSetProviderConfig = jest.fn();
+ const contextValue = {
+ ...SSO_INITIAL_STATE,
+ setCurrentError: jest.fn(),
+ currentError: null,
+ dispatchSsoState: jest.fn(),
+ ssoState: {
+ idp: {
+ metadataURL: '',
+ entityID: '',
+ entryType: '',
+ isDirty: false,
+ },
+ serviceprovider: {
+ isSPConfigured: false,
+ },
+ refreshBool: false,
+ providerConfig: {
+ id: 1337,
+ },
+ },
+ setProviderConfig: mockSetProviderConfig,
+ setRefreshBool: jest.fn(),
+ };
render(
- ,
+
+
+ ,
);
- expect(
- screen.getByTestId(
- 'existing-sso-config-card-configure-button',
- ),
- ).toBeInTheDocument();
+ const button = screen.getByTestId(
+ 'existing-sso-config-card-configure-button',
+ );
+ act(() => {
+ userEvent.click(button);
+ });
+ expect(mockSetIsStepperOpen).toHaveBeenCalledWith(true);
+ expect(mockSetProviderConfig).toHaveBeenCalled();
});
test('displays enable button properly', async () => {
render(
@@ -154,6 +197,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-09-05T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -179,6 +224,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-09-05T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
@@ -206,6 +253,8 @@ describe('New SSO Config Card Tests', () => {
configured_at: '2021-09-05T15:00:00Z',
submitted_at: '2021-08-05T15:00:00Z',
}}
+ setUpdateError={jest.fn()}
+ setIsStepperOpen={jest.fn()}
setLoading={jest.fn()}
setRefreshBool={jest.fn()}
refreshBool={false}
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
index 82fbe4109e..084157a67b 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
@@ -80,12 +80,20 @@ const contextValue = {
setRefreshBool: jest.fn(),
};
+const setIsStepperOpen = jest.fn();
+
const setupNewSSOStepper = (contextChanges = {}) => {
features.AUTH0_SELF_SERVICE_INTEGRATION = true;
return render(
-
+
+
+
,
);
@@ -103,7 +111,11 @@ describe('SAML Config Tab', () => {
render(
-
+
,
);
@@ -124,7 +136,11 @@ describe('SAML Config Tab', () => {
render(
-
+
,
);
@@ -154,7 +170,13 @@ describe('SAML Config Tab', () => {
render(
-
+
+
+
,
);
expect(
@@ -180,7 +202,13 @@ describe('SAML Config Tab', () => {
render(
-
+
+
+
,
);
expect(
@@ -200,7 +228,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
@@ -224,7 +258,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
@@ -245,7 +285,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
@@ -271,7 +317,13 @@ describe('SAML Config Tab', () => {
render(
-
+
+
+
,
);
expect(
@@ -298,7 +350,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
@@ -318,7 +376,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'idp';
render(
-
+
+
+
,
);
await waitFor(() => {
@@ -417,14 +481,7 @@ describe('SAML Config Tab', () => {
}, []);
userEvent.click(getButtonElement('Exit'));
- await waitFor(() => {
- expect(
- screen.queryByText(
- 'Connect to a SAML identity provider for single sign-on'
- + ' to allow quick access to your organization\'s learning catalog.',
- ),
- ).toBeInTheDocument();
- }, []);
+ expect(setIsStepperOpen).toHaveBeenCalledWith(false);
});
test('new SSO workflow load existing metadata url config', async () => {
const testMetadataUrl = 'http://test.metadata';
@@ -492,25 +549,6 @@ describe('SAML Config Tab', () => {
expect(getButtonElement('Next')).toBeInTheDocument();
}, []);
});
- test('cancel out of new SSO workflow', async () => {
- setupNewSSOStepper();
- // Connect Step Select an option to trigger cancel modal
- userEvent.click(screen.getByText('Okta'));
- userEvent.click(getButtonElement('Cancel'));
- await waitFor(() => {
- expect(getButtonElement('Exit')).toBeInTheDocument();
- }, []);
- userEvent.click(getButtonElement('Exit'));
-
- await waitFor(() => {
- expect(
- screen.queryByText(
- 'Connect to a SAML identity provider for single sign-on'
- + ' to allow quick access to your organization\'s learning catalog.',
- ),
- ).toBeInTheDocument();
- }, []);
- });
test('idp step fetches and displays existing idp data fields', async () => {
// Setup
const mockGetProviderData = jest.spyOn(LmsApiService, 'getProviderData');
@@ -520,7 +558,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'idp';
render(
-
+
+
+
,
);
expect(
@@ -539,7 +583,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
@@ -612,7 +662,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
@@ -640,7 +696,13 @@ describe('SAML Config Tab', () => {
contextValue.ssoState.currentStep = 'configure';
render(
-
+
+
+
,
);
expect(
From c5cb6ace4f9f10e40b5f46afb305cec63b63e11d Mon Sep 17 00:00:00 2001
From: Marlon Keating <322346+marlonkeating@users.noreply.github.com>
Date: Wed, 22 Nov 2023 09:17:09 -0800
Subject: [PATCH 076/124] chore: Add error message for SSO config step 3
authorization checkbox (#1100)
chore: rename ambiguous variable
---
.../forms/ValidatedFormCheckbox.tsx | 60 +++++++++++++++++++
.../steps/NewSSOConfigAuthorizeStep.tsx | 14 ++---
2 files changed, 64 insertions(+), 10 deletions(-)
create mode 100644 src/components/forms/ValidatedFormCheckbox.tsx
diff --git a/src/components/forms/ValidatedFormCheckbox.tsx b/src/components/forms/ValidatedFormCheckbox.tsx
new file mode 100644
index 0000000000..48ab385624
--- /dev/null
+++ b/src/components/forms/ValidatedFormCheckbox.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import omit from 'lodash/omit';
+import isString from 'lodash/isString';
+
+import { Form } from '@edx/paragon';
+
+import { setFormFieldAction } from './data/actions';
+import { useFormContext } from './FormContext';
+
+type InheritedParagonCheckboxProps = {
+ className?: string;
+ children: string;
+};
+
+export type ValidatedFormCheckboxProps = {
+ // Field id, required to map to field in FormContext
+ formId: string;
+ // Inline Instructions inside form field when blank
+ fieldInstructions?: string;
+ label?: string;
+ options?: string[][];
+ isInline?: boolean;
+} & InheritedParagonCheckboxProps;
+
+const ValidatedFormCheckbox = (props: ValidatedFormCheckboxProps) => {
+ const {
+ showErrors, formFields, errorMap, dispatch,
+ } = useFormContext();
+ const onChange = (e: React.ChangeEvent) => {
+ if (dispatch) {
+ dispatch(setFormFieldAction({ fieldId: props.formId, value: e.target.checked }));
+ }
+ };
+ const value = formFields?.[props?.formId];
+ const errors = errorMap?.[props.formId];
+ // Show error message if an error message was part of any detected errors
+ const errorShown = errors?.find?.(error => isString(error));
+ const formCheckboxProps = {
+ ...omit(props, ['formId']),
+ onChange,
+ isInvalid: showErrors && errorShown,
+ id: props.formId,
+ value: formFields && formFields[props.formId],
+ };
+
+ return (
+ <>
+ {formCheckboxProps.label}
+
+ {formCheckboxProps.fieldInstructions && (
+ {formCheckboxProps.fieldInstructions}
+ )}
+ {showErrors && errorShown && (
+ {errorShown}
+ )}
+ >
+ );
+};
+
+export default ValidatedFormCheckbox;
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
index 6e62cb3524..dd640f2450 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
@@ -9,12 +9,13 @@ import { createSAMLURLs } from '../utils';
import { SSOConfigContext } from '../SSOConfigContext';
import { setFormFieldAction } from '../../../forms/data/actions';
import { FormFieldValidation, useFormContext } from '../../../forms/FormContext';
+import ValidatedFormCheckbox from '../../../forms/ValidatedFormCheckbox';
export const validations: FormFieldValidation[] = [
{
formFieldId: 'confirmAuthorizedEdxServiceProvider',
validator: (fields) => {
- const ret = !fields.confirmAuthorizedEdxServiceProvider;
+ const ret = !fields.confirmAuthorizedEdxServiceProvider && 'Please verify authorization of edX as a Service Provider.';
return ret;
},
},
@@ -51,12 +52,6 @@ const SSOConfigAuthorizeStep = () => {
configuration, idpSlug, enterpriseSlug, learnerPortalEnabled,
});
- const handleCheck = (event) => {
- dispatch?.(
- setFormFieldAction({ fieldId: 'confirmAuthorizedEdxServiceProvider', value: event.target.checked }),
- );
- };
-
return (
<>
Authorize edX as a Service Provider
@@ -85,10 +80,9 @@ const SSOConfigAuthorizeStep = () => {
Return to this window and check the box once complete
-
-
+
I have authorized edX as a Service Provider
-
+
>
);
};
From 8476f40d214f429635a2356d2313f25e40feff10 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Mon, 27 Nov 2023 10:29:56 -0500
Subject: [PATCH 077/124] fix: update duplicate email validation method to
handle casing (#1101)
* fix: update duplicate email validation method to handle casing
* fix: PR feedback
---
.../learner-credit-management/cards/CourseCard.test.jsx | 5 +++++
src/components/learner-credit-management/cards/data/utils.js | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/CourseCard.test.jsx
index d522cb2ad6..00bf58ecc5 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.test.jsx
@@ -466,6 +466,11 @@ describe('Course card works as expected', () => {
spendAvailableUsd: 1000,
expectedValidationMessage: 'b@b.com has been entered more than once.',
},
+ {
+ learnerEmails: ['a@a.com', 'b@b.com', 'c@c.com', 'B@b.com'],
+ spendAvailableUsd: 1000,
+ expectedValidationMessage: 'B@b.com has been entered more than once.',
+ },
{
learnerEmails: ['a@a.com', 'b@bcom', 'c@c.com', 'a@a.com'],
spendAvailableUsd: 1000,
diff --git a/src/components/learner-credit-management/cards/data/utils.js b/src/components/learner-credit-management/cards/data/utils.js
index 7fc8c91b9f..8dd63d436c 100644
--- a/src/components/learner-credit-management/cards/data/utils.js
+++ b/src/components/learner-credit-management/cards/data/utils.js
@@ -53,7 +53,7 @@ export const isEmailAddressesInputValueValid = ({
const hasEnoughBalanceForAssigment = remainingBalanceAfterAssignment >= 0;
const invalidEmails = learnerEmails.filter((email) => !isEmail(email));
- const duplicateEmails = learnerEmails.filter((email, index) => learnerEmails.indexOf(email) !== index);
+ const duplicateEmails = learnerEmails.filter((email, index) => learnerEmails.indexOf(email.toLowerCase()) !== index);
const isValidInput = invalidEmails.length === 0 && duplicateEmails.length === 0;
const canAllocate = learnerEmailsCount > 0 && hasEnoughBalanceForAssigment && isValidInput;
From ec5d16b8a8f731edd3c994c721f758ff446f07e5 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Tue, 28 Nov 2023 12:49:28 -0500
Subject: [PATCH 078/124] fix: use controlled selection components from
DataTable for BudgetAssigmentsTable (#1102)
---
.../learner-credit-management/BudgetAssignmentsTable.jsx | 9 +++++++++
.../tests/BudgetDetailPage.test.jsx | 4 ++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 69821935f9..a747adaad5 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -28,6 +28,13 @@ const getLearnerStateDisplayName = (learnerState) => {
return undefined;
};
+const selectColumn = {
+ id: 'selection',
+ Header: DataTable.ControlledSelectHeader,
+ Cell: DataTable.ControlledSelect,
+ disableSortBy: true,
+};
+
const BudgetAssignmentsTable = ({
isLoading,
tableData,
@@ -52,7 +59,9 @@ const BudgetAssignmentsTable = ({
manualFilters
isLoading={isLoading}
defaultColumnValues={{ Filter: TableTextFilter }}
+ manualSelectColumn={selectColumn}
FilterStatusComponent={FilterStatus}
+ SelectionStatusComponent={DataTable.ControlledSelectionStatus}
columns={[
{
Header: 'Assignment details',
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 62f31fd0b4..41c4ffc760 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -1074,8 +1074,8 @@ describe(' ', () => {
const remindRowAction = screen.getByTestId('remind-assignment-test-uuid');
expect(remindRowAction).toBeInTheDocument();
}
-
- const checkBox = screen.getByTestId('datatable-select-column-checkbox-cell');
+ // 2 checkboxes exist; the first is the "Select all" checkbox; the 2nd is the checkbox for the first row
+ const checkBox = screen.getAllByRole('checkbox')[1];
expect(checkBox).toBeInTheDocument();
userEvent.click(checkBox);
expect(await screen.findByText('Cancel (1)')).toBeInTheDocument();
From 3649e42e5d10e13aea8dacad608773ad1d910e45 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Wed, 29 Nov 2023 09:54:32 -0500
Subject: [PATCH 079/124] feat: segment eventing for LCM (#1092)
* feat: segment eventing for LCM
* feat: segment eventing for LCM
* feat: segment eventing for LCM
* feat: additional segment events on LCM
* feat: additional segment events on LCM
* fix: remove local test flag
* fix: remove duplicate string contents
* chore: test writing
* chore: PR feedback and tests/refactoring
* chore: PR feedback fixes
* fix: modify segment event naming
* feat: segment events on sort and filter assignment table
* fix: removed casing fix to resolve in seperate PR
* chore: PR feedback
* chore: PR fixes 2
* feat: implements useRef to prevent duplicate track event calls
* chore: PR fixes 3
* chore: PR fixes 3
---
.../CurrentContentHighlightHeader.jsx | 2 +-
.../AssignmentsTableRefreshAction.jsx | 34 +++++-
.../BudgetAssignmentsTable.jsx | 1 -
.../BudgetDetailAssignments.jsx | 4 +
.../MultipleBudgetsPicker.jsx | 1 +
.../NoBudgetActivityEmptyState.jsx | 27 ++++-
.../AssignmentAllocationHelpCollapsibles.jsx | 98 +++++++++++++++++
.../cards/AssignmentModalContent.jsx | 30 ++++--
.../cards/Collapsibles.jsx | 72 -------------
.../cards/CourseCard.jsx | 26 +----
.../cards/CourseCardFooterActions.jsx | 49 +++++++++
.../CreateAllocationErrorAlertModals.jsx | 26 ++++-
.../cards/NewAssignmentModalButton.jsx | 100 ++++++++++++++++--
.../cards/{ => tests}/CourseCard.test.jsx | 73 +++++++++++--
.../data/constants.js | 17 +++
.../useBudgetContentAssignments.test.js | 86 +++++++++++++--
.../useBudgetDetailActivityOverview.test.jsx | 20 ++--
.../{ => tests}/useOfferRedemptions.test.jsx | 16 +--
.../hooks/{ => tests}/useOfferSummary.test.js | 6 +-
.../useSubsidyAccessPolicy.test.jsx | 8 +-
.../data/hooks/useBudgetContentAssignments.js | 47 ++++++--
.../data/hooks/useOfferRedemptions.js | 3 +-
.../data/tests/constants.test.js | 20 ++++
.../search/CatalogSearch.jsx | 7 +-
.../search/CatalogSearchResults.jsx | 1 -
.../{ => search}/tests/CatalogSearch.test.jsx | 8 +-
.../tests/CatalogSearchResults.test.jsx | 38 +++++--
.../tests/BudgetDetailPage.test.jsx | 12 ++-
.../LearnerCreditAllocationTable.test.jsx | 17 +++
.../tests/MultipleBudgetsPage.test.jsx | 12 +++
src/eventTracking.js | 34 +++++-
31 files changed, 698 insertions(+), 197 deletions(-)
create mode 100644 src/components/learner-credit-management/cards/AssignmentAllocationHelpCollapsibles.jsx
delete mode 100644 src/components/learner-credit-management/cards/Collapsibles.jsx
create mode 100644 src/components/learner-credit-management/cards/CourseCardFooterActions.jsx
rename src/components/learner-credit-management/cards/{ => tests}/CourseCard.test.jsx (89%)
rename src/components/learner-credit-management/data/hooks/{ => tests}/useBudgetContentAssignments.test.js (71%)
rename src/components/learner-credit-management/data/hooks/{ => tests}/useBudgetDetailActivityOverview.test.jsx (89%)
rename src/components/learner-credit-management/data/hooks/{ => tests}/useOfferRedemptions.test.jsx (88%)
rename src/components/learner-credit-management/data/hooks/{ => tests}/useOfferSummary.test.js (89%)
rename src/components/learner-credit-management/data/hooks/{ => tests}/useSubsidyAccessPolicy.test.jsx (92%)
create mode 100644 src/components/learner-credit-management/data/tests/constants.test.js
rename src/components/learner-credit-management/{ => search}/tests/CatalogSearch.test.jsx (87%)
rename src/components/learner-credit-management/{ => search}/tests/CatalogSearchResults.test.jsx (83%)
diff --git a/src/components/ContentHighlights/CurrentContentHighlightHeader.jsx b/src/components/ContentHighlights/CurrentContentHighlightHeader.jsx
index d646300301..204de3a95b 100644
--- a/src/components/ContentHighlights/CurrentContentHighlightHeader.jsx
+++ b/src/components/ContentHighlights/CurrentContentHighlightHeader.jsx
@@ -46,7 +46,7 @@ const CurrentContentHighlightHeader = ({ enterpriseId }) => {
};
sendEnterpriseTrackEvent(
enterpriseId,
- `${EVENT_NAMES.CONTENT_HIGHLIGHTS.NEW_HIGHLIHT_MAX_REACHED}`,
+ `${EVENT_NAMES.CONTENT_HIGHLIGHTS.NEW_HIGHLIGHT_MAX_REACHED}`,
trackInfo,
);
} else {
diff --git a/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx b/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx
index f3186d9e75..dd981d5a4f 100644
--- a/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx
+++ b/src/components/learner-credit-management/AssignmentsTableRefreshAction.jsx
@@ -1,11 +1,37 @@
import React from 'react';
import { Button } from '@edx/paragon';
import PropTypes from 'prop-types';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
+import EVENT_NAMES from '../../eventTracking';
+import { applyFiltersToOptions, applySortByToOptions } from './data/hooks/useBudgetContentAssignments';
-const AssignmentsTableRefreshAction = ({ tableInstance, refresh }) => {
+const AssignmentsTableRefreshAction = ({ enterpriseId, tableInstance, refresh }) => {
const handleRefresh = () => {
const { state: dataTableState } = tableInstance;
refresh(dataTableState);
+
+ // Construct track event data with a identical syntax to useBudgetContentAssignments
+ const options = {
+ page: dataTableState.pageIndex + 1, // `DataTable` uses zero-indexed array
+ pageSize: dataTableState.pageSize,
+ };
+ applyFiltersToOptions(dataTableState.filters, options);
+ applySortByToOptions(dataTableState.sortBy, options);
+ const trackEventMetadata = {
+ filters: {
+ learnerState: options.learnerState || null,
+ search: options.search || null,
+ },
+ ordering: options.ordering || null,
+ page: options.page || null,
+ pageSize: options.pageSize || null,
+ };
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_DETAILS_ASSIGNED_DATATABLE_ACTIONS_REFRESH,
+ trackEventMetadata,
+ );
};
return (
@@ -19,10 +45,14 @@ const AssignmentsTableRefreshAction = ({ tableInstance, refresh }) => {
};
AssignmentsTableRefreshAction.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
refresh: PropTypes.func.isRequired,
tableInstance: PropTypes.shape({
state: PropTypes.shape(),
}),
};
-export default AssignmentsTableRefreshAction;
+const mapStateToProps = (state) => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+export default connect(mapStateToProps)(AssignmentsTableRefreshAction);
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index a747adaad5..9488d2f754 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -47,7 +47,6 @@ const BudgetAssignmentsTable = ({
number: count,
value: learnerState,
}));
-
return (
{
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
@@ -23,6 +24,7 @@ const BudgetDetailAssignments = ({
} = useBudgetContentAssignments({
isEnabled: isAssignableBudget && hasContentAssignments,
assignmentConfigurationUUID,
+ enterpriseId,
});
if (!isTopDownAssignmentEnabled || !isAssignableBudget) {
@@ -52,10 +54,12 @@ const BudgetDetailAssignments = ({
};
const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
});
BudgetDetailAssignments.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
hasContentAssignments: PropTypes.bool.isRequired,
hasSpentTransactions: PropTypes.bool.isRequired,
enterpriseFeatures: PropTypes.shape({
diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
index b8220c4768..280468ae81 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx
@@ -16,6 +16,7 @@ const MultipleBudgetsPicker = ({
enableLearnerPortal,
}) => {
const orderedOffers = orderOffers(offers);
+
return (
diff --git a/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx b/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx
index 3d6f074208..53559092e1 100644
--- a/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx
+++ b/src/components/learner-credit-management/NoBudgetActivityEmptyState.jsx
@@ -1,14 +1,18 @@
import React from 'react';
+import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
Button, Card, Row, Col,
} from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { Link } from 'react-router-dom';
+import { connect } from 'react-redux';
import { useIsLargeOrGreater, usePathToCatalogTab } from './data';
import nameYourLearners from './assets/nameYourLearners.svg';
import findTheRightCourse from './assets/findTheRightCourse.svg';
import confirmSpend from './assets/confirmSpend.svg';
+import EVENT_NAMES from '../../eventTracking';
const FindTheRightCourseIllustration = (props) => (
@@ -22,7 +26,7 @@ const ConfirmSpendIllustration = (props) => (
);
-const NoBudgetActivityEmptyState = () => {
+const NoBudgetActivityEmptyState = ({ enterpriseId }) => {
const pathToCatalogTab = usePathToCatalogTab();
const isLargeOrGreater = useIsLargeOrGreater();
@@ -84,7 +88,16 @@ const NoBudgetActivityEmptyState = () => {
- Get started
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.EMPTY_STATE_CTA,
+ )}
+ >
+ Get started
+
@@ -92,4 +105,12 @@ const NoBudgetActivityEmptyState = () => {
);
};
-export default NoBudgetActivityEmptyState;
+NoBudgetActivityEmptyState.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+};
+
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(NoBudgetActivityEmptyState);
diff --git a/src/components/learner-credit-management/cards/AssignmentAllocationHelpCollapsibles.jsx b/src/components/learner-credit-management/cards/AssignmentAllocationHelpCollapsibles.jsx
new file mode 100644
index 0000000000..2ac97635cb
--- /dev/null
+++ b/src/components/learner-credit-management/cards/AssignmentAllocationHelpCollapsibles.jsx
@@ -0,0 +1,98 @@
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { Collapsible, Stack } from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import React from 'react';
+import { ASSIGNMENT_ENROLLMENT_DEADLINE } from '../data';
+import EVENT_NAMES from '../../../eventTracking';
+
+const AssignmentAllocationHelpCollapsibles = ({ enterpriseId, course }) => (
+
+ Next steps for assigned learners}
+ defaultOpen
+ onToggle={(open) => {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.TOGGLE_NEXT_STEPS,
+ { isOpen: open },
+ );
+ }}
+ >
+
+
+
+ Learners will be notified of this course assignment by email.
+
+
+ Learners must complete enrollment for this assignment by {course.enrollmentDeadline}. This deadline
+ is calculated based on the course enrollment deadline or {ASSIGNMENT_ENROLLMENT_DEADLINE} days
+ past the date of assignment, whichever is sooner.
+
+
+
+
+ Impact on your Learner Credit budget}
+ onToggle={(open) => {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.TOGGLE_IMPACT_ON_YOUR_LEARNERS,
+ { isOpen: open },
+ );
+ }}
+ >
+
+
+
+ The total assignment cost will be earmarked as "assigned" funds in your
+ Learner Credit budget so you can't overspend.
+
+
+ The course cost will automatically convert from "assigned" to "spent" funds
+ when your learners complete registration.
+
+
+
+
+ Managing this assignment}
+ onToggle={(open) => {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.TOGGLE_MANAGING_THIS_ASSIGNMENT,
+ { isOpen: open },
+ );
+ }}
+ >
+
+
+
+ You will be able to monitor the status of this assignment by reviewing
+ your Learner Credit Budget activity.
+
+
+ You can cancel this course assignment or send email reminders any time
+ before learners complete enrollment.
+
+
+
+
+
+);
+
+AssignmentAllocationHelpCollapsibles.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+ course: PropTypes.shape({
+ enrollmentDeadline: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(AssignmentAllocationHelpCollapsibles);
diff --git a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
index 0dab3dc809..a02f63016b 100644
--- a/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
+++ b/src/components/learner-credit-management/cards/AssignmentModalContent.jsx
@@ -11,18 +11,20 @@ import {
Form,
Card,
} from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
import BaseCourseCard from './BaseCourseCard';
import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from '../data';
-import { ImpactOnYourLearnerCreditBudget, ManagingThisAssignment, NextStepsForAssignedLearners } from './Collapsibles';
import AssignmentModalSummary from './AssignmentModalSummary';
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY, isEmailAddressesInputValueValid } from './data';
+import AssignmentAllocationHelpCollapsibles from './AssignmentAllocationHelpCollapsibles';
+import EVENT_NAMES from '../../../eventTracking';
-const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
+const AssignmentModalContent = ({ enterpriseId, course, onEmailAddressesChange }) => {
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const spendAvailable = subsidyAccessPolicy.aggregates.spendAvailableUsd;
-
const [learnerEmails, setLearnerEmails] = useState([]);
const [emailAddressesInputValue, setEmailAddressesInputValue] = useState('');
const [assignmentAllocationMetadata, setAssignmentAllocationMetadata] = useState({});
@@ -61,12 +63,19 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
contentPrice,
});
setAssignmentAllocationMetadata(allocationMetadata);
+ if (allocationMetadata.validationError?.reason) {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.EMAIL_ADDRESS_VALIDATION,
+ { validationErrorReason: allocationMetadata.validationError.reason },
+ );
+ }
if (allocationMetadata.canAllocate) {
onEmailAddressesChange(learnerEmails, { canAllocate: true });
} else {
onEmailAddressesChange([]);
}
- }, [onEmailAddressesChange, learnerEmails, contentPrice, spendAvailable]);
+ }, [onEmailAddressesChange, learnerEmails, contentPrice, spendAvailable, enterpriseId]);
return (
@@ -100,11 +109,7 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
)}
How assigning this course works
-
-
-
-
-
+
Pay by Learner Credit
@@ -151,8 +156,13 @@ const AssignmentModalContent = ({ course, onEmailAddressesChange }) => {
};
AssignmentModalContent.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
course: PropTypes.shape().isRequired, // Pass-thru prop to `BaseCourseCard`
onEmailAddressesChange: PropTypes.func.isRequired,
};
-export default AssignmentModalContent;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(AssignmentModalContent);
diff --git a/src/components/learner-credit-management/cards/Collapsibles.jsx b/src/components/learner-credit-management/cards/Collapsibles.jsx
deleted file mode 100644
index 501b113dcf..0000000000
--- a/src/components/learner-credit-management/cards/Collapsibles.jsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Collapsible } from '@edx/paragon';
-
-import { ASSIGNMENT_ENROLLMENT_DEADLINE } from '../data';
-
-export const NextStepsForAssignedLearners = ({ course }) => (
- Next steps for assigned learners}
- defaultOpen
- >
-
-
-
- Learners will be notified of this course assignment by email.
-
-
- Learners must complete enrollment for this assignment by {course.enrollmentDeadline}. This deadline
- is calculated based on the course enrollment deadline or {ASSIGNMENT_ENROLLMENT_DEADLINE} days
- past the date of assignment, whichever is sooner.
-
-
-
-
-);
-
-NextStepsForAssignedLearners.propTypes = {
- course: PropTypes.shape({
- enrollmentDeadline: PropTypes.string.isRequired,
- }).isRequired,
-};
-
-export const ImpactOnYourLearnerCreditBudget = () => (
- Impact on your Learner Credit budget}
- >
-
-
-
- The total assignment cost will be earmarked as "assigned" funds in your
- Learner Credit budget so you can't overspend.
-
-
- The course cost will automatically convert from "assigned" to "spent" funds
- when your learners complete registration.
-
-
-
-
-);
-
-export const ManagingThisAssignment = () => (
- Managing this assignment}
- >
-
-
-
- You will be able to monitor the status of this assignment by reviewing
- your Learner Credit Budget activity.
-
-
- You can cancel this course assignment or send email reminders any time
- before learners complete enrollment.
-
-
-
-
-);
diff --git a/src/components/learner-credit-management/cards/CourseCard.jsx b/src/components/learner-credit-management/cards/CourseCard.jsx
index 2e1ec52cdb..90f78cc6b6 100644
--- a/src/components/learner-credit-management/cards/CourseCard.jsx
+++ b/src/components/learner-credit-management/cards/CourseCard.jsx
@@ -1,32 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Button, Hyperlink } from '@edx/paragon';
-import NewAssignmentModalButton from './NewAssignmentModalButton';
-import CARD_TEXT from '../constants';
import BaseCourseCard from './BaseCourseCard';
-
-const { BUTTON_ACTION } = CARD_TEXT;
-
-const CourseCardFooterActions = ({ course }) => {
- const { linkToCourse } = course;
-
- return [
-
- {BUTTON_ACTION.viewCourse}
- ,
-
- {BUTTON_ACTION.assign}
- ,
- ];
-};
+import CourseCardFooterActions from './CourseCardFooterActions';
const CourseCard = ({ original }) => (
{
+ const { linkToCourse, uuid } = course;
+ const handleViewCourse = () => {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.VIEW_COURSE,
+ { courseUUID: uuid },
+ );
+ };
+ return [
+
+ {BUTTON_ACTION.viewCourse}
+ ,
+
+ {BUTTON_ACTION.assign}
+ ,
+ ];
+};
+
+CourseCardFooterActions.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
+ course: PropTypes.shape().isRequired,
+};
+
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(CourseCardFooterActions);
diff --git a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
index 6bec89834a..dbf371d078 100644
--- a/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
+++ b/src/components/learner-credit-management/cards/CreateAllocationErrorAlertModals.jsx
@@ -1,11 +1,15 @@
import React, { useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useToggle } from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
import SystemErrorAlertModal from './assignment-allocation-status-modals/SystemErrorAlertModal';
import ContentNotInCatalogErrorAlertModal from './assignment-allocation-status-modals/ContentNotInCatalogErrorAlertModal';
import NotEnoughBalanceAlertModal from './assignment-allocation-status-modals/NotEnoughBalanceAlertModal';
+import EVENT_NAMES from '../../../eventTracking';
const CreateAllocationErrorAlertModals = ({
+ enterpriseId,
errorReason,
retry,
closeAssignmentModal,
@@ -24,6 +28,15 @@ const CreateAllocationErrorAlertModals = ({
});
}, [closeCatalogErrorModal, closeBalanceErrorModal, closeSystemErrorModal]);
+ const closeAssignmentModalWithTrackEvent = () => {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_ASSIGNMENT_ALLOCATION_ERROR,
+ { errorReason },
+ );
+ closeAssignmentModal();
+ };
+
/**
* Retry the original action that caused the error and close all error modals.
*/
@@ -57,18 +70,18 @@ const CreateAllocationErrorAlertModals = ({
>
@@ -76,9 +89,14 @@ const CreateAllocationErrorAlertModals = ({
};
CreateAllocationErrorAlertModals.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
closeAssignmentModal: PropTypes.func.isRequired,
retry: PropTypes.func.isRequired,
errorReason: PropTypes.string,
};
-export default CreateAllocationErrorAlertModals;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(CreateAllocationErrorAlertModals);
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 2dee3ff00a..353af471e3 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -9,23 +9,29 @@ import {
Hyperlink,
StatefulButton,
} from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { snakeCaseObject } from '@edx/frontend-platform/utils';
+import { camelCaseObject, snakeCaseObject } from '@edx/frontend-platform/utils';
+import { connect } from 'react-redux';
import AssignmentModalContent from './AssignmentModalContent';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys, useBudgetId } from '../data';
import CreateAllocationErrorAlertModals from './CreateAllocationErrorAlertModals';
import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
+import EVENT_NAMES from '../../../eventTracking';
const useAllocateContentAssignments = () => useMutation({
mutationFn: async ({
subsidyAccessPolicyId,
payload,
- }) => EnterpriseAccessApiService.allocateContentAssignments(subsidyAccessPolicyId, payload),
+ }) => {
+ const response = await EnterpriseAccessApiService.allocateContentAssignments(subsidyAccessPolicyId, payload);
+ return camelCaseObject(response.data);
+ },
});
-const NewAssignmentModalButton = ({ course, children }) => {
+const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
const history = useHistory();
const routeMatch = useRouteMatch();
const queryClient = useQueryClient();
@@ -41,6 +47,17 @@ const NewAssignmentModalButton = ({ course, children }) => {
const pathToActivityTab = generatePath(routeMatch.path, { budgetId: subsidyAccessPolicyId, activeTabKey: 'activity' });
+ const handleOpenAssignmentModal = () => {
+ open();
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_ASSIGN_COURSE,
+ {
+ isOpen: !isOpen,
+ courseUUID: course.uuid,
+ },
+ );
+ };
const handleCloseAssignmentModal = () => {
close();
setAssignButtonState('default');
@@ -57,6 +74,21 @@ const NewAssignmentModalButton = ({ course, children }) => {
setCanAllocateAssignments(canAllocate);
}, []);
+ const onSuccessEnterpriseTrackEvents = ({ created, noChange, updated }) => {
+ const trackEventMetadata = {
+ totalAllocatedLearners: learnerEmails.length,
+ created: created.length,
+ noChange: noChange.length,
+ updated: updated.length,
+ courseUUID: course.uuid,
+ };
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_ALLOCATION_LEARNER_ASSIGNMENT,
+ trackEventMetadata,
+ );
+ };
+
const handleAllocateContentAssignments = () => {
const payload = snakeCaseObject({
contentPriceCents: course.normalizedMetadata.contentPrice * 100, // Convert to USD cents
@@ -70,12 +102,13 @@ const NewAssignmentModalButton = ({ course, children }) => {
setAssignButtonState('pending');
setCreateAssignmentsErrorReason(null);
mutate(mutationArgs, {
- onSuccess: () => {
+ onSuccess: ({ created, noChange, updated }) => {
setAssignButtonState('complete');
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
});
handleCloseAssignmentModal();
+ onSuccessEnterpriseTrackEvents({ created, noChange, updated });
displayToastForAssignmentAllocation({ totalLearnersAssigned: learnerEmails.length });
history.push(pathToActivityTab);
},
@@ -84,32 +117,72 @@ const NewAssignmentModalButton = ({ course, children }) => {
httpErrorStatus,
httpErrorResponseData,
} = err.customAttributes;
+ let errorReason = 'system_error';
if (httpErrorStatus === 422) {
const responseData = JSON.parse(httpErrorResponseData);
- setCreateAssignmentsErrorReason(responseData[0].reason);
+ errorReason = responseData[0].reason;
+ setCreateAssignmentsErrorReason(errorReason);
} else {
- setCreateAssignmentsErrorReason('system_error');
+ setCreateAssignmentsErrorReason(errorReason);
}
setAssignButtonState('error');
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_ALLOCATION_ERROR,
+ {
+ totalAllocatedLearners: learnerEmails.length,
+ courseUUID: course.uuid,
+ errorStatus: httpErrorStatus,
+ errorReason,
+ },
+ );
},
});
};
return (
<>
- {children}
+ {children}
{
+ handleCloseAssignmentModal();
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_EXIT,
+ { assignButtonState },
+ );
+ }}
footerNode={(
-
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_HELP_CENTER,
+ )}
+ destination="https://edx.org"
+ target="_blank"
+ >
Help Center: Course Assignments
- Cancel
+ {
+ handleCloseAssignmentModal();
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_CANCEL,
+ { assignButtonState },
+ );
+ }}
+ >
+ Cancel
+
{
};
NewAssignmentModalButton.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
course: PropTypes.shape().isRequired, // Pass-thru prop to `BaseCourseCard`
children: PropTypes.node.isRequired, // Represents the button text
};
-export default NewAssignmentModalButton;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(NewAssignmentModalButton);
diff --git a/src/components/learner-credit-management/cards/CourseCard.test.jsx b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
similarity index 89%
rename from src/components/learner-credit-management/cards/CourseCard.test.jsx
rename to src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
index 00bf58ecc5..38d4d5267a 100644
--- a/src/components/learner-credit-management/cards/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
@@ -8,32 +8,37 @@ import configureMockStore from 'redux-mock-store';
import { QueryClientProvider, useQueryClient } from '@tanstack/react-query';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { renderWithRouter } from '@edx/frontend-enterprise-utils';
+import { renderWithRouter, sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-import CourseCard from './CourseCard';
+import CourseCard from '../CourseCard';
import {
formatPrice,
learnerCreditManagementQueryKeys,
useBudgetId,
useSubsidyAccessPolicy,
-} from '../data';
-import { getButtonElement, queryClient } from '../../test/testUtils';
+} from '../../data';
+import { getButtonElement, queryClient } from '../../../test/testUtils';
-import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
-import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
-import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from './data';
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import { BudgetDetailPageContext } from '../../BudgetDetailPageWrapper';
+import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY } from '../data';
+
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-utils'),
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
useQueryClient: jest.fn(),
}));
-jest.mock('../data', () => ({
- ...jest.requireActual('../data'),
+jest.mock('../../data', () => ({
+ ...jest.requireActual('../../data'),
useBudgetId: jest.fn(),
useSubsidyAccessPolicy: jest.fn(),
}));
-jest.mock('../../../data/services/EnterpriseAccessApiService');
+jest.mock('../../../../data/services/EnterpriseAccessApiService');
const originalData = {
availability: ['Upcoming'],
@@ -216,6 +221,45 @@ describe('Course card works as expected', () => {
expect(viewCourseCTA.href).toContain('https://enterprise.stage.edx.org/test-enterprise-slug/executive-education-2u/course/exec-ed-course-123x');
});
+ test('view course sends segment events', () => {
+ renderWithRouter( );
+ const viewCourseCTA = screen.getByText('View course', { selector: 'a' });
+ userEvent.click(viewCourseCTA);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ });
+
+ test('card exits and sends segment events', () => {
+ renderWithRouter( );
+
+ const assignCourseCTA = getButtonElement('Assign');
+ expect(assignCourseCTA).toBeInTheDocument();
+ userEvent.click(assignCourseCTA);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+
+ const assignmentModal = within(screen.getByRole('dialog'));
+ expect(assignmentModal.getByText('Assign this course')).toBeInTheDocument();
+
+ const closeButton = screen.getByRole('button', { name: 'Close' });
+ userEvent.click(closeButton);
+
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
+ });
+
+ test('help center article link sends segment events', () => {
+ renderWithRouter( );
+
+ const assignCourseCTA = getButtonElement('Assign');
+ expect(assignCourseCTA).toBeInTheDocument();
+ userEvent.click(assignCourseCTA);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+
+ const helpCenterButton = screen.getByText('Help Center: Course Assignments');
+
+ expect(helpCenterButton).toBeInTheDocument();
+ userEvent.click(helpCenterButton);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
+ });
+
test.each([
{
shouldSubmitAssignments: true,
@@ -346,15 +390,20 @@ describe('Course card works as expected', () => {
expect(assignmentModal.getByText('Learners will be notified of this course assignment by email.')).toBeInTheDocument();
const budgetImpact = assignmentModal.getByText('Impact on your Learner Credit budget');
expect(budgetImpact).toBeInTheDocument();
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
expect(assignmentModal.queryByText('The total assignment cost will be earmarked as "assigned" funds', { exact: false })).not.toBeInTheDocument();
userEvent.click(budgetImpact);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
expect(assignmentModal.getByText('The total assignment cost will be earmarked as "assigned" funds', { exact: false })).toBeInTheDocument();
const managingAssignment = assignmentModal.getByText('Managing this assignment');
expect(managingAssignment).toBeInTheDocument();
expect(assignmentModal.queryByText('You will be able to monitor the status of this assignment', { exact: false })).not.toBeInTheDocument();
userEvent.click(managingAssignment);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(3);
expect(assignmentModal.getByText('You will be able to monitor the status of this assignment', { exact: false })).toBeInTheDocument();
-
+ const nextSteps = assignmentModal.getByText('Next steps for assigned learners');
+ userEvent.click(nextSteps);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(4);
// Verify modal footer
expect(assignmentModal.getByText('Help Center: Course Assignments')).toBeInTheDocument();
const cancelAssignmentCTA = getButtonElement('Cancel', { screenOverride: assignmentModal });
@@ -426,8 +475,10 @@ describe('Course card works as expected', () => {
expect(assignmentErrorModal.getByText(errorModalTitle)).toBeInTheDocument();
if (shouldRetryAllocationAfterException) {
await simulateClickErrorModalTryAgain(errorModalTitle, assignmentErrorModal);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalled();
} else {
await simulateClickErrorModalExit(assignmentErrorModal);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalled();
}
}
} else {
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index f448267938..11dd744498 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -1,3 +1,20 @@
+/* START LOCAL TESTING CONSTANTS */
+// Set to false before pushing PR! otherwise set to true to enable local testing of learner-credit-management components
+// Test will fail as additional check to ensure this is set to false before pushing PR
+export const TEST_FLAG = false;
+// Test enterpriseCatalogUuid for learner-credit-management search
+// to display card selections and confirmation
+export const testEnterpriseCatalogUuid = 'e3107bf4-2eac-4307-a049-cc691ea7213b ';
+// function that passes through enterpriseCatalogUuid if TEST_FLAG is false, otherwise
+// returns local testing enterpriseCatalogUuid
+export const ENABLE_TESTING = (enterpriseCatalogUuid, enableTest = TEST_FLAG) => {
+ if (enableTest) {
+ return testEnterpriseCatalogUuid;
+ }
+ return enterpriseCatalogUuid;
+};
+/* END LOCAL TESTING CONSTANTS */
+
export const API_FIELDS_BY_TABLE_COLUMN_ACCESSOR = {
courseTitle: 'course_title',
enrollmentDate: 'enrollment_date',
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js b/src/components/learner-credit-management/data/hooks/tests/useBudgetContentAssignments.test.js
similarity index 71%
rename from src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
rename to src/components/learner-credit-management/data/hooks/tests/useBudgetContentAssignments.test.js
index 27d7aef092..f05fc7cfd3 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.test.js
+++ b/src/components/learner-credit-management/data/hooks/tests/useBudgetContentAssignments.test.js
@@ -1,13 +1,23 @@
import { renderHook } from '@testing-library/react-hooks';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-import useBudgetContentAssignments from './useBudgetContentAssignments';
-import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import useBudgetContentAssignments from '../useBudgetContentAssignments';
+import EnterpriseAccessApiService from '../../../../../data/services/EnterpriseAccessApiService';
+
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-utils'),
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
describe('useBudgetContentAssignments', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
it('does not call fetchContentAssignments if isEnabled is false', async () => {
const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
assignmentConfigurationUUID: '123',
isEnabled: false,
+ enterpriseId: 'test-enterprise-id',
}));
const { fetchContentAssignments } = result.current;
const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
@@ -34,6 +44,7 @@ describe('useBudgetContentAssignments', () => {
const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
assignmentConfigurationUUID: '123',
isEnabled: true,
+ enterpriseId: 'test-enterprise-id',
}));
const { fetchContentAssignments } = result.current;
const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
@@ -91,10 +102,13 @@ describe('useBudgetContentAssignments', () => {
],
hasSearchParam: false,
},
- ])('handles assignment details filter with search query parameter (%s)', async ({ filters, hasSearchParam }) => {
+ ])('handles assignment details filter with search query parameter (%s)', async ({
+ filters, hasSearchParam,
+ }) => {
const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
assignmentConfigurationUUID: '123',
isEnabled: true,
+ enterpriseId: 'test-enterprise-id',
}));
const { fetchContentAssignments } = result.current;
const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
@@ -118,7 +132,6 @@ describe('useBudgetContentAssignments', () => {
});
await waitForNextUpdate();
-
expect(mockListContentAssignments).toHaveBeenCalledWith(
'123',
{
@@ -152,6 +165,7 @@ describe('useBudgetContentAssignments', () => {
const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
assignmentConfigurationUUID: '123',
isEnabled: true,
+ enterpriseId: 'test-enterprise-id',
}));
const { fetchContentAssignments } = result.current;
const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
@@ -185,7 +199,6 @@ describe('useBudgetContentAssignments', () => {
},
);
});
-
it.each([
{
sortBy: [
@@ -247,6 +260,7 @@ describe('useBudgetContentAssignments', () => {
const { result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments({
assignmentConfigurationUUID: '123',
isEnabled: true,
+ enterpriseId: 'test-enterprise-id',
}));
const { fetchContentAssignments } = result.current;
const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
@@ -270,7 +284,6 @@ describe('useBudgetContentAssignments', () => {
});
await waitForNextUpdate();
-
expect(mockListContentAssignments).toHaveBeenCalledWith(
'123',
{
@@ -280,4 +293,65 @@ describe('useBudgetContentAssignments', () => {
},
);
});
+ it('calls enterprise track event', async () => {
+ const mockUseBudgetContentAssignmentsData = {
+ assignmentConfigurationUUID: '123',
+ isEnabled: true,
+ enterpriseId: 'test-enterprise-id',
+ };
+ const mockListContentAssignmentsData = {
+ data: {
+ results: [
+ {
+ id: 1,
+ name: 'test',
+ },
+ ],
+ count: 1,
+ numPages: 1,
+ currentPage: 1,
+ },
+ };
+ const initialSortByMetadata = {
+ id: 'amount',
+ desc: true,
+ };
+ const modifiedSortByMetaData = {
+ id: 'amount',
+ desc: false,
+ };
+
+ // Perform first render where currentArgsRef.current = null, no track event called
+ const { rerender, result, waitForNextUpdate } = renderHook(() => useBudgetContentAssignments(
+ mockUseBudgetContentAssignmentsData,
+ ));
+
+ const { fetchContentAssignments } = result.current;
+ const mockListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockListContentAssignments.mockResolvedValue(mockListContentAssignmentsData);
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ sortBy: [initialSortByMetadata],
+ });
+
+ await waitForNextUpdate();
+
+ expect(sendEnterpriseTrackEvent).not.toHaveBeenCalled();
+
+ // Performs a `rerender` of the first renderHook call after the currentArgsRef.current has been hydrated
+ rerender(mockUseBudgetContentAssignmentsData);
+
+ const mockSecondListContentAssignments = jest.spyOn(EnterpriseAccessApiService, 'listContentAssignments');
+ mockSecondListContentAssignments.mockResolvedValue(mockListContentAssignmentsData);
+ await fetchContentAssignments({
+ pageIndex: 0,
+ pageSize: 10,
+ sortBy: [modifiedSortByMetaData],
+ });
+
+ await waitForNextUpdate();
+
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ });
});
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx b/src/components/learner-credit-management/data/hooks/tests/useBudgetDetailActivityOverview.test.jsx
similarity index 89%
rename from src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
rename to src/components/learner-credit-management/data/hooks/tests/useBudgetDetailActivityOverview.test.jsx
index 351f8b5cf4..ebe6ad7b9e 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetDetailActivityOverview.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/tests/useBudgetDetailActivityOverview.test.jsx
@@ -1,22 +1,22 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
-import useBudgetDetailActivityOverview from './useBudgetDetailActivityOverview';
-import useBudgetId from './useBudgetId';
-import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
-import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
-import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import useBudgetDetailActivityOverview from '../useBudgetDetailActivityOverview';
+import useBudgetId from '../useBudgetId';
+import useSubsidyAccessPolicy from '../useSubsidyAccessPolicy';
+import EnterpriseAccessApiService from '../../../../../data/services/EnterpriseAccessApiService';
+import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
import {
mockAssignableSubsidyAccessPolicy,
mockPerLearnerSpendLimitSubsidyAccessPolicy,
mockEnterpriseOfferId,
mockSubsidyAccessPolicyUUID,
-} from '../tests/constants';
-import { queryClient } from '../../../test/testUtils';
-import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
+} from '../../tests/constants';
+import { queryClient } from '../../../../test/testUtils';
+import SubsidyApiService from '../../../../../data/services/EnterpriseSubsidyApiService';
-jest.mock('./useBudgetId');
-jest.mock('./useSubsidyAccessPolicy');
+jest.mock('../useBudgetId');
+jest.mock('../useSubsidyAccessPolicy');
const mockEnterpriseUUID = 'mock-enterprise-uuid';
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx b/src/components/learner-credit-management/data/hooks/tests/useOfferRedemptions.test.jsx
similarity index 88%
rename from src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
rename to src/components/learner-credit-management/data/hooks/tests/useOfferRedemptions.test.jsx
index 0738b5bf1a..5e8d29a495 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/tests/useOfferRedemptions.test.jsx
@@ -2,11 +2,11 @@ import { QueryClientProvider } from '@tanstack/react-query';
import { act, renderHook } from '@testing-library/react-hooks/dom';
import { camelCaseObject } from '@edx/frontend-platform/utils';
-import useOfferRedemptions from './useOfferRedemptions';
-import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
-import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
-import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
-import { queryClient } from '../../../test/testUtils';
+import useOfferRedemptions from '../useOfferRedemptions';
+import useSubsidyAccessPolicy from '../useSubsidyAccessPolicy';
+import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
+import SubsidyApiService from '../../../../../data/services/EnterpriseSubsidyApiService';
+import { queryClient } from '../../../../test/testUtils';
const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
const TEST_ENTERPRISE_OFFER_ID = 1;
@@ -49,9 +49,9 @@ const mockEnterpriseOffer = {
id: TEST_ENTERPRISE_OFFER_ID,
};
-jest.mock('./useSubsidyAccessPolicy');
-jest.mock('../../../../data/services/EnterpriseDataApiService');
-jest.mock('../../../../data/services/EnterpriseSubsidyApiService');
+jest.mock('../useSubsidyAccessPolicy');
+jest.mock('../../../../../data/services/EnterpriseDataApiService');
+jest.mock('../../../../../data/services/EnterpriseSubsidyApiService');
const wrapper = ({ children }) => (
{children}
diff --git a/src/components/learner-credit-management/data/hooks/useOfferSummary.test.js b/src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js
similarity index 89%
rename from src/components/learner-credit-management/data/hooks/useOfferSummary.test.js
rename to src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js
index 352b104f6b..40ccc6df01 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferSummary.test.js
+++ b/src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js
@@ -1,14 +1,14 @@
import { renderHook } from '@testing-library/react-hooks/dom';
-import useOfferSummary from './useOfferSummary';
-import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import useOfferSummary from '../useOfferSummary';
+import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
jest.mock('@edx/frontend-platform/config', () => ({
getConfig: jest.fn(() => ({
FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
})),
}));
-jest.mock('../../../../data/services/EnterpriseDataApiService');
+jest.mock('../../../../../data/services/EnterpriseDataApiService');
const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
const TEST_ENTERPRISE_OFFER_ID = 1;
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx b/src/components/learner-credit-management/data/hooks/tests/useSubsidyAccessPolicy.test.jsx
similarity index 92%
rename from src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
rename to src/components/learner-credit-management/data/hooks/tests/useSubsidyAccessPolicy.test.jsx
index cc773fcb79..1d654bd428 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/tests/useSubsidyAccessPolicy.test.jsx
@@ -1,15 +1,15 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
-import useSubsidyAccessPolicy from './useSubsidyAccessPolicy'; // Import the hook
-import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
-import { queryClient } from '../../../test/testUtils';
+import useSubsidyAccessPolicy from '../useSubsidyAccessPolicy'; // Import the hook
+import EnterpriseAccessApiService from '../../../../../data/services/EnterpriseAccessApiService';
+import { queryClient } from '../../../../test/testUtils';
const mockSubsidyAccessPolicyUUID = '9af340a9-48de-4d94-976d-e2282b9eb7f3';
const mockAssignmentConfiguration = { uuid: 'test-assignment-configuration-uuid' };
// Mock the EnterpriseAccessApiService
-jest.mock('../../../../data/services/EnterpriseAccessApiService', () => ({
+jest.mock('../../../../../data/services/EnterpriseAccessApiService', () => ({
retrieveSubsidyAccessPolicy: jest.fn().mockResolvedValue({
data: {
uuid: '9af340a9-48de-4d94-976d-e2282b9eb7f3',
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
index 8bd33e9a07..ff9c15d22d 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useBudgetContentAssignments.js
@@ -1,8 +1,12 @@
-import { useCallback, useMemo, useState } from 'react';
+import {
+ useCallback, useMemo, useState, useRef,
+} from 'react';
import debounce from 'lodash.debounce';
import { camelCaseObject } from '@edx/frontend-platform/utils';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import EVENT_NAMES from '../../../../eventTracking';
const initialContentAssignmentsState = {
results: [],
@@ -12,7 +16,7 @@ const initialContentAssignmentsState = {
learnerStateCounts: [],
};
-const applyFiltersToOptions = (filters, options) => {
+export const applyFiltersToOptions = (filters, options) => {
if (!filters || filters.length === 0) {
return;
}
@@ -26,7 +30,7 @@ const applyFiltersToOptions = (filters, options) => {
}
};
-const applySortByToOptions = (sortBy, options) => {
+export const applySortByToOptions = (sortBy, options) => {
if (!sortBy || sortBy.length === 0) {
return;
}
@@ -44,7 +48,7 @@ const applySortByToOptions = (sortBy, options) => {
const apiFieldKey = apiFieldForColumnAccessor.key;
// Determine whether the API field ordering should be reversed based on the column accessor. This is
// necessary because the content_quantity field is a negative number, but if the column is sorted in a
- // descending order, users would likely expect the larger contenr quantity to be at the top of the list,
+ // descending order, users would likely expect the larger content quantity to be at the top of the list,
// which is technically the smaller number since its negative.
if (isApiFieldOrderingReversed) {
return desc ? apiFieldKey : `-${apiFieldKey}`;
@@ -59,10 +63,11 @@ const applySortByToOptions = (sortBy, options) => {
const useBudgetContentAssignments = ({
assignmentConfigurationUUID,
isEnabled,
+ enterpriseId,
}) => {
+ const currentArgsRef = useRef(null);
const [isLoading, setIsLoading] = useState(true);
const [contentAssignments, setContentAssignments] = useState(initialContentAssignmentsState);
-
const fetchContentAssignments = useCallback((args) => {
if (!isEnabled || !assignmentConfigurationUUID) {
setIsLoading(false);
@@ -76,15 +81,45 @@ const useBudgetContentAssignments = ({
};
applyFiltersToOptions(args.filters, options);
applySortByToOptions(args.sortBy, options);
+
+ /* This logic in conjunction with useRef is being used to prevent track events
+ from being called when the page re-renders without the specifically selected
+ arguments (argCopy) being changed */
+ const argsCopy = {
+ pageIndex: args.pageIndex,
+ pageSize: args.pageSize,
+ filters: args.filters,
+ sortBy: args.sortBy,
+ };
+ const shouldEmitSegmentEvent = !!currentArgsRef.current && (
+ JSON.stringify(argsCopy) !== JSON.stringify(currentArgsRef.current));
+ if (shouldEmitSegmentEvent) {
+ const trackEventMetadata = {
+ filters: {
+ learnerState: options.learnerState || null,
+ search: options.search || null,
+ },
+ ordering: options.ordering || null,
+ page: options.page || null,
+ pageSize: options.pageSize || null,
+ };
+ await sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_DETAILS_ASSIGNED_DATATABLE_SORT_BY_OR_FILTER,
+ trackEventMetadata,
+ );
+ }
const assignmentsResponse = await EnterpriseAccessApiService.listContentAssignments(
assignmentConfigurationUUID,
options,
);
setContentAssignments(camelCaseObject(assignmentsResponse.data));
setIsLoading(false);
+ // Memoizes argsCopy to be referenced against future re-renders
+ currentArgsRef.current = argsCopy;
};
getContentAssignments();
- }, [isEnabled, assignmentConfigurationUUID]);
+ }, [isEnabled, assignmentConfigurationUUID, enterpriseId]);
const debouncedFetchContentAssigments = useMemo(
() => debounce(fetchContentAssignments, 300),
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
index 2898577a0a..64618cae8b 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
+++ b/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
@@ -14,6 +14,7 @@ import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiSer
import { API_FIELDS_BY_TABLE_COLUMN_ACCESSOR } from '../constants';
import { transformUtilizationTableResults, transformUtilizationTableSubsidyTransactionResults } from '../utils';
import useSubsidyAccessPolicy from './useSubsidyAccessPolicy';
+import EVENT_NAMES from '../../../../eventTracking';
const applySortByToOptions = (sortBy, options) => {
const orderingStrings = sortBy.map(({ id, desc }) => {
@@ -113,7 +114,7 @@ const useOfferRedemptions = (
// send all table state as event properties.
sendEnterpriseTrackEvent(
enterpriseUUID,
- 'edx.ui.enterprise.admin_portal.learner-credit-management.table.data.changed',
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_DETAILS_SPENT_DATATABLE_SORT_BY_OR_FILTER,
options,
);
} else {
diff --git a/src/components/learner-credit-management/data/tests/constants.test.js b/src/components/learner-credit-management/data/tests/constants.test.js
new file mode 100644
index 0000000000..9c5faf8131
--- /dev/null
+++ b/src/components/learner-credit-management/data/tests/constants.test.js
@@ -0,0 +1,20 @@
+import {
+ TEST_FLAG,
+ ENABLE_TESTING,
+ testEnterpriseCatalogUuid,
+} from '../constants';
+
+const enterpriseCatalogUuid = 'test-enterprise-catalogUuid';
+
+describe('constants', () => {
+ it('should be defined', () => {
+ expect(testEnterpriseCatalogUuid).toBeDefined();
+ });
+ it('ENABLE_TESTING should pass through when the TEST_FLAG = false', () => {
+ expect(TEST_FLAG).toBe(false);
+ expect(ENABLE_TESTING(enterpriseCatalogUuid)).toBe(enterpriseCatalogUuid);
+ });
+ it('ENABLE_TESTING should return the testEnterpriseId when passing true parameter', () => {
+ expect(ENABLE_TESTING(testEnterpriseCatalogUuid, true)).toBe(testEnterpriseCatalogUuid);
+ });
+});
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
index bde723051e..a557a66dc3 100644
--- a/src/components/learner-credit-management/search/CatalogSearch.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -7,7 +7,9 @@ import { SearchHeader } from '@edx/frontend-enterprise-catalog-search';
import { configuration } from '../../../config';
import CatalogSearchResults from './CatalogSearchResults';
-import { SEARCH_RESULT_PAGE_SIZE, useBudgetId, useSubsidyAccessPolicy } from '../data';
+import {
+ ENABLE_TESTING, SEARCH_RESULT_PAGE_SIZE, useBudgetId, useSubsidyAccessPolicy,
+} from '../data';
const CatalogSearch = () => {
const searchClient = algoliasearch(configuration.ALGOLIA.APP_ID, configuration.ALGOLIA.SEARCH_API_KEY);
@@ -15,9 +17,8 @@ const CatalogSearch = () => {
const {
data: subsidyAccessPolicy,
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
- const searchFilters = `enterprise_catalog_uuids:${subsidyAccessPolicy.catalogUuid} AND content_type:course`;
+ const searchFilters = `enterprise_catalog_uuids:${ENABLE_TESTING(subsidyAccessPolicy.catalogUuid)} AND content_type:course`;
const displayName = subsidyAccessPolicy.displayName ? `${subsidyAccessPolicy.displayName} catalog` : 'Overview';
-
return (
);
}
-
return (
({
...jest.requireActual('react-instantsearch-dom'),
@@ -18,7 +18,7 @@ jest.mock('react-instantsearch-dom', () => ({
Index: () => SEARCH
,
}));
-jest.mock('../data');
+jest.mock('../../data');
const DEFAULT_SEARCH_CONTEXT_VALUE = { refinements: {} };
diff --git a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
similarity index 83%
rename from src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
rename to src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
index 0143e8d5ba..60107dbfa5 100644
--- a/src/components/learner-credit-management/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
@@ -9,10 +9,10 @@ import { IntlProvider } from '@edx/frontend-platform/i18n';
import { renderWithRouter } from '@edx/frontend-enterprise-utils';
import { QueryClientProvider } from '@tanstack/react-query';
-import { BaseCatalogSearchResults } from '../search/CatalogSearchResults';
-import { CONTENT_TYPE_COURSE } from '../data/constants';
-import { queryClient } from '../../test/testUtils';
-import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
+import { BaseCatalogSearchResults } from '../CatalogSearchResults';
+import { CONTENT_TYPE_COURSE } from '../../data/constants';
+import { queryClient } from '../../../test/testUtils';
+import { BudgetDetailPageContext } from '../../BudgetDetailPageWrapper';
// Mocking this connected component so as not to have to mock the algolia Api
const PAGINATE_ME = 'PAGINATE ME :)';
@@ -25,8 +25,8 @@ jest.mock('react-instantsearch-dom', () => ({
Index: () => Popular Courses
,
}));
-jest.mock('../data', () => ({
- ...jest.requireActual('../data'),
+jest.mock('../../data', () => ({
+ ...jest.requireActual('../../data'),
useSubsidyAccessPolicy: jest.fn().mockReturnValue({
data: {
uuid: 'test-uuid',
@@ -183,11 +183,35 @@ describe('Main Catalogs view works as expected', () => {
- ,
,
);
expect(screen.queryByText(TEST_COURSE_NAME)).toBeInTheDocument();
expect(screen.queryByText(TEST_COURSE_NAME_2)).toBeInTheDocument();
expect(screen.getAllByText('Showing 2 of 2.')[0]).toBeInTheDocument();
});
+ test('error state displays', async () => {
+ const budgetDetailPageContextValue = {
+ isSuccessfulAssignmentAllocationToastOpen: false,
+ totalLearnersAssigned: undefined,
+ displayToastForAssignmentAllocation: jest.fn(),
+ closeToastForAssignmentAllocation: jest.fn(),
+ };
+ const error = {
+ message: 'Your test has Failed',
+ };
+ const errorState = {
+ ...defaultProps,
+ error,
+ };
+ renderWithRouter(
+
+
+
+
+
+
+ ,
+ );
+ expect(screen.getByText(error.message, { exact: false })).toBeInTheDocument();
+ });
});
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 41c4ffc760..a309d59968 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -8,7 +8,7 @@ import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { renderWithRouter } from '@edx/frontend-enterprise-utils';
+import { renderWithRouter, sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { act } from 'react-dom/test-utils';
import BudgetDetailPage from '../BudgetDetailPage';
@@ -32,6 +32,11 @@ import {
} from '../data/tests/constants';
import { getButtonElement, queryClient } from '../../test/testUtils';
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-utils'),
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
+
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: jest.fn(),
@@ -205,7 +210,7 @@ describe(' ', () => {
it.each([
{ isLargeViewport: true },
{ isLargeViewport: false },
- ])('displays budget activity overview empty state', ({ isLargeViewport }) => {
+ ])('displays budget activity overview empty state', async ({ isLargeViewport }) => {
useIsLargeOrGreater.mockReturnValue(isLargeViewport);
useParams.mockReturnValue({
budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
@@ -226,6 +231,8 @@ describe(' ', () => {
const illustrationTestIds = ['find-the-right-course-illustration', 'name-your-learners-illustration', 'confirm-spend-illustration'];
illustrationTestIds.forEach(testId => expect(screen.getByTestId(testId)).toBeInTheDocument());
expect(screen.getByText('Get started', { selector: 'a' })).toBeInTheDocument();
+ userEvent.click(screen.getByText('Get started'));
+ await waitFor(() => expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1));
});
it.each([
@@ -404,6 +411,7 @@ describe(' ', () => {
userEvent.click(refreshCTA);
expect(mockFetchContentAssignments).toHaveBeenCalledTimes(2); // should be called again on refresh
expect(mockFetchContentAssignments).toHaveBeenLastCalledWith(expect.objectContaining(expectedTableFetchDataArgs));
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
});
it.each([
diff --git a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
index 692405c4ed..bb4b0f31ea 100644
--- a/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
+++ b/src/components/learner-credit-management/tests/LearnerCreditAllocationTable.test.jsx
@@ -138,4 +138,21 @@ describe(' ', () => {
const courseTitleElement = screen.queryByText('course-title');
expect(courseTitleElement.closest('a')).toBeNull();
});
+ it('displays isLoading state component', () => {
+ const store = mockStore({
+ portalConfiguration: {
+ enterpriseId: 'test-enterprise-id',
+ enterpriseSlug: 'test-enterprise-slug',
+ enableLearnerPortal: false,
+ },
+ });
+ const props = {
+ isLoading: true,
+ tableData: {
+ results: [],
+ },
+ };
+ render( );
+ expect(screen.getByText('loading')).toBeTruthy();
+ });
});
diff --git a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
index 09d2f29192..701e12dbb3 100644
--- a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
+++ b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
@@ -66,4 +66,16 @@ describe(' ', () => {
render( );
expect(screen.getByText('Budgets'));
});
+ it('Shows loading spinner', () => {
+ const enterpriseSubsidiesContextValue = {
+ ...defaultEnterpriseSubsidiesContextValue,
+ isLoading: true,
+ };
+ render( );
+ expect(screen.getByText('Loading...')).toBeInTheDocument();
+ });
});
diff --git a/src/eventTracking.js b/src/eventTracking.js
index f126e15c6e..a2f04f1cbc 100644
--- a/src/eventTracking.js
+++ b/src/eventTracking.js
@@ -18,12 +18,22 @@ const SETTINGS_PREFIX = `${PROJECT_NAME}.settings`;
const CONTENT_HIGHLIGHTS_PREFIX = `${PROJECT_NAME}.content_highlights`;
const LEARNER_CREDIT_MANAGEMENT_PREFIX = `${PROJECT_NAME}.learner_credit_management`;
+// Sub-prefixes
+// Subscriptions
const SUBSCRIPTION_TABLE_PREFIX = `${SUBSCRIPTION_PREFIX}.table`;
+
+// ContentHighlights
const CONTENT_HIGHLIGHT_STEPPER_BASE_PREFIX = `${CONTENT_HIGHLIGHTS_PREFIX}.stepper`;
const CONTENT_HIGHLIGHTS_STEPPER_STEP_PREFIX = `${CONTENT_HIGHLIGHT_STEPPER_BASE_PREFIX}_step`;
const CONTENT_HIGHLIGHTS_DASHBOARD_PREFIX = `${CONTENT_HIGHLIGHTS_PREFIX}.dashboard`;
const CONTENT_HIGHLIGHTS_DELETE_CONTENT_PREFIX = `${CONTENT_HIGHLIGHTS_PREFIX}.delete_content_highlight`;
+// learner-credit-management
+const BUDGET_DETAIL_ACTIVITY_TAB_PREFIX = `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.activity`;
+const BUDGET_DETAIL_CATALOG_TAB_PREFIX = `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.catalog`;
+const BUDGET_DETAIL_SEARCH_PREFIX = `${BUDGET_DETAIL_CATALOG_TAB_PREFIX}.search`;
+const BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX = `${BUDGET_DETAIL_CATALOG_TAB_PREFIX}.assignment_modal`;
+
export const SUBSCRIPTION_TABLE_EVENTS = {
// Pagination
PAGINATION_NEXT: `${SUBSCRIPTION_TABLE_PREFIX}.pagination.next.clicked`,
@@ -75,7 +85,7 @@ export const CONTENT_HIGHLIGHTS_EVENTS = {
HIGHLIGHT_DASHBOARD_SET_CATALOG_VISIBILITY: `${CONTENT_HIGHLIGHTS_DASHBOARD_PREFIX}.set_catalog_visibility.clicked`,
HIGHLIGHT_DASHBOARD_SELECT_TAB: `${CONTENT_HIGHLIGHTS_DASHBOARD_PREFIX}.tab.clicked`,
// Highlight Creation
- NEW_HIGHLIHT_MAX_REACHED: `${CONTENT_HIGHLIGHTS_DASHBOARD_PREFIX}.create_new_content_highlight.max_reached.clicked`,
+ NEW_HIGHLIGHT_MAX_REACHED: `${CONTENT_HIGHLIGHTS_DASHBOARD_PREFIX}.create_new_content_highlight.max_reached.clicked`,
NEW_HIGHLIGHT: `${CONTENT_HIGHLIGHTS_DASHBOARD_PREFIX}.create_new_content_highlight.clicked`,
};
@@ -93,7 +103,27 @@ export const SUBSCRIPTION_EVENTS = {
};
export const LEARNER_CREDIT_MANAGEMENT_EVENTS = {
- TAB_CHANGED: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget-detail.tab.changed`,
+ TAB_CHANGED: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.tab.changed`,
+ // Activity tab
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_SORT_BY_OR_FILTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table.changed`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_ACTIONS_REFRESH: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_refresh.clicked`,
+ BUDGET_DETAILS_SPENT_DATATABLE_SORT_BY_OR_FILTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.spent_table.changed`,
+ EMPTY_STATE_CTA: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.empty_state_cta_to_catalog.clicked`,
+ // Catalog tab
+ // Catalog tab search
+ VIEW_COURSE: `${BUDGET_DETAIL_SEARCH_PREFIX}.view_course.clicked`,
+ ASSIGN_COURSE: `${BUDGET_DETAIL_SEARCH_PREFIX}.assign_course_cta.clicked`,
+ // Catalog tab - Assignment Modal
+ TOGGLE_NEXT_STEPS: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.next_steps_collapsible.toggled`,
+ TOGGLE_IMPACT_ON_YOUR_LEARNERS: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.impact_on_your_learners_collapsible.toggled`,
+ TOGGLE_MANAGING_THIS_ASSIGNMENT: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.managing_this_assignment_collapsible.toggled`,
+ ASSIGNMENT_MODAL_CANCEL: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.close_modal_cancel.clicked`,
+ ASSIGNMENT_MODAL_EXIT: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.close_modal_exit.clicked`,
+ ASSIGNMENT_MODAL_ASSIGNMENT_ALLOCATION_ERROR: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.exit_assignment_allocation_modal.clicked`,
+ ASSIGNMENT_MODAL_HELP_CENTER: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.help_center_article_course_assignments.clicked`,
+ ASSIGNMENT_ALLOCATION_LEARNER_ASSIGNMENT: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.assignment_allocation.assigned`,
+ EMAIL_ADDRESS_VALIDATION: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.email_validation.changed`,
+ ASSIGNMENT_ALLOCATION_ERROR: `${BUDGET_DETAIL_ASSIGNMENT_MODAL_PREFIX}.assignment_allocation.errored`,
};
const EVENT_NAMES = {
From 10811a7ac559b4d072d2c629c5c9e99716c8b84f Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Wed, 29 Nov 2023 10:17:25 -0500
Subject: [PATCH 080/124] fix: update help text copy above budget detail tables
to reflect Figma (#1105)
---
src/components/App/index.jsx | 4 +-
.../BudgetDetailRedemptions.jsx | 9 ++++-
.../tests/BudgetDetailPage.test.jsx | 37 ++++++++++++++++++-
3 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/src/components/App/index.jsx b/src/components/App/index.jsx
index ee1afbbf11..e56484d078 100644
--- a/src/components/App/index.jsx
+++ b/src/components/App/index.jsx
@@ -29,12 +29,12 @@ const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: defaultQueryClientRetryHandler,
- // Specifying a longer `staleTime` of 20 seconds means queries will not refetch their data
+ // Specifying a longer `staleTime` of 60 seconds means queries will not refetch their data
// as often; mitigates making duplicate queries when within the `staleTime` window, instead
// relying on the cached data until the `staleTime` window has exceeded. This may be modified
// per-query, as needed, if certain queries expect to be more up-to-date than others. Allows
// `useQuery` to be used as a state manager.
- staleTime: 1000 * 20,
+ staleTime: 1000 * 60,
},
},
});
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index 0ea2fb766a..a9e114744f 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -21,8 +21,13 @@ const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
Spent
- Spent activity is driven by completed enrollments. Enrollment data is automatically updated every 12 hours.
- Come back later to view more recent enrollments.
+ Spent activity is driven by completed enrollments.
+ {(enterpriseOfferId || (subsidyAccessPolicyId && !enterpriseFeatures.topDownAssignmentRealTimeLcm)) && (
+ <>
+ Enrollment data is automatically updated every 12 hours.
+ Come back later to view more recent enrollments.
+ >
+ )}
', () => {
it.each([
{
budgetId: mockEnterpriseOfferId,
+ isTopDownAssignmentEnabled: true,
expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null, true],
},
+ {
+ budgetId: mockEnterpriseOfferId,
+ isTopDownAssignmentEnabled: false,
+ expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null, false],
+ },
{
budgetId: mockSubsidyAccessPolicyUUID,
+ isTopDownAssignmentEnabled: true,
expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID, true],
},
+ {
+ budgetId: mockSubsidyAccessPolicyUUID,
+ isTopDownAssignmentEnabled: false,
+ expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID, false],
+ },
])('displays spend table in "Activity" tab with empty results (%s)', async ({
budgetId,
+ isTopDownAssignmentEnabled,
expectedUseOfferRedemptionsArgs,
}) => {
useParams.mockReturnValue({
@@ -277,7 +290,17 @@ describe(' ', () => {
offerRedemptions: mockEmptyOfferRedemptions,
fetchOfferRedemptions: jest.fn(),
});
- renderWithRouter( );
+ const storeState = {
+ ...initialStoreState,
+ portalConfiguration: {
+ ...initialStoreState.portalConfiguration,
+ enterpriseFeatures: {
+ ...initialStoreState.portalConfiguration.enterpriseFeatures,
+ topDownAssignmentRealTimeLcm: isTopDownAssignmentEnabled,
+ },
+ },
+ };
+ renderWithRouter( );
expect(useOfferRedemptions).toHaveBeenCalledTimes(1);
expect(useOfferRedemptions).toHaveBeenCalledWith(...expectedUseOfferRedemptionsArgs);
@@ -287,9 +310,19 @@ describe(' ', () => {
// Catalog tab does NOT exist since the budget is not assignable
expect(screen.queryByText('Catalog')).not.toBeInTheDocument();
- // Spent table is visible within Activity tab contents
+ // Spent table and messaging is visible within Activity tab contents
const spentSection = within(screen.getByText('Spent').closest('section'));
expect(spentSection.getByText('No results found')).toBeInTheDocument();
+ expect(spentSection.getByText('Spent activity is driven by completed enrollments.', { exact: false })).toBeInTheDocument();
+ const isSubsidyAccessPolicyWithAnalyicsApi = (
+ budgetId === mockSubsidyAccessPolicyUUID && !isTopDownAssignmentEnabled
+ );
+ if (budgetId === mockEnterpriseOfferId || isSubsidyAccessPolicyWithAnalyicsApi) {
+ // This copy is only present when the "Spent" table is backed by the
+ // analytics API (i.e., budget is an enterprise offer or a subsidy access
+ // policy with the LC2 feature flag disabled).
+ expect(spentSection.getByText('Enrollment data is automatically updated every 12 hours.', { exact: false })).toBeInTheDocument();
+ }
});
it('renders with assigned table empty state with spent table and catalog tab available for assignable budgets', async () => {
From 629abf01581c7361f48f4e3876602656ddcbec74 Mon Sep 17 00:00:00 2001
From: edX requirements bot
<49161187+edx-requirements-bot@users.noreply.github.com>
Date: Wed, 29 Nov 2023 10:35:07 -0500
Subject: [PATCH 081/124] chore: update browserslist DB (#964)
Co-authored-by: abdullahwaheed
Co-authored-by: Adam Stankiewicz
---
package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 86dab080e7..2d6e45a74f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8439,9 +8439,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001495",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz",
- "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==",
+ "version": "1.0.30001564",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001564.tgz",
+ "integrity": "sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==",
"funding": [
{
"type": "opencollective",
From 66bcc8524008ca9801790f26fb6578b1a205b35c Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Tue, 28 Nov 2023 19:31:39 +0000
Subject: [PATCH 082/124] fix: adding stepper opening functionality to no sso
card button
---
.../settings/SettingsSSOTab/NoSSOCard.jsx | 4 +++-
src/components/settings/SettingsSSOTab/index.jsx | 16 ++++++++++++++--
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/NoSSOCard.jsx b/src/components/settings/SettingsSSOTab/NoSSOCard.jsx
index 0cad49a922..a8189d2d23 100644
--- a/src/components/settings/SettingsSSOTab/NoSSOCard.jsx
+++ b/src/components/settings/SettingsSSOTab/NoSSOCard.jsx
@@ -6,10 +6,11 @@ import { Add } from '@edx/paragon/icons';
import cardImage from '../../../data/images/NoSSO.svg';
const NoSSOCard = ({
- setShowNoSSOCard, setShowNewSSOForm,
+ setShowNoSSOCard, setShowNewSSOForm, setIsStepperOpen,
}) => {
const onClick = () => {
setShowNoSSOCard(false);
+ setIsStepperOpen(true);
setShowNewSSOForm(true);
};
@@ -38,6 +39,7 @@ const NoSSOCard = ({
NoSSOCard.propTypes = {
setShowNoSSOCard: PropTypes.func.isRequired,
setShowNewSSOForm: PropTypes.func.isRequired,
+ setIsStepperOpen: PropTypes.func.isRequired,
};
export default NoSSOCard;
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index 52ac8817ec..4fddf1263a 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -135,7 +135,11 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
)}
{/* Nothing found so guide user to creation/edit form */}
{showNoSSOCard && (
-
+
)}
{/* Since we found a selected providerConfig we know we are in editing mode and can safely
render the create/edit form */}
@@ -193,7 +197,15 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
/>
)}
{/* Nothing found so guide user to creation/edit form */}
- {showNoSSOCard && }
+ {/* Because NoSSOCard is shared component between old and new sso steppers, setIsStepperOpen is a placeholder
+ for now. This whole tree is scheduled to be deleted soon */}
+ {showNoSSOCard && (
+ { }}
+ />
+ )}
{/* Since we found a selected providerConfig we know we are in editing mode and can safely
render the create/edit form */}
{((existingConfigs?.length > 0 && providerConfig !== null) || showNewSSOForm) && (
From a193f0f3fd44af90596a8921a030f18ea03dfbdb Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Wed, 29 Nov 2023 18:29:36 -0500
Subject: [PATCH 083/124] fix: temporarily patch @edx/paragon to have a correct
select all count in DataTables (#1104)
---
docs/decisions/0007-patch-package.rst | 42 +++
package-lock.json | 266 +++++++++++++++++-
package.json | 2 +
patches/@edx+paragon+20.46.3.patch | 29 ++
.../tests/BudgetDetailPage.test.jsx | 42 ++-
5 files changed, 364 insertions(+), 17 deletions(-)
create mode 100644 docs/decisions/0007-patch-package.rst
create mode 100644 patches/@edx+paragon+20.46.3.patch
diff --git a/docs/decisions/0007-patch-package.rst b/docs/decisions/0007-patch-package.rst
new file mode 100644
index 0000000000..9f3527e1c8
--- /dev/null
+++ b/docs/decisions/0007-patch-package.rst
@@ -0,0 +1,42 @@
+7. Utilizing ``patch-package`` for temporary fixes in third-party dependencies
+==============================================================================
+
+Status
+******
+
+Accepted (November 2023)
+
+Context
+*******
+
+The ``frontend-app-admin-portal`` repository is currently blocked on upgrading to the latest version of ``@edx/paragon`` until the React, React Router, ``@edx/frontend-platform``, etc. packages are upgraded first. Given this, we cannot rely on upstream fixes to ``@edx/paragon`` for bugs or security issues.
+
+There is at least one opportunity identified where there is a bug within Paragon's ``DataTable`` component, where the "Select all X" label's count is inaccurate when one or more rows are selected, with or without filters applied, using the ``DataTable.ControlledSelect*`` sub-components. A separate bug issue was filed upstream to the Paragon repository, but given ``frontend-app-admin-portal`` is blocked on a Paragon upgrade, we can turn to ``patch-package`` to temporarily fix the issue locally within this repository itself.
+
+``patch-package``
+-----------------
+
+``patch-package`` is an NPM package that allows one to modify third-party dependencies' code within ``node_modules``, and persist the patch so it's applied for other developers and within CI (i.e., anytime ``npm install`` is executed).
+
+By creating a temporary, committed patch file for ``@edx/paragon``, we can resolve the aforementioned "Select all X" label's inaccurate count without needing an upstream fix or upgrading to the latest version of ``@edx/paragon``.
+
+Decisions
+*********
+
+We will use ``patch-package`` to temporarily create a patch of Paragon's ``DataTable`` component to shows the correct number in the "Select all X" label count until we can upgrade to the latest version of Paragon containing a fix for this issue.
+
+We will keep the ``patch-package`` devDependency installed and running in the ``postinstall`` NPM script. However, we should only reach for ``patch-package`` when necessary, and should not use it as a crutch for not upgrading to the latest version of a dependency or making a contribution to the upstream third-party dependency (e.g., ``@edx/paragon``).
+
+Consequences
+************
+
+* The generated patch file is versioned to the currently installed version of the patched dependency. If the installed version of the patched dependency changes, the patch file's version may need to be updated as well by ensuring the local package changes still function as expected and then generating the patch file (i.e., ``npx patch-package @edx/paragon``).
+* Because code changes made in a generated patch are applied after ``npm install``, the changes in the patch should apply to unit and integration tests within this repository (i.e., the patch changes should be testable).
+
+
+Alternatives Considered
+***********************
+
+* Implement a fix upstream in ``@edx/paragon`` and then upgrade to the latest versions of React, React Router, ``@edx/frontend-platform``, etc. in order to unblock the upgrade to the latest Paragon version. This was rejected because it would take focus off of the critical path of feature release with an impending deadline.
+* Replicate a large portion of the Paragon code (i.e., ``SelectionStatusComponent``) into ``frontend-app-admin-portal`` to temporarily remove the "Select all X" label from the ``DataTable``. This was rejected because the "Select all X" functionality is intended to be there to make it easier to work with bulk actions on the table. Without "Select all X" functionality, users would need to manually paginate and select through each individual page which is not a good user experience.
+* Do nothing. This was rejected as the state of the world without a fix leaves a known bug fix that causes usability issues and user confusion.
diff --git a/package-lock.json b/package-lock.json
index 2d6e45a74f..fe96608470 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
"": {
"name": "frontend-app-admin-portal",
"version": "0.1.0",
+ "hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
@@ -77,6 +78,7 @@
"identity-obj-proxy": "3.0.0",
"jest-canvas-mock": "^2.4.0",
"jest-localstorage-mock": "^2.4.22",
+ "patch-package": "8.0.0",
"postcss": "8.4.24",
"react-dev-utils": "11.0.4",
"react-test-renderer": "16.13.1",
@@ -6975,6 +6977,12 @@
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
+ "node_modules/@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -8388,12 +8396,13 @@
"integrity": "sha512-O0KwuHuJnbHUrghHi2kGp0SxnWSIBXTYt7M8WVhW0kbPRUNUKoE/Of6e1rRD6AAxmfxFunKnt90yEK09D+sc5g=="
},
"node_modules/call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
+ "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"dependencies": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.1",
+ "set-function-length": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -9546,6 +9555,19 @@
"node": ">=8"
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
+ "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -11657,6 +11679,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/find-yarn-workspace-root": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
+ "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
+ "dev": true,
+ "dependencies": {
+ "micromatch": "^4.0.2"
+ }
+ },
"node_modules/flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -12033,9 +12064,12 @@
}
},
"node_modules/function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/function.prototype.name": {
"version": "1.1.5",
@@ -16220,11 +16254,35 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
+ "node_modules/json-stable-stringify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz",
+ "integrity": "sha512-zfA+5SuwYN2VWqN1/5HZaDzQKLJHaBVMZIIM+wuYjdptkaQsqzDdqjqf+lZZJUuJq1aanHiY8LhH8LmH+qBYJA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "isarray": "^2.0.5",
+ "jsonify": "^0.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
},
+ "node_modules/json-stable-stringify/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -16247,6 +16305,15 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsonify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
+ "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/jsx-ast-utils": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
@@ -16277,6 +16344,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/klaw-sync": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+ "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11"
+ }
+ },
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -17600,6 +17676,15 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/p-each-series": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
@@ -17784,6 +17869,145 @@
"node": ">=0.10.0"
}
},
+ "node_modules/patch-package": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
+ "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
+ "dev": true,
+ "dependencies": {
+ "@yarnpkg/lockfile": "^1.1.0",
+ "chalk": "^4.1.2",
+ "ci-info": "^3.7.0",
+ "cross-spawn": "^7.0.3",
+ "find-yarn-workspace-root": "^2.0.0",
+ "fs-extra": "^9.0.0",
+ "json-stable-stringify": "^1.0.2",
+ "klaw-sync": "^6.0.0",
+ "minimist": "^1.2.6",
+ "open": "^7.4.2",
+ "rimraf": "^2.6.3",
+ "semver": "^7.5.3",
+ "slash": "^2.0.0",
+ "tmp": "^0.0.33",
+ "yaml": "^2.2.2"
+ },
+ "bin": {
+ "patch-package": "index.js"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">5"
+ }
+ },
+ "node_modules/patch-package/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/patch-package/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/patch-package/node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/patch-package/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/patch-package/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/patch-package/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/patch-package/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/patch-package/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/patch-package/node_modules/yaml": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
+ "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -20693,6 +20917,20 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
+ "node_modules/set-function-length": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
+ "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+ "dependencies": {
+ "define-data-property": "^1.1.1",
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -22152,6 +22390,18 @@
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
+ "node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
diff --git a/package.json b/package.json
index 9cca08b084..e41c5aa0dd 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx --ext .tsx --ext .ts .",
"precommit": "npm run lint",
"prepublishOnly": "npm run build",
+ "postinstall": "patch-package",
"install-theme": "npm install \"@edx/brand@${THEME}\" --no-save",
"start": "fedx-scripts webpack-dev-server --progress",
"start:with-theme": "THEME=npm:@edx/brand-edx.org@latest npm run install-theme && fedx-scripts webpack-dev-server --progress",
@@ -102,6 +103,7 @@
"identity-obj-proxy": "3.0.0",
"jest-canvas-mock": "^2.4.0",
"jest-localstorage-mock": "^2.4.22",
+ "patch-package": "8.0.0",
"postcss": "8.4.24",
"react-dev-utils": "11.0.4",
"react-test-renderer": "16.13.1",
diff --git a/patches/@edx+paragon+20.46.3.patch b/patches/@edx+paragon+20.46.3.patch
new file mode 100644
index 0000000000..aa7efa884f
--- /dev/null
+++ b/patches/@edx+paragon+20.46.3.patch
@@ -0,0 +1,29 @@
+diff --git a/node_modules/@edx/paragon/dist/DataTable/selection/BaseSelectionStatus.js b/node_modules/@edx/paragon/dist/DataTable/selection/BaseSelectionStatus.js
+index 00c4c4b..426ca4e 100644
+--- a/node_modules/@edx/paragon/dist/DataTable/selection/BaseSelectionStatus.js
++++ b/node_modules/@edx/paragon/dist/DataTable/selection/BaseSelectionStatus.js
+@@ -20,11 +20,12 @@ function BaseSelectionStatus(_ref) {
+ itemCount,
+ filteredRows,
+ isPaginated,
+- state
++ state,
++ controlledTableSelections,
+ } = useContext(DataTableContext);
+ const hasAppliedFilters = state?.filters?.length > 0;
+- const isAllRowsSelected = numSelectedRows === itemCount;
+- const filteredItems = filteredRows?.length || itemCount;
++ const isAllRowsSelected = controlledTableSelections?.isEntireTableSelected || numSelectedRows === itemCount;
++ const selectAllItemCount = itemCount;
+ const intlAllSelectedText = allSelectedText || /*#__PURE__*/React.createElement(FormattedMessage, {
+ id: "pgn.DataTable.BaseSelectionStatus.allSelectedText",
+ defaultMessage: "All {numSelectedRows} selected",
+@@ -62,7 +63,7 @@ function BaseSelectionStatus(_ref) {
+ defaultMessage: "Select all {itemCount}",
+ description: "A label for select all button.",
+ values: {
+- itemCount: filteredItems
++ itemCount: selectAllItemCount
+ }
+ })), numSelectedRows > 0 && /*#__PURE__*/React.createElement(Button, {
+ className: CLEAR_SELECTION_TEST_ID,
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index e8a17870fe..0982821ad7 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -10,6 +10,8 @@ import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { renderWithRouter, sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { act } from 'react-dom/test-utils';
+import { v4 as uuidv4 } from 'uuid';
+import { faker } from '@faker-js/faker';
import BudgetDetailPage from '../BudgetDetailPage';
import {
@@ -113,6 +115,11 @@ const mockLearnerContentAssignment = {
actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction],
errorReason: null,
};
+const createMockLearnerContentAssignment = () => ({
+ ...mockLearnerContentAssignment,
+ uuid: uuidv4(),
+ learnerEmail: faker.internet.email(),
+});
const mockEnrollmentTransactionReversal = {
uuid: 'test-transaction-reversal-uuid',
created: '2023-10-31',
@@ -384,6 +391,7 @@ describe(' ', () => {
});
it('renders with assigned table data and handles table refresh', () => {
+ const NUMBER_OF_ASSIGNMENTS_TO_GENERATE = 60;
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
@@ -395,18 +403,23 @@ describe(' ', () => {
useBudgetDetailActivityOverview.mockReturnValue({
isLoading: false,
data: {
- contentAssignments: { count: 1 },
+ contentAssignments: { count: NUMBER_OF_ASSIGNMENTS_TO_GENERATE },
spentTransactions: { count: 0 },
},
});
const mockFetchContentAssignments = jest.fn();
+ // Max page size is 25 rows. Generate one assignment with a known learner email and the others with random emails.
+ const mockAssignmentsList = [
+ mockLearnerContentAssignment,
+ ...Array.from({ length: PAGE_SIZE - 1 }, createMockLearnerContentAssignment),
+ ];
useBudgetContentAssignments.mockReturnValue({
isLoading: false,
contentAssignments: {
- count: 1,
- results: [mockLearnerContentAssignment],
- learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
- numPages: 1,
+ count: NUMBER_OF_ASSIGNMENTS_TO_GENERATE,
+ results: mockAssignmentsList,
+ learnerStateCounts: [{ learnerState: 'waiting', count: NUMBER_OF_ASSIGNMENTS_TO_GENERATE }],
+ numPages: Math.floor(NUMBER_OF_ASSIGNMENTS_TO_GENERATE / PAGE_SIZE),
currentPage: 1,
},
fetchContentAssignments: mockFetchContentAssignments,
@@ -422,12 +435,23 @@ describe(' ', () => {
const assignedSection = within(screen.getByText('Assigned').closest('section'));
expect(assignedSection.queryByText('No results found')).not.toBeInTheDocument();
expect(assignedSection.getByText(mockLearnerEmail)).toBeInTheDocument();
- const viewCourseCTA = assignedSection.getByText(mockContentTitle, { selector: 'a' });
+ const viewCourseCTA = assignedSection.queryAllByText(mockContentTitle, { selector: 'a' })[0];
expect(viewCourseCTA).toBeInTheDocument();
expect(viewCourseCTA.getAttribute('href')).toEqual(`${process.env.ENTERPRISE_LEARNER_PORTAL_URL}/${enterpriseSlug}/course/${mockCourseKey}`);
- expect(assignedSection.getByText('-$199')).toBeInTheDocument();
- expect(assignedSection.getByText('Waiting for learner')).toBeInTheDocument();
- expect(assignedSection.getByText(`Assigned: ${formatDate('2023-10-27')}`)).toBeInTheDocument();
+ expect(assignedSection.queryAllByText('-$199')).toHaveLength(PAGE_SIZE);
+ expect(assignedSection.queryAllByText('Waiting for learner')).toHaveLength(PAGE_SIZE);
+ expect(assignedSection.queryAllByText(`Assigned: ${formatDate('2023-10-27')}`)).toHaveLength(PAGE_SIZE);
+
+ // Assert the "Select all X" label count is correct, after selecting a row. This verifies the
+ // temporary patch of Paragon is working as intended. If this test fails, it may mean Paragon
+ // was upgraded to a version that does not yet contain a fix for the underlying bug related to
+ // the incorrect "Select all X" count.
+ const selectAllCheckbox = assignedSection.queryAllByRole('checkbox')[0];
+ userEvent.click(selectAllCheckbox);
+ expect(getButtonElement(`Select all ${NUMBER_OF_ASSIGNMENTS_TO_GENERATE}`, { screenOverride: assignedSection })).toBeInTheDocument();
+
+ // Unselect the checkbox the "Refresh" table action appears
+ userEvent.click(selectAllCheckbox);
const expectedTableFetchDataArgs = {
pageIndex: DEFAULT_PAGE,
From 6367d7afa99e435b32db4adf1a0850384735346c Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Thu, 30 Nov 2023 09:35:47 -0500
Subject: [PATCH 084/124] feat: adds additional track event to assigned and
spent table courses (#1106)
* feat: adds additional track event to assigned table courses
* feat: adds track event on spent datatable view course
* chore: test for spent table track event call
* chore: test for spent table track event call
* feat: add additional metadata to assignment segment event
* feat: add additional metadata to view course from spend and assigned table
* chore: test cleanup
* fix: PR feedback
---
.../AssignmentDetailsTableCell.jsx | 24 +++++++++++--
.../SpendTableEnrollmentDetails.jsx | 17 ++++++++++
.../cards/CourseCardFooterActions.jsx | 2 +-
.../cards/NewAssignmentModalButton.jsx | 34 ++++++++++++++-----
.../cards/tests/CourseCard.test.jsx | 4 +++
.../tests/CatalogSearchResults.test.jsx | 4 +++
.../tests/BudgetDetailPage.test.jsx | 8 ++++-
src/eventTracking.js | 2 ++
8 files changed, 83 insertions(+), 12 deletions(-)
diff --git a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
index 00b20f2419..83c8a0f26a 100644
--- a/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentDetailsTableCell.jsx
@@ -2,12 +2,25 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Stack, Hyperlink } from '@edx/paragon';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { configuration } from '../../config';
import EmailAddressTableCell from './EmailAddressTableCell';
+import EVENT_NAMES from '../../eventTracking';
-const AssignmentDetailsTableCell = ({ row, enterpriseSlug }) => {
+const AssignmentDetailsTableCell = ({ row, enterpriseSlug, enterpriseId }) => {
const { ENTERPRISE_LEARNER_PORTAL_URL } = configuration;
+ const handleOnViewCourseClick = () => sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_DETAILS_ASSIGNED_DATATABLE_VIEW_COURSE,
+ {
+ courseKey: row.original.contentKey,
+ contentQuantityInCents: row.original.contentQuantity,
+ errorReason: row.original.errorReason,
+ learnerState: row.original.learnerState,
+ state: row.original.state,
+ },
+ );
return (
{
@@ -30,19 +44,25 @@ const AssignmentDetailsTableCell = ({ row, enterpriseSlug }) => {
};
const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
});
AssignmentDetailsTableCell.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
- uuid: PropTypes.string.isRequired,
+ uuid: PropTypes.string,
learnerEmail: PropTypes.string,
contentKey: PropTypes.string.isRequired,
contentTitle: PropTypes.string,
+ contentQuantity: PropTypes.number,
+ errorReason: PropTypes.string,
+ learnerState: PropTypes.string,
+ state: PropTypes.string,
}).isRequired,
}).isRequired,
enterpriseSlug: PropTypes.string,
+ enterpriseId: PropTypes.string.isRequired,
};
export default connect(mapStateToProps)(AssignmentDetailsTableCell);
diff --git a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
index 8ec52fd7cb..e4347ca1a8 100644
--- a/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
+++ b/src/components/learner-credit-management/SpendTableEnrollmentDetails.jsx
@@ -3,14 +3,17 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Stack, Hyperlink } from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import EmailAddressTableCell from './EmailAddressTableCell';
import { formatDate } from './data';
+import EVENT_NAMES from '../../eventTracking';
const SpendTableEnrollmentDetailsContents = ({
row,
enableLearnerPortal,
enterpriseSlug,
+ enterpriseId,
}) => (
{row.original.reversal && (
@@ -29,6 +32,16 @@ const SpendTableEnrollmentDetailsContents = ({
{
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_DETAILS_SPENT_DATATABLE_VIEW_COURSE,
+ {
+ courseKey: row.original.courseKey,
+ courseListPriceInCents: row.original.courseListPrice * 100,
+ },
+ );
+ }}
target="_blank"
isInline
>
@@ -43,8 +56,10 @@ const SpendTableEnrollmentDetailsContents = ({
const rowPropType = PropTypes.shape({
original: PropTypes.shape({
+ uuid: PropTypes.string.isRequired,
courseKey: PropTypes.string.isRequired,
courseTitle: PropTypes.string,
+ courseListPrice: PropTypes.number,
userEmail: PropTypes.string,
enterpriseEnrollmentId: PropTypes.number,
fulfillmentIdentifier: PropTypes.string,
@@ -57,6 +72,7 @@ const rowPropType = PropTypes.shape({
const mapStateToProps = state => ({
enableLearnerPortal: state.portalConfiguration.enableLearnerPortal,
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enterpriseId: state.portalConfiguration.enterpriseId,
});
const ConnectedSpendTableEnrollmentDetailsContents = connect(mapStateToProps)(SpendTableEnrollmentDetailsContents);
@@ -65,6 +81,7 @@ SpendTableEnrollmentDetailsContents.propTypes = {
row: rowPropType,
enableLearnerPortal: PropTypes.bool.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
+ enterpriseId: PropTypes.string.isRequired,
};
const SpendTableEnrollmentDetails = ({ row }) => ;
diff --git a/src/components/learner-credit-management/cards/CourseCardFooterActions.jsx b/src/components/learner-credit-management/cards/CourseCardFooterActions.jsx
index 3866d007ee..38ac15756b 100644
--- a/src/components/learner-credit-management/cards/CourseCardFooterActions.jsx
+++ b/src/components/learner-credit-management/cards/CourseCardFooterActions.jsx
@@ -16,7 +16,7 @@ const CourseCardFooterActions = ({ enterpriseId, course }) => {
sendEnterpriseTrackEvent(
enterpriseId,
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.VIEW_COURSE,
- { courseUUID: uuid },
+ { courseUuid: uuid },
);
};
return [
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 353af471e3..7f20e7e8aa 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -16,7 +16,7 @@ import { camelCaseObject, snakeCaseObject } from '@edx/frontend-platform/utils';
import { connect } from 'react-redux';
import AssignmentModalContent from './AssignmentModalContent';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
-import { learnerCreditManagementQueryKeys, useBudgetId } from '../data';
+import { learnerCreditManagementQueryKeys, useBudgetId, useSubsidyAccessPolicy } from '../data';
import CreateAllocationErrorAlertModals from './CreateAllocationErrorAlertModals';
import { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
import EVENT_NAMES from '../../../eventTracking';
@@ -42,19 +42,33 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
const [assignButtonState, setAssignButtonState] = useState('default');
const [createAssignmentsErrorReason, setCreateAssignmentsErrorReason] = useState();
const { displayToastForAssignmentAllocation } = useContext(BudgetDetailPageContext);
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid,
+ } = subsidyAccessPolicy;
+ const sharedEnterpriseTrackEventMetadata = {
+ subsidyAccessPolicyId,
+ catalogUuid,
+ subsidyUuid,
+ isSubsidyActive,
+ isAssignable,
+ contentPriceCents: course.normalizedMetadata.contentPrice * 100,
+ contentKey: course.key,
+ courseUuid: course.uuid,
+ assignmentConfigurationUuid: assignmentConfiguration.uuid,
+ };
const { mutate } = useAllocateContentAssignments();
-
const pathToActivityTab = generatePath(routeMatch.path, { budgetId: subsidyAccessPolicyId, activeTabKey: 'activity' });
const handleOpenAssignmentModal = () => {
open();
sendEnterpriseTrackEvent(
enterpriseId,
- EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_ASSIGN_COURSE,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGN_COURSE,
{
+ ...sharedEnterpriseTrackEventMetadata,
isOpen: !isOpen,
- courseUUID: course.uuid,
},
);
};
@@ -74,13 +88,15 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
setCanAllocateAssignments(canAllocate);
}, []);
- const onSuccessEnterpriseTrackEvents = ({ created, noChange, updated }) => {
+ const onSuccessEnterpriseTrackEvents = ({
+ created, noChange, updated,
+ }) => {
const trackEventMetadata = {
+ ...sharedEnterpriseTrackEventMetadata,
totalAllocatedLearners: learnerEmails.length,
created: created.length,
noChange: noChange.length,
updated: updated.length,
- courseUUID: course.uuid,
};
sendEnterpriseTrackEvent(
enterpriseId,
@@ -108,7 +124,9 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
});
handleCloseAssignmentModal();
- onSuccessEnterpriseTrackEvents({ created, noChange, updated });
+ onSuccessEnterpriseTrackEvents({
+ created, noChange, updated,
+ });
displayToastForAssignmentAllocation({ totalLearnersAssigned: learnerEmails.length });
history.push(pathToActivityTab);
},
@@ -130,8 +148,8 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
enterpriseId,
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_ALLOCATION_ERROR,
{
+ ...sharedEnterpriseTrackEventMetadata,
totalAllocatedLearners: learnerEmails.length,
- courseUUID: course.uuid,
errorStatus: httpErrorStatus,
errorReason,
},
diff --git a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
index 38d4d5267a..42697fc761 100644
--- a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
@@ -95,6 +95,10 @@ const initialStoreState = {
const mockSubsidyAccessPolicy = {
uuid: 'test-subsidy-access-policy-uuid',
displayName: 'Test Subsidy Access Policy',
+ assignmentConfiguration: {
+ uuid: 'test-assignment-configuration-uuid',
+ active: true,
+ },
aggregates: {
spendAvailableUsd: 50000,
},
diff --git a/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
index 60107dbfa5..d46d96dec2 100644
--- a/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
@@ -31,6 +31,10 @@ jest.mock('../../data', () => ({
data: {
uuid: 'test-uuid',
displayName: 'Test Budget',
+ assignmentConfiguration: {
+ uuid: 'test-assignment-configuration-uuid',
+ active: true,
+ },
aggregates: {
spendAvailableUsd: 100,
},
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 0982821ad7..170b33b0ad 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -388,6 +388,9 @@ describe(' ', () => {
const transactionRowWithReversal = within(spentSection.getByText(mockSecondLearnerEmail).closest('tr'));
expect(transactionRowWithReversal.getByText(`Refunded on ${formatDate(mockEnrollmentTransactionReversal.created)}`)).toBeInTheDocument();
expect(transactionRowWithReversal.getByText(`+${formatPrice(mockEnrollmentTransaction.courseListPrice)}`)).toBeInTheDocument();
+
+ userEvent.click(spentSection.queryAllByText(mockContentTitle, { selector: 'a' })[0]);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
});
it('renders with assigned table data and handles table refresh', () => {
@@ -466,9 +469,12 @@ describe(' ', () => {
const refreshCTA = assignedSection.getByText('Refresh', { selector: 'button' });
expect(refreshCTA).toBeInTheDocument();
userEvent.click(refreshCTA);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
expect(mockFetchContentAssignments).toHaveBeenCalledTimes(2); // should be called again on refresh
expect(mockFetchContentAssignments).toHaveBeenLastCalledWith(expect.objectContaining(expectedTableFetchDataArgs));
- expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+
+ userEvent.click(viewCourseCTA);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
});
it.each([
diff --git a/src/eventTracking.js b/src/eventTracking.js
index a2f04f1cbc..ed4244786f 100644
--- a/src/eventTracking.js
+++ b/src/eventTracking.js
@@ -106,8 +106,10 @@ export const LEARNER_CREDIT_MANAGEMENT_EVENTS = {
TAB_CHANGED: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.tab.changed`,
// Activity tab
BUDGET_DETAILS_ASSIGNED_DATATABLE_SORT_BY_OR_FILTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table.changed`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_VIEW_COURSE: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_view_course.clicked`,
BUDGET_DETAILS_ASSIGNED_DATATABLE_ACTIONS_REFRESH: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_refresh.clicked`,
BUDGET_DETAILS_SPENT_DATATABLE_SORT_BY_OR_FILTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.spent_table.changed`,
+ BUDGET_DETAILS_SPENT_DATATABLE_VIEW_COURSE: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.spent_table_view_course.clicked`,
EMPTY_STATE_CTA: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.empty_state_cta_to_catalog.clicked`,
// Catalog tab
// Catalog tab search
From 9b52b1b4cac7de99f352c060e51b39bc31cf62aa Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Thu, 30 Nov 2023 01:01:52 +0000
Subject: [PATCH 085/124] fix: cleaning up sso network error page
---
src/components/settings/SettingsSSOTab/SsoErrorPage.jsx | 5 +++--
src/components/settings/settings.scss | 5 +++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx b/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx
index 49450619d4..20849ffd44 100644
--- a/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx
+++ b/src/components/settings/SettingsSSOTab/SsoErrorPage.jsx
@@ -19,17 +19,18 @@ const SsoErrorPage = ({
return (
{ /* FullscreenModal requires an onClose prop despite hasCloseButton is false */ }}
title={(
)}
>
-
+
Date: Fri, 1 Dec 2023 07:51:48 -0800
Subject: [PATCH 086/124] fix: Add loading spinner to Create new SSO button
(#1107)
test: Create new SSO button
---
.../settings/SettingsSSOTab/index.jsx | 15 +++++--
.../tests/SettingsSSOTab.test.jsx | 39 +++++++++++++++++++
2 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index 4fddf1263a..e98c94840b 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -1,7 +1,7 @@
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
- Alert, ActionRow, Button, Hyperlink, ModalDialog, Toast, Skeleton, useToggle,
+ Alert, ActionRow, Button, Hyperlink, ModalDialog, Toast, Skeleton, Spinner, useToggle,
} from '@edx/paragon';
import { Add, WarningFilled } from '@edx/paragon/icons';
import { HELP_CENTER_SAML_LINK } from '../data/constants';
@@ -30,12 +30,15 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const [isOpen, open, close] = useToggle(false);
const [pollingNetworkError, setPollingNetworkError] = useState(false);
const [isStepperOpen, setIsStepperOpen] = useState(true);
+ const [isDeletingOldConfigs, setIsDeletingOldConfigs] = useState(false);
const newConfigurationButtonOnClick = async () => {
+ setIsDeletingOldConfigs(true);
Promise.all(existingConfigs.map(config => LmsApiService.updateEnterpriseSsoOrchestrationRecord(
{ active: false, is_removed: true },
config.uuid,
))).then(() => {
+ setIsDeletingOldConfigs(false);
setRefreshBool(!refreshBool);
close();
});
@@ -89,8 +92,13 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
- Create new SSO
+ {isDeletingOldConfigs ? (
+
+ ) : (
+ <>Create new SSO>
+ )}
@@ -151,7 +159,8 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
)}
{pdError && (
- An error occurred loading the SAML data: {pdError?.message}
+ An error occurred loading the SAML data:{' '}
+ {pdError?.message}
)}
{
),
).toBeInTheDocument());
});
+ test('creating new sso config with existing config', async () => {
+ features.AUTH0_SELF_SERVICE_INTEGRATION = true;
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockImplementation(() => Promise.resolve({
+ data: [{
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ display_name: 'foobar',
+ active: true,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: '2022-05-12T19:51:25Z',
+ validated_at: '2022-06-12T19:51:25Z',
+ submitted_at: '2022-04-12T19:51:25Z',
+ }],
+ }));
+ const updateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
+ await waitFor(() => render(
+
+
+
+
+ ,
+
+ ,
+ ));
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'New',
+ ),
+ ).toBeInTheDocument());
+ userEvent.click(screen.getByText('New'));
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'Create new SSO configuration?',
+ ),
+ ).toBeInTheDocument());
+ userEvent.click(screen.getByText('Create new SSO'));
+ expect(updateEnterpriseSsoOrchestrationRecord).toBeCalled();
+ });
});
From d983dc7d757e94a5116fafe27ba6a35a57558b72 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 1 Dec 2023 11:16:07 -0500
Subject: [PATCH 087/124] feat: integrate with policies REST api on budgets
overview page (#1109)
---
.../EnterpriseAppContextProvider.jsx | 5 +
.../EnterpriseAppContextProvider.test.jsx | 5 +
.../EnterpriseApp/data/constants.js | 1 +
src/components/EnterpriseApp/index.jsx | 6 +
.../EnterpriseSubsidiesContext/data/hooks.js | 176 +++---
.../data/tests/hooks.test.js | 330 ------------
.../data/tests/hooks.test.jsx | 503 ++++++++++++++++++
.../data/tests/index.test.js | 43 +-
.../EnterpriseSubsidiesContext/index.jsx | 35 +-
.../learner-credit-management/BudgetCard.jsx | 96 ++--
.../BudgetDetailRedemptions.jsx | 12 +-
.../MultipleBudgetsPage.jsx | 18 +-
.../MultipleBudgetsPicker.jsx | 17 +-
.../SubBudgetCard.jsx | 35 +-
.../cards/NewAssignmentModalButton.jsx | 7 +
.../cards/tests/CourseCard.test.jsx | 5 +-
.../data/constants.js | 2 +-
.../data/hooks/index.js | 4 +-
...test.jsx => useBudgetRedemptions.test.jsx} | 16 +-
.../data/hooks/tests/useOfferSummary.test.js | 67 ---
.../useSubsidySummaryAnalyticsApi.test.js | 93 ++++
...Redemptions.js => useBudgetRedemptions.js} | 19 +-
.../data/hooks/useOfferSummary.js | 41 --
.../hooks/useSubsidySummaryAnalyticsApi.js | 47 ++
.../data/tests/utils.test.js | 54 +-
.../learner-credit-management/data/utils.js | 60 ++-
.../learner-credit-management/index.jsx | 4 +-
.../tests/BudgetCard.test.jsx | 346 ++++++------
.../tests/BudgetDetailPage.test.jsx | 76 +--
.../tests/MultipleBudgetsPage.test.jsx | 6 +-
.../tests/SubsidyRequestsContext.test.jsx | 2 +-
src/containers/EnterpriseApp/index.jsx | 1 +
src/data/constants/subsidyTypes.js | 2 +-
.../services/EnterpriseAccessApiService.js | 16 +
.../tests/EnterpriseAccessApiService.test.js | 7 +
35 files changed, 1269 insertions(+), 888 deletions(-)
delete mode 100644 src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
create mode 100644 src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx
rename src/components/learner-credit-management/data/hooks/tests/{useOfferRedemptions.test.jsx => useBudgetRedemptions.test.jsx} (91%)
delete mode 100644 src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js
create mode 100644 src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
rename src/components/learner-credit-management/data/hooks/{useOfferRedemptions.js => useBudgetRedemptions.js} (91%)
delete mode 100644 src/components/learner-credit-management/data/hooks/useOfferSummary.js
create mode 100644 src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
diff --git a/src/components/EnterpriseApp/EnterpriseAppContextProvider.jsx b/src/components/EnterpriseApp/EnterpriseAppContextProvider.jsx
index b197cf81fb..9d7960ae21 100644
--- a/src/components/EnterpriseApp/EnterpriseAppContextProvider.jsx
+++ b/src/components/EnterpriseApp/EnterpriseAppContextProvider.jsx
@@ -45,6 +45,7 @@ export const EnterpriseAppContext = createContext();
const EnterpriseAppContextProvider = ({
enterpriseId,
enterpriseName,
+ enterpriseFeatures,
enablePortalLearnerCreditManagementScreen,
children,
}) => {
@@ -52,6 +53,7 @@ const EnterpriseAppContextProvider = ({
const enterpriseSubsidiesContext = useEnterpriseSubsidiesContext({
enterpriseId,
enablePortalLearnerCreditManagementScreen,
+ isTopDownAssignmentEnabled: enterpriseFeatures.topDownAssignmentRealTimeLcm,
});
// subsidy requests for the enterprise customer
@@ -97,6 +99,9 @@ const EnterpriseAppContextProvider = ({
EnterpriseAppContextProvider.propTypes = {
enterpriseId: PropTypes.string.isRequired,
enterpriseName: PropTypes.string.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }).isRequired,
enablePortalLearnerCreditManagementScreen: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
};
diff --git a/src/components/EnterpriseApp/EnterpriseAppContextProvider.test.jsx b/src/components/EnterpriseApp/EnterpriseAppContextProvider.test.jsx
index 5d490ebcd0..892200045b 100644
--- a/src/components/EnterpriseApp/EnterpriseAppContextProvider.test.jsx
+++ b/src/components/EnterpriseApp/EnterpriseAppContextProvider.test.jsx
@@ -14,6 +14,10 @@ const TEST_ENTERPRISE_NAME = 'test-enterprise-name';
jest.mock('./data/hooks');
+const mockEnterpriseFeatures = {
+ topDownAssignmentRealTimeLcm: true,
+};
+
describe(' ', () => {
it.each([{
isLoadingEnterpriseSubsidies: true,
@@ -63,6 +67,7 @@ describe(' ', () => {
children
diff --git a/src/components/EnterpriseApp/data/constants.js b/src/components/EnterpriseApp/data/constants.js
index 1773fd73e2..e37edb3636 100644
--- a/src/components/EnterpriseApp/data/constants.js
+++ b/src/components/EnterpriseApp/data/constants.js
@@ -23,4 +23,5 @@ export const BUDGET_STATUSES = {
export const BUDGET_TYPES = {
ecommerce: 'ecommerce',
subsidy: 'subsidy',
+ policy: 'policy',
};
diff --git a/src/components/EnterpriseApp/index.jsx b/src/components/EnterpriseApp/index.jsx
index c05a732308..13617549f4 100644
--- a/src/components/EnterpriseApp/index.jsx
+++ b/src/components/EnterpriseApp/index.jsx
@@ -86,6 +86,7 @@ class EnterpriseApp extends React.Component {
enablePortalLearnerCreditManagementScreen,
enterpriseId,
enterpriseName,
+ enterpriseFeatures,
enterpriseBranding,
loading,
} = this.props;
@@ -123,6 +124,7 @@ class EnterpriseApp extends React.Component {
@@ -173,6 +175,7 @@ class EnterpriseApp extends React.Component {
EnterpriseApp.defaultProps = {
enterpriseId: null,
enterpriseName: null,
+ enterpriseFeatures: {},
enterpriseBranding: {
primary_color: SCHOLAR_THEME.button,
secondary_color: SCHOLAR_THEME.banner,
@@ -196,6 +199,9 @@ EnterpriseApp.propTypes = {
}).isRequired,
enterpriseId: PropTypes.string,
enterpriseName: PropTypes.string,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }),
enterpriseBranding: PropTypes.shape({
primary_color: PropTypes.string,
secondary_color: PropTypes.string,
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index 6e9b040167..c576a19541 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -1,87 +1,121 @@
import { useEffect, useState } from 'react';
import dayjs from 'dayjs';
-import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
-import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
-
+import isBetween from 'dayjs/plugin/isBetween';
import { logError } from '@edx/frontend-platform/logging';
-import { getConfig } from '@edx/frontend-platform/config';
import { camelCaseObject } from '@edx/frontend-platform/utils';
+import { getConfig } from '@edx/frontend-platform/config';
+import { useQuery } from '@tanstack/react-query';
import EcommerceApiService from '../../../data/services/EcommerceApiService';
import LicenseManagerApiService from '../../../data/services/LicenseManagerAPIService';
import SubsidyApiService from '../../../data/services/EnterpriseSubsidyApiService';
import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
-
-export const useEnterpriseOffers = ({ enablePortalLearnerCreditManagementScreen, enterpriseId }) => {
- const [offers, setOffers] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
- const [canManageLearnerCredit, setCanManageLearnerCredit] = useState(false);
-
- dayjs.extend(isSameOrBefore);
- dayjs.extend(isSameOrAfter);
-
- useEffect(() => {
- setIsLoading(true);
- const fetchOffers = async () => {
- try {
- const [enterpriseSubsidyResponse, ecommerceApiResponse] = await Promise.all([
- SubsidyApiService.getSubsidyByCustomerUUID(enterpriseId, { subsidyType: 'learner_credit' }),
- EcommerceApiService.fetchEnterpriseOffers(),
- ]);
-
- // We have to consider both type of offers active and inactive.
-
- const enterpriseSubsidyResults = camelCaseObject(enterpriseSubsidyResponse.data).results;
- const ecommerceOffersResults = camelCaseObject(ecommerceApiResponse.data.results);
-
- const offerData = [];
-
- enterpriseSubsidyResults.forEach((result) => {
- offerData.push({
- source: BUDGET_TYPES.subsidy,
- id: result.uuid,
- name: result.title,
- start: result.activeDatetime,
- end: result.expirationDatetime,
- isCurrent: result.isActive,
- });
- });
-
- ecommerceOffersResults.forEach((result) => {
- offerData.push({
- source: BUDGET_TYPES.ecommerce,
- id: (result.id).toString(),
- name: result.displayName,
- start: result.startDatetime,
- end: result.endDatetime,
- isCurrent: result.isCurrent,
- });
- });
- setOffers(offerData);
- if (offerData.length > 0) {
- setCanManageLearnerCredit(true);
- }
- } catch (error) {
- logError(error);
- } finally {
- setIsLoading(false);
- }
+import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
+import { learnerCreditManagementQueryKeys } from '../../learner-credit-management/data';
+
+dayjs.extend(isBetween);
+
+async function fetchEnterpriseBudgets({
+ isTopDownAssignmentEnabled,
+ enterpriseId,
+ enablePortalLearnerCreditManagementScreen,
+}) {
+ // If the LC2 feature is disabled, do nothing.
+ if (!getConfig().FEATURE_LEARNER_CREDIT_MANAGEMENT || !enablePortalLearnerCreditManagementScreen) {
+ return {
+ budgets: [],
+ canManageLearnerCredit: false,
};
-
- if (getConfig().FEATURE_LEARNER_CREDIT_MANAGEMENT
- && enablePortalLearnerCreditManagementScreen) {
- fetchOffers();
- } else {
- setIsLoading(false);
- }
- }, [enablePortalLearnerCreditManagementScreen, enterpriseId]);
+ }
+
+ // Call the appropriate API based on the feature flag
+ const budgetPromisesToFulfill = isTopDownAssignmentEnabled
+ ? [undefined, EnterpriseAccessApiService.listSubsidyAccessPolicies(enterpriseId)]
+ : [SubsidyApiService.getSubsidyByCustomerUUID(enterpriseId, { subsidyType: 'learner_credit' }), undefined];
+
+ // Always prepend the promise to fetch ecommerce offers
+ budgetPromisesToFulfill.unshift(EcommerceApiService.fetchEnterpriseOffers());
+
+ // Attempt to resolve all promises
+ const [
+ ecommerceApiResponse,
+ enterpriseSubsidyResponse,
+ enterprisePolicyResponse,
+ ] = await Promise.allSettled(budgetPromisesToFulfill);
+
+ // Log any errors
+ if (ecommerceApiResponse.status === 'rejected') {
+ logError(ecommerceApiResponse.reason);
+ }
+ if (enterpriseSubsidyResponse.status === 'rejected') {
+ logError(enterpriseSubsidyResponse.reason);
+ }
+ if (enterprisePolicyResponse.status === 'rejected') {
+ logError(enterprisePolicyResponse.reason);
+ }
+
+ // Transform the API responses
+ const ecommerceOffersResults = camelCaseObject(ecommerceApiResponse.value?.data.results);
+ const enterpriseSubsidyResults = camelCaseObject(enterpriseSubsidyResponse.value?.data.results);
+ const enterprisePolicyResults = camelCaseObject(enterprisePolicyResponse.value?.data.results);
+
+ // Iterate through each API response (if applicable) and concatenate the results into a single array of budgets.
+ const budgetsList = [];
+ enterprisePolicyResults?.forEach((result) => {
+ budgetsList.push({
+ source: BUDGET_TYPES.policy,
+ id: result.uuid,
+ name: result.displayName || 'Overview',
+ start: result.subsidyActiveDatetime,
+ end: result.subsidyExpirationDatetime,
+ isCurrent: dayjs().isBetween(result.subsidyActiveDatetime, result.subsidyExpirationDatetime, 'day', '[]'),
+ aggregates: {
+ available: result.aggregates.spendAvailableUsd,
+ spent: result.aggregates.amountRedeemedUsd,
+ pending: result.aggregates.amountAllocatedUsd,
+ },
+ });
+ });
+ enterpriseSubsidyResults?.forEach((result) => {
+ budgetsList.push({
+ source: BUDGET_TYPES.subsidy,
+ id: result.uuid,
+ name: result.title,
+ start: result.activeDatetime,
+ end: result.expirationDatetime,
+ isCurrent: result.isActive,
+ });
+ });
+ ecommerceOffersResults?.forEach((result) => {
+ budgetsList.push({
+ source: BUDGET_TYPES.ecommerce,
+ id: (result.id).toString(),
+ name: result.displayName,
+ start: result.startDatetime,
+ end: result.endDatetime,
+ isCurrent: result.isCurrent,
+ });
+ });
return {
- isLoading,
- offers,
- canManageLearnerCredit,
+ budgets: budgetsList,
+ canManageLearnerCredit: budgetsList.length > 0,
};
-};
+}
+
+export const useEnterpriseBudgets = ({
+ enablePortalLearnerCreditManagementScreen,
+ enterpriseId,
+ isTopDownAssignmentEnabled,
+}) => useQuery({
+ queryKey: learnerCreditManagementQueryKeys.budgets(enterpriseId),
+ queryFn: (args) => fetchEnterpriseBudgets({
+ queryArgs: args,
+ isTopDownAssignmentEnabled,
+ enterpriseId,
+ enablePortalLearnerCreditManagementScreen,
+ }),
+});
export const useCustomerAgreement = ({ enterpriseId }) => {
const [customerAgreement, setCustomerAgreement] = useState();
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
deleted file mode 100644
index ec1c5466af..0000000000
--- a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js
+++ /dev/null
@@ -1,330 +0,0 @@
-import { renderHook } from '@testing-library/react-hooks/dom';
-
-import { useCoupons, useCustomerAgreement, useEnterpriseOffers } from '../hooks';
-import EcommerceApiService from '../../../../data/services/EcommerceApiService';
-import LicenseManagerApiService from '../../../../data/services/LicenseManagerAPIService';
-import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
-import { BUDGET_TYPES } from '../../../EnterpriseApp/data/constants';
-
-jest.mock('@edx/frontend-platform/config', () => ({
- getConfig: jest.fn(() => ({
- FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
- })),
-}));
-jest.mock('../../../../data/services/EcommerceApiService');
-jest.mock('../../../../data/services/LicenseManagerAPIService');
-jest.mock('../../../../data/services/EnterpriseAccessApiService');
-jest.mock('../../../../data/services/EnterpriseSubsidyApiService');
-
-const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
-
-describe('useEnterpriseOffers', () => {
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('should not fetch enterprise offers if enablePortalLearnerCreditManagementScreen is false', async () => {
- const { result } = renderHook(() => useEnterpriseOffers({ enablePortalLearnerCreditManagementScreen: false }));
-
- expect(EcommerceApiService.fetchEnterpriseOffers).not.toHaveBeenCalled();
- expect(SubsidyApiService.getSubsidyByCustomerUUID).not.toHaveBeenCalled();
-
- expect(result.current).toEqual({
- offers: [],
- isLoading: false,
- canManageLearnerCredit: false,
- });
- });
-
- it('should fetch enterprise offers for the enterprise when data is available only in e-commerce', async () => {
- const mockEcommerceResponse = [
- {
- id: 'uuid',
- display_name: 'offer-name',
- start_datetime: '2021-05-15T19:56:09Z',
- end_datetime: '2100-05-15T19:56:09Z',
- is_current: true,
- },
- ];
- const mockOffers = [{
- id: 'uuid',
- name: 'offer-name',
- start: '2021-05-15T19:56:09Z',
- end: '2100-05-15T19:56:09Z',
- isCurrent: true,
- source: BUDGET_TYPES.ecommerce,
- }];
-
- SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
- data: {
- results: [],
- },
- });
- EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: mockEcommerceResponse,
- },
- });
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers({
- enablePortalLearnerCreditManagementScreen: true,
- }));
-
- await waitForNextUpdate();
-
- expect(EcommerceApiService.fetchEnterpriseOffers).toHaveBeenCalled();
- expect(result.current).toEqual({
- offers: mockOffers,
- isLoading: false,
- canManageLearnerCredit: true,
- });
- });
-
- it('should fetch enterprise offers for the enterprise when data available in enterprise-subsidy', async () => {
- const mockEnterpriseSubsidyResponse = [
- {
- uuid: 'offer-id',
- title: 'offer-name',
- activeDatetime: '2021-05-15T19:56:09Z',
- expirationDatetime: '2100-05-15T19:56:09Z',
- isActive: true,
- },
- ];
-
- const mockEcommerceResponse = [
- {
- id: 'uuid',
- display_name: 'offer-name',
- start_datetime: '2021-05-15T19:56:09Z',
- end_datetime: '2100-05-15T19:56:09Z',
- is_current: true,
- },
- ];
-
- SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
- data: {
- results: mockEnterpriseSubsidyResponse,
- },
- });
-
- EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: mockEcommerceResponse,
- },
- });
-
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers({
- enablePortalLearnerCreditManagementScreen: true,
- enterpriseId: TEST_ENTERPRISE_UUID,
- }));
-
- await waitForNextUpdate();
-
- expect(SubsidyApiService.getSubsidyByCustomerUUID).toHaveBeenCalledWith(
- TEST_ENTERPRISE_UUID,
- { subsidyType: 'learner_credit' },
- );
-
- const expectedOffers = [
- {
- id: 'offer-id',
- name: 'offer-name',
- start: '2021-05-15T19:56:09Z',
- end: '2100-05-15T19:56:09Z',
- isCurrent: true,
- source: BUDGET_TYPES.subsidy,
- },
- {
- id: 'uuid',
- name: 'offer-name',
- start: '2021-05-15T19:56:09Z',
- end: '2100-05-15T19:56:09Z',
- isCurrent: true,
- source: BUDGET_TYPES.ecommerce,
- },
- ];
-
- expect(result.current).toEqual({
- offers: expectedOffers,
- isLoading: false,
- canManageLearnerCredit: true,
- });
- });
-
- it('should set canManageLearnerCredit to false if active enterprise offer or subsidy not found', async () => {
- const mockSubsidyServiceResponse = [];
-
- EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: [],
- },
- });
- SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
- data: {
- results: mockSubsidyServiceResponse,
- },
- });
-
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers({
- enablePortalLearnerCreditManagementScreen: true,
- enterpriseId: TEST_ENTERPRISE_UUID,
- }));
-
- await waitForNextUpdate();
-
- expect(SubsidyApiService.getSubsidyByCustomerUUID).toHaveBeenCalledWith(
- TEST_ENTERPRISE_UUID,
- { subsidyType: 'learner_credit' },
- );
-
- const hasActiveOffersOrSubsidies = mockSubsidyServiceResponse.some(offer => offer.is_active);
- let canManageLearnerCredit = false;
-
- if (hasActiveOffersOrSubsidies) {
- canManageLearnerCredit = true;
- }
-
- expect(result.current).toEqual({
- offers: [],
- isLoading: false,
- canManageLearnerCredit,
- });
- });
-
- it('should return the active enterprise offer or subsidy when multiple available', async () => {
- const mockSubsidyServiceResponse = [
- {
- uuid: 'offer-1',
- title: 'offer-name',
- active_datetime: '2005-05-15T19:56:09Z',
- expiration_datetime: '2006-05-15T19:56:09Z',
- is_active: false,
- },
- {
- uuid: 'offer-2',
- title: 'offer-name-2',
- active_datetime: '2006-05-15T19:56:09Z',
- expiration_datetime: '2099-05-15T19:56:09Z',
- is_active: true,
- },
- ];
- const mockOfferData = [
- {
- id: 'offer-1',
- name: 'offer-name',
- start: '2005-05-15T19:56:09Z',
- end: '2006-05-15T19:56:09Z',
- isCurrent: false,
- source: BUDGET_TYPES.subsidy,
- },
- {
- id: 'offer-2',
- name: 'offer-name-2',
- start: '2006-05-15T19:56:09Z',
- end: '2099-05-15T19:56:09Z',
- isCurrent: true,
- source: BUDGET_TYPES.subsidy,
- },
- ];
-
- EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({
- data: {
- results: [],
- },
- });
- SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({
- data: {
- results: mockSubsidyServiceResponse,
- },
- });
-
- const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffers({
- enablePortalLearnerCreditManagementScreen: true,
- enterpriseId: TEST_ENTERPRISE_UUID,
- }));
-
- await waitForNextUpdate();
-
- expect(SubsidyApiService.getSubsidyByCustomerUUID).toHaveBeenCalledWith(
- TEST_ENTERPRISE_UUID,
- { subsidyType: 'learner_credit' },
- );
- expect(result.current).toEqual({
- offers: mockOfferData,
- isLoading: false,
- canManageLearnerCredit: true,
- });
- });
-});
-
-describe('useCustomerAgreement', () => {
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('should fetch customer agreement for the enterprise', async () => {
- const mockCustomerAgreement = {
- subscriptions: [],
- };
- LicenseManagerApiService.fetchCustomerAgreementData.mockResolvedValueOnce({
- data: {
- results: [mockCustomerAgreement],
- },
- });
- const { result, waitForNextUpdate } = renderHook(() => useCustomerAgreement({
- enterpriseId: TEST_ENTERPRISE_UUID,
- }));
-
- await waitForNextUpdate();
-
- expect(LicenseManagerApiService.fetchCustomerAgreementData).toHaveBeenCalledWith({
- enterprise_customer_uuid: TEST_ENTERPRISE_UUID,
- });
- expect(result.current).toEqual({
- customerAgreement: mockCustomerAgreement,
- isLoading: false,
- });
- });
-
- it('should should not set customer agreement if results are empty', async () => {
- LicenseManagerApiService.fetchCustomerAgreementData.mockResolvedValueOnce({
- data: {
- results: [],
- },
- });
- const { result, waitForNextUpdate } = renderHook(() => useCustomerAgreement({
- enterpriseId: TEST_ENTERPRISE_UUID,
- }));
-
- await waitForNextUpdate();
-
- expect(LicenseManagerApiService.fetchCustomerAgreementData).toHaveBeenCalled();
- expect(result.current).toEqual({
- customerAgreement: undefined,
- isLoading: false,
- });
- });
-});
-
-describe('useCoupons', () => {
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('should fetch coupons for the enterprise', async () => {
- const mockCoupons = [{ uuid: 'test-coupon' }];
- EcommerceApiService.fetchCouponOrders.mockResolvedValueOnce({
- data: {
- results: mockCoupons,
- },
- });
- const { result, waitForNextUpdate } = renderHook(() => useCoupons());
-
- await waitForNextUpdate();
-
- expect(EcommerceApiService.fetchCouponOrders).toHaveBeenCalled();
- expect(result.current).toEqual({
- coupons: mockCoupons,
- isLoading: false,
- });
- });
-});
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx
new file mode 100644
index 0000000000..4a8b09fa08
--- /dev/null
+++ b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx
@@ -0,0 +1,503 @@
+import { renderHook } from '@testing-library/react-hooks/dom';
+import { logError } from '@edx/frontend-platform/logging';
+import dayjs from 'dayjs';
+import { QueryClientProvider } from '@tanstack/react-query';
+
+import { useCoupons, useCustomerAgreement, useEnterpriseBudgets } from '../hooks';
+import EcommerceApiService from '../../../../data/services/EcommerceApiService';
+import LicenseManagerApiService from '../../../../data/services/LicenseManagerAPIService';
+import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import { BUDGET_TYPES } from '../../../EnterpriseApp/data/constants';
+import { queryClient } from '../../../test/testUtils';
+
+jest.mock('@edx/frontend-platform/config', () => ({
+ ...jest.requireActual('@edx/frontend-platform/config'),
+ getConfig: jest.fn(() => ({
+ FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
+ })),
+}));
+jest.mock('@edx/frontend-platform/logging', () => ({
+ ...jest.requireActual('@edx/frontend-platform/logging'),
+ logError: jest.fn(),
+}));
+jest.mock('../../../../data/services/EcommerceApiService');
+jest.mock('../../../../data/services/LicenseManagerAPIService');
+jest.mock('../../../../data/services/EnterpriseAccessApiService');
+jest.mock('../../../../data/services/EnterpriseSubsidyApiService');
+
+const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
+
+describe('useEnterpriseBudgets', () => {
+ const wrapper = ({ children }) => (
+
+ {children}
+
+ );
+
+ const fetchEnterpriseOffersSpy = jest.spyOn(EcommerceApiService, 'fetchEnterpriseOffers').mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+ const getSubsidyByCustomerUUIDSpy = jest.spyOn(SubsidyApiService, 'getSubsidyByCustomerUUID').mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+ const listSubsidyAccessPoliciesSpy = jest.spyOn(EnterpriseAccessApiService, 'listSubsidyAccessPolicies').mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+
+ const mockBudgetStart = dayjs().subtract(1, 'week').toISOString();
+ const mockBudgetEnd = dayjs().add(1, 'week').toISOString();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should not fetch any budgets if enablePortalLearnerCreditManagementScreen is false', async () => {
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: false,
+ isTopDownAssignmentEnabled: false,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ }),
+ { wrapper },
+ );
+
+ expect(EcommerceApiService.fetchEnterpriseOffers).not.toHaveBeenCalled();
+ expect(SubsidyApiService.getSubsidyByCustomerUUID).not.toHaveBeenCalled();
+ expect(EnterpriseAccessApiService.listSubsidyAccessPolicies).not.toHaveBeenCalled();
+
+ await waitForNextUpdate();
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ canManageLearnerCredit: false,
+ budgets: [],
+ },
+ }),
+ );
+ });
+
+ it('should always fetch enterprise offers from ecommerce', async () => {
+ const mockEcommerceResponse = [
+ {
+ id: 'uuid',
+ display_name: 'offer-name',
+ start_datetime: mockBudgetStart,
+ end_datetime: mockBudgetEnd,
+ is_current: true,
+ },
+ ];
+ const mockBudgets = [{
+ id: 'uuid',
+ name: 'offer-name',
+ start: mockBudgetStart,
+ end: mockBudgetEnd,
+ isCurrent: true,
+ source: BUDGET_TYPES.ecommerce,
+ }];
+ fetchEnterpriseOffersSpy.mockResolvedValue({
+ data: {
+ results: mockEcommerceResponse,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: true,
+ }),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(fetchEnterpriseOffersSpy).toHaveBeenCalledTimes(1);
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ budgets: mockBudgets,
+ canManageLearnerCredit: true,
+ },
+ }),
+ );
+ });
+
+ it('should fetch Subsidy-associated budgets from enterprise-subsidy when LC2 feature flag is disabled', async () => {
+ const mockEnterpriseSubsidyResponse = [
+ {
+ uuid: 'offer-id',
+ title: 'offer-name',
+ activeDatetime: mockBudgetStart,
+ expirationDatetime: mockBudgetEnd,
+ isActive: true,
+ },
+ ];
+ getSubsidyByCustomerUUIDSpy.mockResolvedValue({
+ data: {
+ results: mockEnterpriseSubsidyResponse,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ isTopDownAssignmentEnabled: false,
+ }),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(getSubsidyByCustomerUUIDSpy).toHaveBeenCalledTimes(1);
+ expect(getSubsidyByCustomerUUIDSpy).toHaveBeenCalledWith(
+ TEST_ENTERPRISE_UUID,
+ { subsidyType: 'learner_credit' },
+ );
+ expect(listSubsidyAccessPoliciesSpy).not.toHaveBeenCalled();
+
+ const expectedBudgets = [
+ {
+ id: 'offer-id',
+ name: 'offer-name',
+ start: mockBudgetStart,
+ end: mockBudgetEnd,
+ isCurrent: true,
+ source: BUDGET_TYPES.subsidy,
+ },
+ {
+ id: 'uuid',
+ name: 'offer-name',
+ start: mockBudgetStart,
+ end: mockBudgetEnd,
+ isCurrent: true,
+ source: BUDGET_TYPES.ecommerce,
+ },
+ ];
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ budgets: expectedBudgets,
+ canManageLearnerCredit: true,
+ },
+ }),
+ );
+ });
+
+ it('should fetch Subsidy Access Policies (budgets) from enterprise-access when LC2 feature flag is enabled', async () => {
+ const mockEnterprisePoliciesResponse = [
+ {
+ uuid: 'budget-uuid',
+ displayName: 'Budget Display name',
+ subsidyActiveDatetime: mockBudgetStart,
+ subsidyExpirationDatetime: mockBudgetEnd,
+ active: true,
+ aggregates: {
+ spendAvailableUsd: 700,
+ amountRedeemedUsd: 200,
+ amountAllocatedUsd: 100,
+ },
+ },
+ ];
+ fetchEnterpriseOffersSpy.mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+ listSubsidyAccessPoliciesSpy.mockResolvedValue({
+ data: {
+ results: mockEnterprisePoliciesResponse,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ isTopDownAssignmentEnabled: true,
+ }),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(listSubsidyAccessPoliciesSpy).toHaveBeenCalledTimes(1);
+ expect(listSubsidyAccessPoliciesSpy).toHaveBeenCalledWith(TEST_ENTERPRISE_UUID);
+ expect(getSubsidyByCustomerUUIDSpy).not.toHaveBeenCalled();
+
+ const expectedBudgets = [
+ {
+ id: 'budget-uuid',
+ name: 'Budget Display name',
+ start: mockBudgetStart,
+ end: mockBudgetEnd,
+ isCurrent: true,
+ source: BUDGET_TYPES.policy,
+ aggregates: {
+ available: 700,
+ spent: 200,
+ pending: 100,
+ },
+ },
+ ];
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ budgets: expectedBudgets,
+ canManageLearnerCredit: true,
+ },
+ }),
+ );
+ });
+
+ it.each([
+ { isTopDownAssignmentEnabled: false },
+ { isTopDownAssignmentEnabled: true },
+ ])('should log error when budgets API request cannot be fulfilled (%s)', async ({ isTopDownAssignmentEnabled }) => {
+ const mockListOffersError = 'error_list_offers';
+ const mockListSubsidiesError = 'error_list_subsidies';
+ const mockListPoliciesError = 'error_list_policies';
+
+ fetchEnterpriseOffersSpy.mockRejectedValue(mockListOffersError);
+ getSubsidyByCustomerUUIDSpy.mockRejectedValue(mockListSubsidiesError);
+ listSubsidyAccessPoliciesSpy.mockRejectedValue(mockListPoliciesError);
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ isTopDownAssignmentEnabled,
+ }),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ // Assert the failed enterprise offers API call
+ expect(logError).toHaveBeenCalledWith(mockListOffersError);
+
+ if (isTopDownAssignmentEnabled) {
+ // Assert the failed API call to list subsidy access policies from enterprise-access
+ expect(listSubsidyAccessPoliciesSpy).toHaveBeenCalledTimes(1);
+ expect(listSubsidyAccessPoliciesSpy).toHaveBeenCalledWith(TEST_ENTERPRISE_UUID);
+ expect(getSubsidyByCustomerUUIDSpy).not.toHaveBeenCalled();
+
+ expect(logError).toHaveBeenCalledWith(mockListPoliciesError);
+ } else {
+ // Assert the failed API call to list subsidies from enterprise-subsidy
+ expect(getSubsidyByCustomerUUIDSpy).toHaveBeenCalledTimes(1);
+ expect(getSubsidyByCustomerUUIDSpy).toHaveBeenCalledWith(
+ TEST_ENTERPRISE_UUID,
+ { subsidyType: 'learner_credit' },
+ );
+ expect(listSubsidyAccessPoliciesSpy).not.toHaveBeenCalled();
+
+ expect(logError).toHaveBeenCalledWith(mockListSubsidiesError);
+ }
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ budgets: [],
+ canManageLearnerCredit: false,
+ },
+ }),
+ );
+ });
+
+ it.each('should set `canManageLearnerCredit` to false if no budgets are found', async () => {
+ fetchEnterpriseOffersSpy.mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+ getSubsidyByCustomerUUIDSpy.mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+ listSubsidyAccessPoliciesSpy.mockResolvedValue({
+ data: {
+ results: [],
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ }),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ budgets: [],
+ canManageLearnerCredit: false,
+ },
+ }),
+ );
+ });
+
+ it('should return the active enterprise offer or subsidy when multiple available', async () => {
+ const mockSubsidyServiceResponse = [
+ {
+ uuid: 'offer-1',
+ title: 'offer-name',
+ active_datetime: '2005-05-15T19:56:09Z',
+ expiration_datetime: '2006-05-15T19:56:09Z',
+ is_active: false,
+ },
+ {
+ uuid: 'offer-2',
+ title: 'offer-name-2',
+ active_datetime: '2006-05-15T19:56:09Z',
+ expiration_datetime: '2099-05-15T19:56:09Z',
+ is_active: true,
+ },
+ ];
+ const mockOfferData = [
+ {
+ id: 'offer-1',
+ name: 'offer-name',
+ start: '2005-05-15T19:56:09Z',
+ end: '2006-05-15T19:56:09Z',
+ isCurrent: false,
+ source: BUDGET_TYPES.subsidy,
+ },
+ {
+ id: 'offer-2',
+ name: 'offer-name-2',
+ start: '2006-05-15T19:56:09Z',
+ end: '2099-05-15T19:56:09Z',
+ isCurrent: true,
+ source: BUDGET_TYPES.subsidy,
+ },
+ ];
+
+ fetchEnterpriseOffersSpy.mockResolvedValueOnce({
+ data: {
+ results: [],
+ },
+ });
+ getSubsidyByCustomerUUIDSpy.mockResolvedValueOnce({
+ data: {
+ results: mockSubsidyServiceResponse,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ }),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(getSubsidyByCustomerUUIDSpy).toHaveBeenCalledTimes(1);
+ expect(getSubsidyByCustomerUUIDSpy).toHaveBeenCalledWith(
+ TEST_ENTERPRISE_UUID,
+ { subsidyType: 'learner_credit' },
+ );
+ expect(result.current).toEqual(
+ expect.objectContaining({
+ isLoading: false,
+ data: {
+ budgets: mockOfferData,
+ canManageLearnerCredit: true,
+ },
+ }),
+ );
+ });
+});
+
+describe('useCustomerAgreement', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fetch customer agreement for the enterprise', async () => {
+ const mockCustomerAgreement = {
+ subscriptions: [],
+ };
+ LicenseManagerApiService.fetchCustomerAgreementData.mockResolvedValueOnce({
+ data: {
+ results: [mockCustomerAgreement],
+ },
+ });
+ const { result, waitForNextUpdate } = renderHook(() => useCustomerAgreement({
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ }));
+
+ await waitForNextUpdate();
+
+ expect(LicenseManagerApiService.fetchCustomerAgreementData).toHaveBeenCalledWith({
+ enterprise_customer_uuid: TEST_ENTERPRISE_UUID,
+ });
+ expect(result.current).toEqual({
+ customerAgreement: mockCustomerAgreement,
+ isLoading: false,
+ });
+ });
+
+ it('should should not set customer agreement if results are empty', async () => {
+ LicenseManagerApiService.fetchCustomerAgreementData.mockResolvedValueOnce({
+ data: {
+ results: [],
+ },
+ });
+ const { result, waitForNextUpdate } = renderHook(() => useCustomerAgreement({
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ }));
+
+ await waitForNextUpdate();
+
+ expect(LicenseManagerApiService.fetchCustomerAgreementData).toHaveBeenCalled();
+ expect(result.current).toEqual({
+ customerAgreement: undefined,
+ isLoading: false,
+ });
+ });
+});
+
+describe('useCoupons', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fetch coupons for the enterprise', async () => {
+ const mockCoupons = [{ uuid: 'test-coupon' }];
+ EcommerceApiService.fetchCouponOrders.mockResolvedValueOnce({
+ data: {
+ results: mockCoupons,
+ },
+ });
+ const { result, waitForNextUpdate } = renderHook(() => useCoupons());
+
+ await waitForNextUpdate();
+
+ expect(EcommerceApiService.fetchCouponOrders).toHaveBeenCalled();
+ expect(result.current).toEqual({
+ coupons: mockCoupons,
+ isLoading: false,
+ });
+ });
+});
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/index.test.js b/src/components/EnterpriseSubsidiesContext/data/tests/index.test.js
index 2157455f44..2915b49296 100644
--- a/src/components/EnterpriseSubsidiesContext/data/tests/index.test.js
+++ b/src/components/EnterpriseSubsidiesContext/data/tests/index.test.js
@@ -9,37 +9,58 @@ jest.mock('../hooks');
const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
describe('useEnterpriseSubsidiesContext', () => {
- const basicProps = { enablePortalLearnerCreditManagementScreen: true, enterpriseId: TEST_ENTERPRISE_UUID };
+ const basicProps = {
+ enablePortalLearnerCreditManagementScreen: true,
+ enterpriseId: TEST_ENTERPRISE_UUID,
+ };
it.each([
{
- offers: [{ uuid: 'offer-id' }],
+ isLoadingBudgets: false,
+ budgets: [{ uuid: 'offer-id' }],
customerAgreement: { subscriptions: [{ uuid: 'subscription-id' }] },
coupons: [{ uuid: 'coupon-id' }],
expectedEnterpriseSubsidyTypes: [
- SUBSIDY_TYPES.offer,
+ SUBSIDY_TYPES.budget,
SUBSIDY_TYPES.coupon,
- SUBSIDY_TYPES.license],
+ SUBSIDY_TYPES.license,
+ ],
},
{
- offers: [],
+ isLoadingBudgets: true,
+ budgets: undefined,
customerAgreement: { subscriptions: [{ uuid: 'subscription-id' }] },
coupons: [{ uuid: 'coupon-id' }],
expectedEnterpriseSubsidyTypes: [
SUBSIDY_TYPES.coupon,
- SUBSIDY_TYPES.license],
+ SUBSIDY_TYPES.license,
+ ],
},
{
- offers: [],
+ isLoadingBudgets: false,
+ budgets: [],
+ customerAgreement: { subscriptions: [{ uuid: 'subscription-id' }] },
+ coupons: [{ uuid: 'coupon-id' }],
+ expectedEnterpriseSubsidyTypes: [
+ SUBSIDY_TYPES.coupon,
+ SUBSIDY_TYPES.license,
+ ],
+ },
+ {
+ isLoadingBudgets: false,
+ budgets: [],
customerAgreement: { subscriptions: [{ uuid: 'subscription-id' }] },
coupons: [],
expectedEnterpriseSubsidyTypes: [SUBSIDY_TYPES.license],
},
- ])('returns the correct enterpriseSubsidyTypes', ({
- offers, customerAgreement, coupons, expectedEnterpriseSubsidyTypes,
+ ])('returns the correct enterpriseSubsidyTypes (%s)', ({
+ isLoadingBudgets, budgets, customerAgreement, coupons, expectedEnterpriseSubsidyTypes,
}) => {
- hooks.useEnterpriseOffers.mockReturnValue({
- offers,
+ hooks.useEnterpriseBudgets.mockReturnValue({
+ data: isLoadingBudgets ? undefined : {
+ budgets,
+ canManageLearnerCredit: !!budgets.length,
+ },
});
hooks.useCustomerAgreement.mockReturnValue({
customerAgreement,
diff --git a/src/components/EnterpriseSubsidiesContext/index.jsx b/src/components/EnterpriseSubsidiesContext/index.jsx
index 59d4a540bd..234e1fad14 100644
--- a/src/components/EnterpriseSubsidiesContext/index.jsx
+++ b/src/components/EnterpriseSubsidiesContext/index.jsx
@@ -1,15 +1,26 @@
import { createContext, useMemo } from 'react';
import { SUBSIDY_TYPES } from '../../data/constants/subsidyTypes';
-import { useCoupons, useCustomerAgreement, useEnterpriseOffers } from './data/hooks';
+import { useCoupons, useCustomerAgreement, useEnterpriseBudgets } from './data/hooks';
export const EnterpriseSubsidiesContext = createContext();
-export const useEnterpriseSubsidiesContext = ({ enablePortalLearnerCreditManagementScreen, enterpriseId }) => {
+export const useEnterpriseSubsidiesContext = ({
+ enablePortalLearnerCreditManagementScreen,
+ enterpriseId,
+ isTopDownAssignmentEnabled,
+}) => {
const {
- offers,
- canManageLearnerCredit,
- isLoading: isLoadingOffers,
- } = useEnterpriseOffers({ enablePortalLearnerCreditManagementScreen, enterpriseId });
+ isLoading: isLoadingBudgets,
+ data: budgetsOverview,
+ } = useEnterpriseBudgets({
+ enablePortalLearnerCreditManagementScreen,
+ enterpriseId,
+ isTopDownAssignmentEnabled,
+ });
+ const {
+ budgets = [],
+ canManageLearnerCredit = false,
+ } = budgetsOverview || {};
const {
customerAgreement,
@@ -24,8 +35,8 @@ export const useEnterpriseSubsidiesContext = ({ enablePortalLearnerCreditManagem
const enterpriseSubsidyTypes = useMemo(() => {
const subsidyTypes = [];
- if (offers.length > 0) {
- subsidyTypes.push(SUBSIDY_TYPES.offer);
+ if (budgets.length > 0) {
+ subsidyTypes.push(SUBSIDY_TYPES.budget);
}
if (coupons.length > 0) {
@@ -36,18 +47,18 @@ export const useEnterpriseSubsidiesContext = ({ enablePortalLearnerCreditManagem
subsidyTypes.push(SUBSIDY_TYPES.license);
}
return subsidyTypes;
- }, [offers.length, coupons.length, customerAgreement]);
+ }, [budgets.length, coupons.length, customerAgreement]);
- const isLoading = isLoadingOffers || isLoadingCustomerAgreement || isLoadingCoupons;
+ const isLoading = isLoadingBudgets || isLoadingCustomerAgreement || isLoadingCoupons;
const context = useMemo(() => ({
- offers,
+ budgets,
customerAgreement,
coupons,
canManageLearnerCredit,
enterpriseSubsidyTypes,
isLoading,
- }), [offers, customerAgreement, coupons, canManageLearnerCredit, enterpriseSubsidyTypes, isLoading]);
+ }), [budgets, customerAgreement, coupons, canManageLearnerCredit, enterpriseSubsidyTypes, isLoading]);
return context;
};
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index 41ae121150..db5478548c 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -1,42 +1,60 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { useOfferSummary } from './data';
+import { useSubsidySummaryAnalyticsApi } from './data';
import SubBudgetCard from './SubBudgetCard';
import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
/**
- * Renders one or more budget cards for the given offer (enterprise or Subsidy from enterprise-subsidy). If the offer is
- * an enterprise offer, it will render a single card. If the offer is a Subsidy, it will render one card for
- * each associated budget.
+ * Renders one or more budget cards for the given budget. If the budget is
+ * an enterprise offer, it will render a single card. If the budget is a Subsidy,
+ * it will render one card for each associated budget. If the budget is a Policy,
+ * it will also render a single card.
+ *
+ * @param {Object} budget Represents either:
+ * - Enterprise Offer (ecommerce)
+ * - Subsidy (enterprise-subsidy)
+ * - Policy (enterprise-access)
*
- * @param {*} offer Represents either an enterprise offer or a Subsidy (enterprise-subsidy).
* @returns Budget card component(s).
*/
const BudgetCard = ({
- offer,
+ budget,
enterpriseUUID,
enterpriseSlug,
- offerType,
- displayName,
}) => {
- const { start, end } = offer;
-
const {
- isLoading: isLoadingOfferSummary,
- offerSummary,
- } = useOfferSummary(enterpriseUUID, offer);
+ isLoading: isLoadingSubsidySummaryAnalyticsApi,
+ subsidySummary: subsidySummaryAnalyticsApi,
+ } = useSubsidySummaryAnalyticsApi(enterpriseUUID, budget);
+
+ // Subsidy Access Policies will always have a single budget, so we can render a single card
+ // without relying on `useSubsidySummaryAnalyticsApi`.
+ if (budget.source === BUDGET_TYPES.policy) {
+ return (
+
+ );
+ }
- // Enterprise Offers will always have a single budget, so we can render a single card.
- if (offerType === BUDGET_TYPES.ecommerce) {
+ // Enterprise Offers (ecommerce) will always have a single budget, so we can render a single card.
+ if (budget.source === BUDGET_TYPES.ecommerce) {
return (
);
@@ -44,37 +62,41 @@ const BudgetCard = ({
// We're now dealing with a Subsidy (enterprise-subsidy), but the analytics API isn't aware of any
// associated budgets; nothing should display.
- if (!offerSummary?.budgetsSummary) {
+ if (!subsidySummaryAnalyticsApi?.budgetsSummary) {
return null;
}
- // Render a card for each associated budget with the Subsidy (enterprise-subsidy)
- return offerSummary.budgetsSummary.map((budget) => (
+ // Render a card for each associated Budget (Policy, enterprise-access) with the Subsidy (enterprise-subsidy)
+ return subsidySummaryAnalyticsApi.budgetsSummary.map((subBudget) => (
));
};
BudgetCard.propTypes = {
- offer: PropTypes.shape({
- id: PropTypes.string.isRequired,
+ budget: PropTypes.shape({
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
name: PropTypes.string.isRequired,
start: PropTypes.string.isRequired,
end: PropTypes.string.isRequired,
+ source: PropTypes.oneOf(Object.values(BUDGET_TYPES)).isRequired,
+ aggregates: PropTypes.shape({
+ available: PropTypes.number.isRequired,
+ spent: PropTypes.number.isRequired,
+ pending: PropTypes.number,
+ }),
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
- offerType: PropTypes.oneOf(Object.values(BUDGET_TYPES)).isRequired,
- displayName: PropTypes.string,
};
export default BudgetCard;
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index a9e114744f..6cfa8d0d1b 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -3,15 +3,15 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
-import { useBudgetId, useOfferRedemptions } from './data';
+import { useBudgetId, useBudgetRedemptions } from './data';
const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
const {
isLoading,
- offerRedemptions,
- fetchOfferRedemptions,
- } = useOfferRedemptions(
+ budgetRedemptions,
+ fetchBudgetRedemptions,
+ } = useBudgetRedemptions(
enterpriseUUID,
enterpriseOfferId,
subsidyAccessPolicyId,
@@ -31,8 +31,8 @@ const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
);
diff --git a/src/components/learner-credit-management/MultipleBudgetsPage.jsx b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
index fe12ab2719..b8bb186def 100644
--- a/src/components/learner-credit-management/MultipleBudgetsPage.jsx
+++ b/src/components/learner-credit-management/MultipleBudgetsPage.jsx
@@ -7,12 +7,12 @@ import {
Card,
Hyperlink,
Container,
+ Skeleton,
} from '@edx/paragon';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
-import Hero from '../Hero';
-import LoadingMessage from '../LoadingMessage';
+import Hero from '../Hero';
import MultipleBudgetsPicker from './MultipleBudgetsPicker';
import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext';
@@ -25,13 +25,19 @@ const MultipleBudgetsPage = ({
enterpriseSlug,
enableLearnerPortal,
}) => {
- const { offers, isLoading } = useContext(EnterpriseSubsidiesContext);
+ const { budgets, isLoading } = useContext(EnterpriseSubsidiesContext);
if (isLoading) {
- return ;
+ return (
+ <>
+
+
+ Loading budgets...
+ >
+ );
}
- if (offers.length === 0) {
+ if (budgets.length === 0) {
return (
@@ -66,7 +72,7 @@ const MultipleBudgetsPage = ({
{
- const orderedOffers = orderOffers(offers);
-
+ const orderedBudgets = orderBudgets(budgets);
return (
@@ -25,15 +24,13 @@ const MultipleBudgetsPicker = ({
- {orderedOffers?.map(offer => (
+ {orderedBudgets.map(budget => (
))}
@@ -44,7 +41,7 @@ const MultipleBudgetsPicker = ({
};
MultipleBudgetsPicker.propTypes = {
- offers: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ budgets: PropTypes.arrayOf(PropTypes.shape()).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
enableLearnerPortal: PropTypes.bool.isRequired,
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
index 3841aebea3..943090426a 100644
--- a/src/components/learner-credit-management/SubBudgetCard.jsx
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -18,6 +18,7 @@ const SubBudgetCard = ({
start,
end,
available,
+ pending,
spent,
displayName,
enterpriseSlug,
@@ -40,7 +41,7 @@ const SubBudgetCard = ({
const subtitle = (
{budgetLabel.status}
-
+
{budgetLabel.term} {formattedDate}
@@ -50,7 +51,6 @@ const SubBudgetCard = ({
(
+ const renderCardSection = () => (
Balance}
muted
>
-
- Available
- {formatPrice(availableBalance)}
+
+ Available
+ {formatPrice(available)}
-
- Spent
- {formatPrice(spentBalance)}
+ {pending > 0 && (
+
+ Pending
+ {formatPrice(pending)}
+
+ )}
+
+ Spent
+ {formatPrice(spent)}
@@ -84,8 +90,10 @@ const SubBudgetCard = ({
isLoading={isLoading}
>
- {renderCardHeader(displayName || 'Overview', id)}
- {budgetLabel.status !== BUDGET_STATUSES.scheduled && renderCardSection(available, spent)}
+
+ {renderCardHeader(displayName || 'Overview', id)}
+ {budgetLabel.status !== BUDGET_STATUSES.scheduled && renderCardSection()}
+
);
@@ -93,12 +101,13 @@ const SubBudgetCard = ({
SubBudgetCard.propTypes = {
enterpriseSlug: PropTypes.string.isRequired,
- id: PropTypes.string,
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
start: PropTypes.string,
end: PropTypes.string,
spent: PropTypes.number,
isLoading: PropTypes.bool,
available: PropTypes.number,
+ pending: PropTypes.number,
displayName: PropTypes.string,
};
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 7f20e7e8aa..149a7418f0 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -120,14 +120,21 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
mutate(mutationArgs, {
onSuccess: ({ created, noChange, updated }) => {
setAssignButtonState('complete');
+ // Ensure the budget and budgets queries are invalidated so that the relevant
+ // queries become stale and refetches new updated data from the API.
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
});
+ queryClient.invalidateQueries({
+ queryKey: learnerCreditManagementQueryKeys.budgets(enterpriseId),
+ });
handleCloseAssignmentModal();
onSuccessEnterpriseTrackEvents({
created, noChange, updated,
});
displayToastForAssignmentAllocation({ totalLearnersAssigned: learnerEmails.length });
+
+ // Navigate to the activity tab
history.push(pathToActivityTab);
},
onError: (err) => {
diff --git a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
index 42697fc761..2e90beae99 100644
--- a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
@@ -487,10 +487,13 @@ describe('Course card works as expected', () => {
}
} else {
// Verify success state
- expect(mockInvalidateQueries).toHaveBeenCalledTimes(1);
+ expect(mockInvalidateQueries).toHaveBeenCalledTimes(2);
expect(mockInvalidateQueries).toHaveBeenCalledWith({
queryKey: learnerCreditManagementQueryKeys.budget(mockSubsidyAccessPolicy.uuid),
});
+ expect(mockInvalidateQueries).toHaveBeenCalledWith({
+ queryKey: learnerCreditManagementQueryKeys.budgets(enterpriseUUID),
+ });
expect(getButtonElement('Assigned', { screenOverride: assignmentModal })).toHaveAttribute('aria-disabled', 'true');
await waitFor(() => {
// Verify all modals close (error modal + assignment modal)
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 11dd744498..004b905118 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -68,7 +68,7 @@ export const ASSIGNMENT_STATUS_MODAL_MAX_WIDTH = 480;
// Inspired by https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories.
export const learnerCreditManagementQueryKeys = {
all: ['learner-credit-management'],
- budgets: () => [...learnerCreditManagementQueryKeys.all, 'budgets'],
+ budgets: (enterpriseId) => [...learnerCreditManagementQueryKeys.all, 'budgets', enterpriseId],
budget: (budgetId) => [...learnerCreditManagementQueryKeys.all, 'budget', budgetId],
budgetActivity: (budgetId) => [...learnerCreditManagementQueryKeys.budget(budgetId), 'activity'],
budgetActivityOverview: (budgetId) => [...learnerCreditManagementQueryKeys.budgetActivity(budgetId), 'overview'],
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index 6da548a12b..61731bd38e 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -1,6 +1,6 @@
export { default as useBudgetDetailTabs } from './useBudgetDetailTabs';
-export { default as useOfferSummary } from './useOfferSummary';
-export { default as useOfferRedemptions } from './useOfferRedemptions';
+export { default as useSubsidySummaryAnalyticsApi } from './useSubsidySummaryAnalyticsApi';
+export { default as useBudgetRedemptions } from './useBudgetRedemptions';
export { default as useBudgetContentAssignments } from './useBudgetContentAssignments';
export { default as useBudgetId } from './useBudgetId';
export { default as useSubsidyAccessPolicy } from './useSubsidyAccessPolicy';
diff --git a/src/components/learner-credit-management/data/hooks/tests/useOfferRedemptions.test.jsx b/src/components/learner-credit-management/data/hooks/tests/useBudgetRedemptions.test.jsx
similarity index 91%
rename from src/components/learner-credit-management/data/hooks/tests/useOfferRedemptions.test.jsx
rename to src/components/learner-credit-management/data/hooks/tests/useBudgetRedemptions.test.jsx
index 5e8d29a495..dbf59cc0d9 100644
--- a/src/components/learner-credit-management/data/hooks/tests/useOfferRedemptions.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/tests/useBudgetRedemptions.test.jsx
@@ -2,7 +2,7 @@ import { QueryClientProvider } from '@tanstack/react-query';
import { act, renderHook } from '@testing-library/react-hooks/dom';
import { camelCaseObject } from '@edx/frontend-platform/utils';
-import useOfferRedemptions from '../useOfferRedemptions';
+import useBudgetRedemptions from '../useBudgetRedemptions';
import useSubsidyAccessPolicy from '../useSubsidyAccessPolicy';
import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
import SubsidyApiService from '../../../../../data/services/EnterpriseSubsidyApiService';
@@ -57,7 +57,7 @@ const wrapper = ({ children }) => (
{children}
);
-describe('useOfferRedemptions', () => {
+describe('useBudgetRedemptions', () => {
beforeEach(() => {
jest.clearAllMocks();
});
@@ -88,21 +88,21 @@ describe('useOfferRedemptions', () => {
useSubsidyAccessPolicy.mockReturnValue({ data: { subsidyUuid } });
const { result, waitForNextUpdate } = renderHook(
- () => useOfferRedemptions(TEST_ENTERPRISE_UUID, offerId, budgetId, isTopDownAssignmentEnabled),
+ () => useBudgetRedemptions(TEST_ENTERPRISE_UUID, offerId, budgetId, isTopDownAssignmentEnabled),
{ wrapper },
);
expect(result.current).toMatchObject({
- offerRedemptions: {
+ budgetRedemptions: {
itemCount: 0,
pageCount: 0,
results: [],
},
isLoading: true,
- fetchOfferRedemptions: expect.any(Function),
+ fetchBudgetRedemptions: expect.any(Function),
});
act(() => {
- result.current.fetchOfferRedemptions({
+ result.current.fetchBudgetRedemptions({
pageIndex: 0, // `DataTable` uses zero-based indexing
pageSize: 20,
sortBy: [
@@ -151,13 +151,13 @@ describe('useOfferRedemptions', () => {
}] : camelCaseObject(mockOfferEnrollments);
expect(result.current).toMatchObject({
- offerRedemptions: {
+ budgetRedemptions: {
itemCount: 100,
pageCount: 5,
results: mockExpectedResultsObj,
},
isLoading: false,
- fetchOfferRedemptions: expect.any(Function),
+ fetchBudgetRedemptions: expect.any(Function),
});
});
});
diff --git a/src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js b/src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js
deleted file mode 100644
index 40ccc6df01..0000000000
--- a/src/components/learner-credit-management/data/hooks/tests/useOfferSummary.test.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import { renderHook } from '@testing-library/react-hooks/dom';
-
-import useOfferSummary from '../useOfferSummary';
-import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
-
-jest.mock('@edx/frontend-platform/config', () => ({
- getConfig: jest.fn(() => ({
- FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
- })),
-}));
-jest.mock('../../../../../data/services/EnterpriseDataApiService');
-
-const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
-const TEST_ENTERPRISE_OFFER_ID = 1;
-
-const mockOfferSummary = {
- offer_id: TEST_ENTERPRISE_OFFER_ID,
- status: 'Open',
- enterprise_customer_uuid: TEST_ENTERPRISE_UUID,
- amount_of_offer_spent: 200.00,
- max_discount: 5000.00,
- percent_of_offer_spent: 0.04,
- remaining_balance: 4800.00,
-};
-const mockEnterpriseOffer = {
- id: TEST_ENTERPRISE_OFFER_ID,
-};
-
-describe('useOfferSummary', () => {
- it('should handle null enterprise offer', async () => {
- const { result } = renderHook(() => useOfferSummary(TEST_ENTERPRISE_UUID));
-
- expect(result.current).toEqual({
- offerSummary: undefined,
- isLoading: false,
- });
- });
-
- it('should fetch summary data for enterprise offer', async () => {
- EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockResolvedValueOnce({ data: mockOfferSummary });
- const { result, waitForNextUpdate } = renderHook(() => useOfferSummary(TEST_ENTERPRISE_UUID, mockEnterpriseOffer));
-
- expect(result.current).toEqual({
- offerSummary: undefined,
- isLoading: true,
- });
-
- await waitForNextUpdate();
-
- expect(EnterpriseDataApiService.fetchEnterpriseOfferSummary).toHaveBeenCalled();
- const expectedResult = {
- totalFunds: 5000,
- redeemedFunds: 200,
- redeemedFundsExecEd: NaN,
- redeemedFundsOcm: NaN,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerId: 1,
- budgetsSummary: [],
- offerType: undefined,
- };
- expect(result.current).toEqual({
- offerSummary: expectedResult,
- isLoading: false,
- });
- });
-});
diff --git a/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js b/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
new file mode 100644
index 0000000000..320f51d685
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
@@ -0,0 +1,93 @@
+import { renderHook } from '@testing-library/react-hooks/dom';
+import { logError } from '@edx/frontend-platform/logging';
+
+import useSubsidySummaryAnalyticsApi from '../useSubsidySummaryAnalyticsApi';
+import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
+
+jest.mock('@edx/frontend-platform/config', () => ({
+ ...jest.requireActual('@edx/frontend-platform/config'),
+ getConfig: jest.fn(() => ({
+ FEATURE_LEARNER_CREDIT_MANAGEMENT: true,
+ })),
+}));
+jest.mock('@edx/frontend-platform/logging', () => ({
+ ...jest.requireActual('@edx/frontend-platform/logging'),
+ logError: jest.fn(),
+}));
+jest.mock('../../../../../data/services/EnterpriseDataApiService');
+
+const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
+const TEST_ENTERPRISE_OFFER_ID = 1;
+
+const mockOfferSummary = {
+ offer_id: TEST_ENTERPRISE_OFFER_ID,
+ status: 'Open',
+ enterprise_customer_uuid: TEST_ENTERPRISE_UUID,
+ amount_of_offer_spent: 200.00,
+ max_discount: 5000.00,
+ percent_of_offer_spent: 0.04,
+ remaining_balance: 4800.00,
+};
+const mockEnterpriseOffer = {
+ id: TEST_ENTERPRISE_OFFER_ID,
+};
+
+describe('useSubsidySummaryAnalyticsApi', () => {
+ it('should handle null enterprise offer', async () => {
+ const { result } = renderHook(() => useSubsidySummaryAnalyticsApi(TEST_ENTERPRISE_UUID));
+
+ expect(result.current).toEqual({
+ offerSummary: undefined,
+ isLoading: false,
+ });
+ });
+
+ it.each([
+ { shouldThrowApiException: false },
+ { shouldThrowApiException: true },
+ ])('should fetch summary data for enterprise offer (%s)', async ({ shouldThrowApiException }) => {
+ const mockFetchError = 'mock fetch error';
+ if (shouldThrowApiException) {
+ EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockRejectedValueOnce(mockFetchError);
+ } else {
+ EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockResolvedValueOnce({ data: mockOfferSummary });
+ }
+ const {
+ result,
+ waitForNextUpdate,
+ } = renderHook(() => useSubsidySummaryAnalyticsApi(TEST_ENTERPRISE_UUID, mockEnterpriseOffer));
+
+ expect(result.current).toEqual({
+ subsidySummary: undefined,
+ isLoading: true,
+ });
+
+ await waitForNextUpdate();
+
+ expect(EnterpriseDataApiService.fetchEnterpriseOfferSummary).toHaveBeenCalled();
+
+ if (shouldThrowApiException) {
+ expect(logError).toHaveBeenCalledWith(mockFetchError);
+ expect(result.current).toEqual({
+ subsidySummary: undefined,
+ isLoading: false,
+ });
+ } else {
+ const expectedResult = {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ redeemedFundsExecEd: NaN,
+ redeemedFundsOcm: NaN,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerId: 1,
+ budgetsSummary: [],
+ offerType: undefined,
+ };
+ expect(result.current).toEqual({
+ subsidySummary: expectedResult,
+ isLoading: false,
+ });
+ }
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js b/src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js
similarity index 91%
rename from src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
rename to src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js
index 64618cae8b..bb3ed2fbf5 100644
--- a/src/components/learner-credit-management/data/hooks/useOfferRedemptions.js
+++ b/src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js
@@ -46,7 +46,7 @@ const applyFiltersToOptions = (filters, options, shouldFetchSubsidyTransactions
}
};
-const useOfferRedemptions = (
+const useBudgetRedemptions = (
enterpriseUUID,
offerId = null,
budgetId = null,
@@ -54,14 +54,14 @@ const useOfferRedemptions = (
) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
- const [offerRedemptions, setOfferRedemptions] = useState({
+ const [budgetRedemptions, setBudgetRedemptions] = useState({
itemCount: 0,
pageCount: 0,
results: [],
});
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(budgetId);
- const fetchOfferRedemptions = useCallback((args) => {
+ const fetchBudgetRedemptions = useCallback((args) => {
const fetch = async () => {
try {
const shouldFetchSubsidyTransactions = budgetId && isTopDownAssignmentEnabled;
@@ -103,7 +103,7 @@ const useOfferRedemptions = (
transformedTableResults = transformUtilizationTableResults(data.results);
}
- setOfferRedemptions({
+ setBudgetRedemptions({
itemCount: data.count,
pageCount: data.numPages,
results: transformedTableResults,
@@ -139,13 +139,16 @@ const useOfferRedemptions = (
subsidyAccessPolicy?.subsidyUuid,
]);
- const debouncedFetchOfferRedemptions = useMemo(() => debounce(fetchOfferRedemptions, 300), [fetchOfferRedemptions]);
+ const debouncedFetchBudgetRedemptions = useMemo(
+ () => debounce(fetchBudgetRedemptions, 300),
+ [fetchBudgetRedemptions],
+ );
return {
isLoading,
- offerRedemptions,
- fetchOfferRedemptions: debouncedFetchOfferRedemptions,
+ budgetRedemptions,
+ fetchBudgetRedemptions: debouncedFetchBudgetRedemptions,
};
};
-export default useOfferRedemptions;
+export default useBudgetRedemptions;
diff --git a/src/components/learner-credit-management/data/hooks/useOfferSummary.js b/src/components/learner-credit-management/data/hooks/useOfferSummary.js
deleted file mode 100644
index 95a90b348b..0000000000
--- a/src/components/learner-credit-management/data/hooks/useOfferSummary.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { useEffect, useState } from 'react';
-import { camelCaseObject } from '@edx/frontend-platform/utils';
-import { logError } from '@edx/frontend-platform/logging';
-
-import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
-import { transformOfferSummary } from '../utils';
-
-const useOfferSummary = (enterpriseUUID, enterpriseOffer) => {
- const [isLoading, setIsLoading] = useState(true);
- const [offerSummary, setOfferSummary] = useState();
-
- useEffect(() => {
- if (!enterpriseOffer) {
- setIsLoading(false);
- return;
- }
-
- const fetchData = async () => {
- try {
- setIsLoading(true);
- const response = await EnterpriseDataApiService.fetchEnterpriseOfferSummary(enterpriseUUID, enterpriseOffer.id);
- const data = camelCaseObject(response.data);
- const transformedOfferSummary = transformOfferSummary(data);
- setOfferSummary(transformedOfferSummary);
- } catch (error) {
- logError(error);
- } finally {
- setIsLoading(false);
- }
- };
-
- fetchData();
- }, [enterpriseUUID, enterpriseOffer]);
-
- return {
- isLoading,
- offerSummary,
- };
-};
-
-export default useOfferSummary;
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js b/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
new file mode 100644
index 0000000000..1dde1b995c
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
@@ -0,0 +1,47 @@
+import { useEffect, useState } from 'react';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+import { logError } from '@edx/frontend-platform/logging';
+
+import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
+import { transformSubsidySummary } from '../utils';
+import { BUDGET_TYPES } from '../../../EnterpriseApp/data/constants';
+
+const useSubsidySummaryAnalyticsApi = (enterpriseUUID, budget) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [subsidySummary, setSubsidySummary] = useState();
+
+ useEffect(() => {
+ // If there is no budget, or the budget is an ecommerce offer or subsidy, fetch the
+ // subsidy summary data from the analytics API.
+ if (!budget || [BUDGET_TYPES.ecommerce, BUDGET_TYPES.subsidy].includes(budget.source)) {
+ setIsLoading(false);
+ return;
+ }
+
+ const fetchData = async () => {
+ try {
+ setIsLoading(true);
+ const response = await EnterpriseDataApiService.fetchEnterpriseOfferSummary(
+ enterpriseUUID,
+ budget.id,
+ );
+ const data = camelCaseObject(response.data);
+ const transformedSubsidySummary = transformSubsidySummary(data);
+ setSubsidySummary(transformedSubsidySummary);
+ } catch (error) {
+ logError(error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [enterpriseUUID, budget]);
+
+ return {
+ isLoading,
+ subsidySummary,
+ };
+};
+
+export default useSubsidySummaryAnalyticsApi;
diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js
index fa4ec2bb23..060f26a979 100644
--- a/src/components/learner-credit-management/data/tests/utils.test.js
+++ b/src/components/learner-credit-management/data/tests/utils.test.js
@@ -1,13 +1,13 @@
-import { transformOfferSummary, getBudgetStatus, orderOffers } from '../utils';
+import { transformSubsidySummary, getBudgetStatus, orderBudgets } from '../utils';
import { EXEC_ED_OFFER_TYPE } from '../constants';
-describe('transformOfferSummary', () => {
- it('should return null if there is no offerSummary', () => {
- expect(transformOfferSummary()).toBeNull();
+describe('transformSubsidySummary', () => {
+ it('should return null if there is no budgetSummary', () => {
+ expect(transformSubsidySummary()).toBeNull();
});
it('should safeguard against bad data', () => {
- const offerSummary = {
+ const budgetSummary = {
maxDiscount: 1,
amountOfOfferSpent: 1.34,
remainingBalance: -0.34,
@@ -15,7 +15,7 @@ describe('transformOfferSummary', () => {
offerType: EXEC_ED_OFFER_TYPE,
};
- expect(transformOfferSummary(offerSummary)).toEqual({
+ expect(transformSubsidySummary(budgetSummary)).toEqual({
totalFunds: 1,
redeemedFunds: 1,
redeemedFundsExecEd: NaN,
@@ -29,7 +29,7 @@ describe('transformOfferSummary', () => {
});
it('should handle when no maxDiscount is not set', () => {
- const offerSummary = {
+ const budgetSummary = {
maxDiscount: null,
amountOfOfferSpent: 100,
remainingBalance: null,
@@ -39,7 +39,7 @@ describe('transformOfferSummary', () => {
budgetsSummary: [],
};
- expect(transformOfferSummary(offerSummary)).toEqual({
+ expect(transformSubsidySummary(budgetSummary)).toEqual({
totalFunds: null,
redeemedFunds: 100,
remainingFunds: null,
@@ -53,7 +53,7 @@ describe('transformOfferSummary', () => {
});
it('should handle when budgetsSummary is provided', () => {
- const offerSummary = {
+ const budgetSummary = {
maxDiscount: 1000,
amountOfOfferSpent: 500,
remainingBalance: 500,
@@ -71,7 +71,7 @@ describe('transformOfferSummary', () => {
}],
};
- expect(transformOfferSummary(offerSummary)).toEqual({
+ expect(transformSubsidySummary(budgetSummary)).toEqual({
totalFunds: 1000,
redeemedFunds: 500,
remainingFunds: 500,
@@ -118,52 +118,52 @@ describe('getBudgetStatus', () => {
});
});
-// Example offer objects for testing
-const offers = [
+// Example Budget objects for testing
+const budgets = [
{
- name: 'Offer 1',
+ name: 'Budget 1',
start: '2023-01-01T00:00:00Z',
end: '2023-01-10T00:00:00Z',
},
{
- name: 'Offer 2',
+ name: 'Budget 2',
start: '2022-12-01T00:00:00Z',
end: '2022-12-20T00:00:00Z',
},
{
- name: 'Offer 3',
+ name: 'Budget 3',
start: '2023-02-01T00:00:00Z',
end: '2023-02-15T00:00:00Z',
},
{
- name: 'Offer 4',
+ name: 'Budget 4',
start: '2023-01-15T00:00:00Z',
end: '2023-01-25T00:00:00Z',
},
];
-describe('orderOffers', () => {
+describe('orderBudgets', () => {
it('should sort offers correctly', () => {
- const sortedOffers = orderOffers(offers);
+ const sortedBudgets = orderBudgets(budgets);
- // Expected order: Active offers (Offer 2), Upcoming offers (Offer 1, Offer 4), Expired offers (Offer 3)
- expect(sortedOffers.map((offer) => offer.name)).toEqual(['Offer 2', 'Offer 1', 'Offer 4', 'Offer 3']);
+ // Expected order: Active budgets (Budget 2), Upcoming budgets (Budget 1, Budget 4), Expired budgets (Budget 3)
+ expect(sortedBudgets.map((budget) => budget.name)).toEqual(['Budget 2', 'Budget 1', 'Budget 4', 'Budget 3']);
});
it('should handle empty input', () => {
- const sortedOffers = orderOffers([]);
- expect(sortedOffers).toEqual([]);
+ const sortedBudgets = orderBudgets([]);
+ expect(sortedBudgets).toEqual([]);
});
it('should handle offers with the same status and end date', () => {
- const duplicateOffers = [
- { name: 'Offer A', start: '2023-01-01T00:00:00Z', end: '2023-01-15T00:00:00Z' },
- { name: 'Offer B', start: '2023-01-01T00:00:00Z', end: '2023-01-15T00:00:00Z' },
+ const duplicateBudgets = [
+ { name: 'Budget A', start: '2023-01-01T00:00:00Z', end: '2023-01-15T00:00:00Z' },
+ { name: 'Budget B', start: '2023-01-01T00:00:00Z', end: '2023-01-15T00:00:00Z' },
];
- const sortedOffers = orderOffers(duplicateOffers);
+ const sortedBudgets = orderBudgets(duplicateBudgets);
// Since both offers have the same status ("active") and end date, they should be sorted alphabetically by name.
- expect(sortedOffers.map((offer) => offer.name)).toEqual(['Offer A', 'Offer B']);
+ expect(sortedBudgets.map((budget) => budget.name)).toEqual(['Budget A', 'Budget B']);
});
});
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index a055fd567c..51746f3eca 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -14,17 +14,20 @@ import EnterpriseDataApiService from '../../../data/services/EnterpriseDataApiSe
import SubsidyApiService from '../../../data/services/EnterpriseSubsidyApiService';
/**
- * Transforms offer summary from API for display in the UI, guarding
+ * Transforms subsidy (offer or Subsidy) summary from API for display in the UI, guarding
* against bad data (e.g., accounting for refunds).
*
- * @param {object} offerSummary Object containing summary about an offer.
- * @returns Object containing transformed summary about an enterprise offer.
+ * @param {object} subsidySummary Object containing summary about a budget.
+ * @returns Object containing transformed summary about a budget.
*/
-export const transformOfferSummary = (offerSummary) => {
- if (!offerSummary) { return null; }
+export const transformSubsidySummary = (subsidySummary) => {
+ if (!subsidySummary) {
+ return null;
+ }
+
const budgetsSummary = [];
- if (offerSummary?.budgets) {
- const budgets = offerSummary?.budgets;
+ if (subsidySummary?.budgets) {
+ const budgets = subsidySummary?.budgets;
for (let i = 0; i < budgets.length; i++) {
const redeemedFunds = budgets[i].amountOfPolicySpent && parseFloat(budgets[i].amountOfPolicySpent);
const remainingFunds = budgets[i].remainingBalance && parseFloat(budgets[i].remainingBalance);
@@ -37,10 +40,10 @@ export const transformOfferSummary = (offerSummary) => {
}
}
- const totalFunds = offerSummary.maxDiscount && parseFloat(offerSummary.maxDiscount);
- let redeemedFunds = offerSummary.amountOfOfferSpent && parseFloat(offerSummary.amountOfOfferSpent);
- let redeemedFundsOcm = offerSummary.amountOfferSpentOcm && parseFloat(offerSummary.amountOfferSpentOcm);
- let redeemedFundsExecEd = offerSummary.amountOfferSpentExecEd && parseFloat(offerSummary.amountOfferSpentExecEd);
+ const totalFunds = subsidySummary.maxDiscount && parseFloat(subsidySummary.maxDiscount);
+ let redeemedFunds = subsidySummary.amountOfOfferSpent && parseFloat(subsidySummary.amountOfOfferSpent);
+ let redeemedFundsOcm = subsidySummary.amountOfferSpentOcm && parseFloat(subsidySummary.amountOfferSpentOcm);
+ let redeemedFundsExecEd = subsidySummary.amountOfferSpentExecEd && parseFloat(subsidySummary.amountOfferSpentExecEd);
// cap redeemed funds at the maximum funds available (`maxDiscount`), if applicable, so we
// don't display redeemed funds > funds available.
@@ -50,19 +53,20 @@ export const transformOfferSummary = (offerSummary) => {
redeemedFundsExecEd = Math.min(redeemedFundsExecEd, totalFunds);
}
- let remainingFunds = offerSummary.remainingBalance && parseFloat(offerSummary.remainingBalance);
+ let remainingFunds = subsidySummary.remainingBalance && parseFloat(subsidySummary.remainingBalance);
// prevent remaining funds from going below $0, if applicable.
if (remainingFunds) {
remainingFunds = Math.max(remainingFunds, 0.0);
}
- let percentUtilized = offerSummary.percentOfOfferSpent && parseFloat(offerSummary.percentOfOfferSpent);
+ let percentUtilized = subsidySummary.percentOfOfferSpent && parseFloat(subsidySummary.percentOfOfferSpent);
// prevent percent utilized from going over 1.0, if applicable.
if (percentUtilized) {
percentUtilized = Math.min(percentUtilized, 1.0);
}
- const { offerType } = offerSummary;
- const { offerId } = offerSummary;
+ const { offerType } = subsidySummary;
+ const { offerId } = subsidySummary;
+
return {
totalFunds,
redeemedFunds,
@@ -172,36 +176,36 @@ export const formatPrice = (price, options = {}) => {
};
/**
- * Orders a list of offers based on their status, end date, and name.
- * Active offers come first, followed by scheduled offers, and then expired offers.
- * Within each status, offers are sorted by their end date and name.
+ * Orders a list of budgets based on their status, end date, and name.
+ * Active budgets come first, followed by scheduled budgets, and then expired budgets.
+ * Within each status, budgets are sorted by their end date and name.
*
- * @param {Array} offers - An array of offer objects.
- * @returns {Array} - The sorted array of offer objects.
+ * @param {Array} budgets - An array of budget objects.
+ * @returns {Array} - The sorted array of budget objects.
*/
-export const orderOffers = (offers) => {
+export const orderBudgets = (budgets) => {
const statusOrder = {
Active: 0,
Scheduled: 1,
Expired: 2,
};
- offers?.sort((offerA, offerB) => {
- const statusA = getBudgetStatus(offerA.start, offerA.end).status;
- const statusB = getBudgetStatus(offerB.start, offerB.end).status;
+ budgets?.sort((budgetA, budgetB) => {
+ const statusA = getBudgetStatus(budgetA.start, budgetA.end).status;
+ const statusB = getBudgetStatus(budgetB.start, budgetB.end).status;
if (statusOrder[statusA] !== statusOrder[statusB]) {
return statusOrder[statusA] - statusOrder[statusB];
}
- if (offerA.end !== offerB.end) {
- return offerA.end.localeCompare(offerB.end);
+ if (budgetA.end !== budgetB.end) {
+ return budgetA.end.localeCompare(budgetB.end);
}
- return offerA.name.localeCompare(offerB.name);
+ return budgetA.name.localeCompare(budgetB.name);
});
- return offers;
+ return budgets;
};
/**
diff --git a/src/components/learner-credit-management/index.jsx b/src/components/learner-credit-management/index.jsx
index 0eaef07494..14f99a54f8 100644
--- a/src/components/learner-credit-management/index.jsx
+++ b/src/components/learner-credit-management/index.jsx
@@ -5,7 +5,7 @@ import MultipleBudgetsPage from './MultipleBudgetsPage';
import BudgetDetailPage from './BudgetDetailPage';
const LearnerCreditManagementRoutes = ({ match }) => (
-
+
(
path={`${match.path}/:budgetId/:activeTabKey?`}
component={BudgetDetailPage}
/>
-
+
);
LearnerCreditManagementRoutes.propTypes = {
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index 7ddc100c8e..739e8e8996 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -13,43 +13,44 @@ import '@testing-library/jest-dom/extend-expect';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import BudgetCard from '../BudgetCard';
-import { useOfferSummary, useOfferRedemptions } from '../data';
+import { formatPrice, useSubsidySummaryAnalyticsApi, useBudgetRedemptions } from '../data';
import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
jest.mock('../data', () => ({
...jest.requireActual('../data'),
- useOfferSummary: jest.fn(),
- useOfferRedemptions: jest.fn(),
+ useSubsidySummaryAnalyticsApi: jest.fn(),
+ useBudgetRedemptions: jest.fn(),
}));
-useOfferSummary.mockReturnValue({
+useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
offerSummary: null,
});
-useOfferRedemptions.mockReturnValue({
+useBudgetRedemptions.mockReturnValue({
isLoading: false,
offerRedemptions: {
itemCount: 0,
pageCount: 0,
results: [],
},
- fetchOfferRedemptions: jest.fn(),
+ fetchBudgetRedemptions: jest.fn(),
});
const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
-const enterpriseId = 'test-enterprise';
+const enterpriseSlug = 'test-enterprise';
const enterpriseUUID = '1234';
const initialStore = {
portalConfiguration: {
- enterpriseId,
+ enterpriseId: enterpriseUUID,
+ enterpriseSlug,
},
};
const store = getMockStore({ ...initialStore });
-const mockEnterpriseOfferId = '123';
-const mockEnterpriseOfferEnrollmentId = 456;
+const mockEnterpriseOfferId = 123;
+const mockBudgetUuid = 'test-budget-uuid';
-const mockOfferDisplayName = 'Test Enterprise Offer';
+const mockBudgetDisplayName = 'Test Enterprise Budget Display Name';
const BudgetCardWrapper = ({ ...rest }) => (
@@ -62,164 +63,181 @@ const BudgetCardWrapper = ({ ...rest }) => (
);
describe(' ', () => {
- describe('with enterprise offer', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
- it('displays correctly for Offers', () => {
- const mockOffer = {
- id: mockEnterpriseOfferId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- };
- const mockOfferRedemption = {
- created: '2022-02-01',
- enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
- };
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerType: 'Site',
- budgetsSummary: [
- {
- id: 123,
- start: '2022-01-01',
- end: '2022-01-01',
- available: 200,
- spent: 100,
- enterpriseSlug: enterpriseId,
- },
- ],
- },
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- results: [mockOfferRedemption],
- itemCount: 1,
- pageCount: 1,
- },
- fetchOfferRedemptions: jest.fn(),
- });
- render( );
- expect(screen.getByText('Overview'));
- expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
- const formattedString = `Expired ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`;
- const elementsWithTestId = screen.getAllByTestId('offer-date');
- const firstElementWithTestId = elementsWithTestId[0];
- expect(firstElementWithTestId).toHaveTextContent(formattedString);
+ it('displays correctly for Enterprise Offers (ecommerce)', () => {
+ const mockBudget = {
+ id: mockEnterpriseOfferId,
+ name: mockBudgetDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ source: BUDGET_TYPES.ecommerce,
+ };
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {
+ totalFunds: mockBudgetAggregates.total,
+ redeemedFunds: mockBudgetAggregates.spent,
+ remainingFunds: mockBudgetAggregates.available,
+ percentUtilized: mockBudgetAggregates.spent / mockBudgetAggregates.total,
+ offerType: 'Site',
+ offerId: mockEnterpriseOfferId,
+ budgetsSummary: [],
+ },
});
- it('renders SubBudgetCard when offerType is ecommerce', () => {
- const mockOffer = {
- id: mockEnterpriseOfferId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- offerType: BUDGET_TYPES.ecommerce,
- };
- const mockOfferRedemption = {
- created: '2022-02-01',
- enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
- };
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerType: 'learner_credit',
- budgetsSummary: [
- {
- id: 123,
- start: '2022-01-01',
- end: '2022-01-01',
- available: 200,
- spent: 100,
- enterpriseSlug: enterpriseId,
- },
- ],
- },
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- results: [mockOfferRedemption],
- itemCount: 1,
- pageCount: 1,
- },
- fetchOfferRedemptions: jest.fn(),
- });
-
- render( );
-
- expect(screen.getByTestId('view-budget')).toBeInTheDocument();
+ render( );
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+
+ // View budget CTA
+ const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
+ expect(viewBudgetCTA).toBeInTheDocument();
+ expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockEnterpriseOfferId}`);
+
+ // Aggregates
+ expect(screen.getByText('Balance')).toBeInTheDocument();
+ expect(screen.getByText('Available')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.available))).toBeInTheDocument();
+ expect(screen.getByText('Spent')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
+ });
+
+ it('displays correctly for Subsidy (enterprise-subsidy)', () => {
+ const mockBudget = {
+ id: mockEnterpriseOfferId,
+ name: mockBudgetDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ source: BUDGET_TYPES.subsidy,
+ };
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {
+ totalFunds: mockBudgetAggregates.total,
+ redeemedFunds: mockBudgetAggregates.spent,
+ remainingFunds: mockBudgetAggregates.available,
+ percentUtilized: mockBudgetAggregates.spent / mockBudgetAggregates.total,
+ offerType: 'Site',
+ offerId: mockEnterpriseOfferId,
+ budgetsSummary: [
+ {
+ id: 'test-subsidy-uuid',
+ start: '2022-01-01',
+ end: '2022-01-01',
+ remainingFunds: mockBudgetAggregates.available,
+ redeemedFunds: mockBudgetAggregates.spent,
+ enterpriseSlug,
+ subsidyAccessPolicyDisplayName: mockBudgetDisplayName,
+ subsidyAccessPolicyUuid: mockBudgetUuid,
+ },
+ ],
+ },
});
- it('renders SubBudgetCard when offerType is not ecommerce', () => {
- const mockOffer = {
- id: mockEnterpriseOfferId,
- name: mockOfferDisplayName,
- start: '2022-01-01',
- end: '2023-01-01',
- offerType: 'otherOfferType',
- };
- const mockOfferRedemption = {
- created: '2022-02-01',
- enterpriseEnrollmentId: mockEnterpriseOfferEnrollmentId,
- };
- useOfferSummary.mockReturnValue({
- isLoading: false,
- offerSummary: {
- totalFunds: 5000,
- redeemedFunds: 200,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerType: 'learner_credit',
- budgetsSummary: [
- {
- id: 123,
- start: '2022-01-01',
- end: '2022-01-01',
- available: 200,
- spent: 100,
- enterpriseSlug: enterpriseId,
- },
- ],
- },
- });
- useOfferRedemptions.mockReturnValue({
- isLoading: false,
- offerRedemptions: {
- results: [mockOfferRedemption],
- itemCount: 1,
- pageCount: 1,
- },
- fetchOfferRedemptions: jest.fn(),
- });
-
- render( );
-
- expect(screen.getByTestId('view-budget')).toBeInTheDocument();
+ render( );
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+
+ // View budget CTA
+ const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
+ expect(viewBudgetCTA).toBeInTheDocument();
+ expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockBudgetUuid}`);
+
+ // Aggregates
+ expect(screen.getByText('Balance')).toBeInTheDocument();
+ expect(screen.getByText('Available')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.available))).toBeInTheDocument();
+ expect(screen.getByText('Spent')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
+ });
+
+ it.each([
+ { isAssignableBudget: false },
+ { isAssignableBudget: true },
+ ])('displays correctly for Policy (enterprise-access) (%s)', ({ isAssignableBudget }) => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ pending: 100,
+ available: isAssignableBudget ? 4700 : 4800,
+ };
+ const mockBudget = {
+ id: mockBudgetUuid,
+ name: mockBudgetDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ source: BUDGET_TYPES.policy,
+ aggregates: {
+ available: mockBudgetAggregates.available,
+ pending: isAssignableBudget ? mockBudgetAggregates.pending : undefined,
+ spent: mockBudgetAggregates.spent,
+ },
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: undefined,
});
+
+ render( );
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+
+ // View budget CTA
+ const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
+ expect(viewBudgetCTA).toBeInTheDocument();
+ expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockBudgetUuid}`);
+
+ // Aggregates
+ expect(screen.getByText('Balance')).toBeInTheDocument();
+ expect(screen.getByText('Available')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.available))).toBeInTheDocument();
+ if (isAssignableBudget) {
+ expect(screen.getByText('Pending')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.pending))).toBeInTheDocument();
+ } else {
+ expect(screen.queryByText('Pending')).not.toBeInTheDocument();
+ }
+ expect(screen.getByText('Spent')).toBeInTheDocument();
+ expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
});
});
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 170b33b0ad..5138d10080 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -16,7 +16,7 @@ import { faker } from '@faker-js/faker';
import BudgetDetailPage from '../BudgetDetailPage';
import {
useSubsidyAccessPolicy,
- useOfferRedemptions,
+ useBudgetRedemptions,
useBudgetContentAssignments,
useBudgetDetailActivityOverview,
useIsLargeOrGreater,
@@ -46,7 +46,7 @@ jest.mock('react-router-dom', () => ({
jest.mock('../data', () => ({
...jest.requireActual('../data'),
- useOfferRedemptions: jest.fn(),
+ useBudgetRedemptions: jest.fn(),
useBudgetContentAssignments: jest.fn(),
useSubsidyAccessPolicy: jest.fn(),
useBudgetDetailActivityOverview: jest.fn(),
@@ -77,7 +77,7 @@ const mockEmptyStateBudgetDetailActivityOverview = {
contentAssignments: { count: 0 },
spentTransactions: { count: 0 },
};
-const mockEmptyOfferRedemptions = {
+const mockEmptyBudgetRedemptions = {
itemCount: 0,
pageCount: 0,
results: [],
@@ -292,10 +292,10 @@ describe(' ', () => {
},
fetchContentAssignments: jest.fn(),
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
const storeState = {
...initialStoreState,
@@ -309,8 +309,8 @@ describe(' ', () => {
};
renderWithRouter( );
- expect(useOfferRedemptions).toHaveBeenCalledTimes(1);
- expect(useOfferRedemptions).toHaveBeenCalledWith(...expectedUseOfferRedemptionsArgs);
+ expect(useBudgetRedemptions).toHaveBeenCalledTimes(1);
+ expect(useBudgetRedemptions).toHaveBeenCalledWith(...expectedUseOfferRedemptionsArgs);
// Activity tab exists and is active
expect(screen.getByText('Activity').getAttribute('aria-selected')).toBe('true');
@@ -357,14 +357,14 @@ describe(' ', () => {
},
fetchContentAssignments: jest.fn(),
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: {
+ budgetRedemptions: {
itemCount: 2,
pageCount: 1,
results: [mockEnrollmentTransaction, mockEnrollmentTransactionWithReversal],
},
- fetchOfferRedemptions: jest.fn(),
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -427,10 +427,10 @@ describe(' ', () => {
},
fetchContentAssignments: mockFetchContentAssignments,
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -507,10 +507,10 @@ describe(' ', () => {
},
fetchContentAssignments: mockFetchContentAssignments,
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -588,10 +588,10 @@ describe(' ', () => {
},
fetchContentAssignments: mockFetchContentAssignments,
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -676,10 +676,10 @@ describe(' ', () => {
},
fetchContentAssignments: mockFetchContentAssignments,
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -741,10 +741,10 @@ describe(' ', () => {
},
fetchContentAssignments: jest.fn(),
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -864,10 +864,10 @@ describe(' ', () => {
},
fetchContentAssignments: jest.fn(),
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -927,10 +927,10 @@ describe(' ', () => {
spentTransactions: { count: 0 },
},
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -965,10 +965,10 @@ describe(' ', () => {
spentTransactions: { count: 0 },
},
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
renderWithRouter( );
@@ -1106,10 +1106,10 @@ describe(' ', () => {
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
});
- useOfferRedemptions.mockReturnValue({
+ useBudgetRedemptions.mockReturnValue({
isLoading: false,
- offerRedemptions: mockEmptyOfferRedemptions,
- fetchOfferRedemptions: jest.fn(),
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
});
useSubsidyAccessPolicy.mockReturnValue({
isInitialLoading: false,
diff --git a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
index 701e12dbb3..7b922bba84 100644
--- a/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
+++ b/src/components/learner-credit-management/tests/MultipleBudgetsPage.test.jsx
@@ -27,11 +27,11 @@ const store = getMockStore({ ...initialStore });
const enterpriseUUID = '1234';
const emptyOffersContextValue = {
- offers: [], // Empty offers array
+ budgets: [], // Empty offers array
};
const defaultEnterpriseSubsidiesContextValue = {
- offers: [{
+ budgets: [{
source: 'subsidy',
id: '392f1fe1-ee91-4f44-b174-13ecf59866eb',
name: 'Subsidy 2 for Executive Education (2U) Integration QA',
@@ -76,6 +76,6 @@ describe(' ', () => {
enterpriseSlug={enterpriseSlug}
enterpriseSubsidiesContextValue={enterpriseSubsidiesContextValue}
/>);
- expect(screen.getByText('Loading...')).toBeInTheDocument();
+ expect(screen.getByText('Loading budgets...')).toBeInTheDocument();
});
});
diff --git a/src/components/subsidy-requests/tests/SubsidyRequestsContext.test.jsx b/src/components/subsidy-requests/tests/SubsidyRequestsContext.test.jsx
index d9b66ca7f3..2d831ff7e5 100644
--- a/src/components/subsidy-requests/tests/SubsidyRequestsContext.test.jsx
+++ b/src/components/subsidy-requests/tests/SubsidyRequestsContext.test.jsx
@@ -71,7 +71,7 @@ describe('useSubsidyRequestsContext', () => {
() => useSubsidyRequestsContext({
enterpriseId: TEST_ENTERPRISE_UUID,
enterpriseSubsidyTypes: [
- SUBSIDY_TYPES.offer, SUBSIDY_TYPES.license,
+ SUBSIDY_TYPES.budget, SUBSIDY_TYPES.license,
],
}),
);
diff --git a/src/containers/EnterpriseApp/index.jsx b/src/containers/EnterpriseApp/index.jsx
index 787876e5d3..b090f6e975 100644
--- a/src/containers/EnterpriseApp/index.jsx
+++ b/src/containers/EnterpriseApp/index.jsx
@@ -22,6 +22,7 @@ const mapStateToProps = (state) => {
enablePortalLearnerCreditManagementScreen: state.portalConfiguration.enablePortalLearnerCreditManagementScreen,
enterpriseId: state.portalConfiguration.enterpriseId,
enterpriseName: state.portalConfiguration.enterpriseName,
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
enterpriseBranding: state.portalConfiguration.enterpriseBranding,
loading: state.portalConfiguration.loading,
};
diff --git a/src/data/constants/subsidyTypes.js b/src/data/constants/subsidyTypes.js
index e8d60b8d78..36267a913d 100644
--- a/src/data/constants/subsidyTypes.js
+++ b/src/data/constants/subsidyTypes.js
@@ -2,5 +2,5 @@
export const SUBSIDY_TYPES = {
coupon: 'coupon',
license: 'license',
- offer: 'offer',
+ budget: 'budget',
};
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index d0035b3e63..94e4fcae95 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -164,14 +164,30 @@ class EnterpriseAccessApiService {
return EnterpriseAccessApiService.apiClient().get(url);
}
+ static listSubsidyAccessPolicies(enterpriseCustomerId) {
+ const queryParams = new URLSearchParams({
+ enterprise_customer_uuid: enterpriseCustomerId,
+ });
+ const url = `${EnterpriseAccessApiService.baseUrl}/subsidy-access-policies/?${queryParams.toString()}`;
+ return EnterpriseAccessApiService.apiClient().get(url);
+ }
+
/**
* Retrieve a specific subsidy access policy.
+ * @param {string} subsidyAccessPolicyUUID The UUID of the subsidy access policy to retrieve.
+ * @returns {Promise} - A promise that resolves to the response from the API.
*/
static retrieveSubsidyAccessPolicy(subsidyAccessPolicyUUID) {
const url = `${EnterpriseAccessApiService.baseUrl}/subsidy-access-policies/${subsidyAccessPolicyUUID}/`;
return EnterpriseAccessApiService.apiClient().get(url);
}
+ /**
+ * ALlocates assignments for a specific subsidy access policy.
+ * @param {String} subsidyAccessPolicyUUID The UUID of the subsidy access policy to allocate content assignments for.
+ * @param {Object} payload The metadata to send to the API, including learner emails and the content key.
+ * @returns {Promise} - A promise that resolves to the response from the API.
+ */
static allocateContentAssignments(subsidyAccessPolicyUUID, payload) {
const url = `${EnterpriseAccessApiService.baseUrl}/policy-allocation/${subsidyAccessPolicyUUID}/allocate/`;
return EnterpriseAccessApiService.apiClient().post(url, payload);
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index a6a5aeec7f..7317e14639 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -162,6 +162,13 @@ describe('EnterpriseAccessApiService', () => {
);
});
+ test('listSubsidyAccessPolicies calls enterprise-access to fetch subsidy access policies', () => {
+ EnterpriseAccessApiService.listSubsidyAccessPolicies(mockEnterpriseUUID);
+ expect(axios.get).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/subsidy-access-policies/?enterprise_customer_uuid=${mockEnterpriseUUID}`,
+ );
+ });
+
test('retrieveSubsidyAccessPolicy calls enterprise-access to fetch subsidy access policy', () => {
EnterpriseAccessApiService.retrieveSubsidyAccessPolicy(mockSubsidyAccessPolicyUUID);
expect(axios.get).toBeCalledWith(
From 0be2be7772c307a85203b218c8d367f170e54ca6 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 1 Dec 2023 13:35:21 -0500
Subject: [PATCH 088/124] fix: resolve bug with calling analytics api for
budget cards (#1111)
---
.../useSubsidySummaryAnalyticsApi.test.js | 108 ++++++++++++------
.../hooks/useSubsidySummaryAnalyticsApi.js | 4 +-
2 files changed, 74 insertions(+), 38 deletions(-)
diff --git a/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js b/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
index 320f51d685..3fa26919c9 100644
--- a/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
+++ b/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
@@ -3,6 +3,7 @@ import { logError } from '@edx/frontend-platform/logging';
import useSubsidySummaryAnalyticsApi from '../useSubsidySummaryAnalyticsApi';
import EnterpriseDataApiService from '../../../../../data/services/EnterpriseDataApiService';
+import { BUDGET_TYPES } from '../../../../EnterpriseApp/data/constants';
jest.mock('@edx/frontend-platform/config', () => ({
...jest.requireActual('@edx/frontend-platform/config'),
@@ -18,6 +19,7 @@ jest.mock('../../../../../data/services/EnterpriseDataApiService');
const TEST_ENTERPRISE_UUID = 'test-enterprise-uuid';
const TEST_ENTERPRISE_OFFER_ID = 1;
+const TEST_ENTERPRISE_BUDGET_UUID = 'test-enterprise-budget-uuid';
const mockOfferSummary = {
offer_id: TEST_ENTERPRISE_OFFER_ID,
@@ -28,11 +30,14 @@ const mockOfferSummary = {
percent_of_offer_spent: 0.04,
remaining_balance: 4800.00,
};
-const mockEnterpriseOffer = {
- id: TEST_ENTERPRISE_OFFER_ID,
-};
describe('useSubsidySummaryAnalyticsApi', () => {
+ const mockFetchEnterpriseOfferSummarySpy = jest.spyOn(EnterpriseDataApiService, 'fetchEnterpriseOfferSummary');
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
it('should handle null enterprise offer', async () => {
const { result } = renderHook(() => useSubsidySummaryAnalyticsApi(TEST_ENTERPRISE_UUID));
@@ -43,51 +48,82 @@ describe('useSubsidySummaryAnalyticsApi', () => {
});
it.each([
- { shouldThrowApiException: false },
- { shouldThrowApiException: true },
- ])('should fetch summary data for enterprise offer (%s)', async ({ shouldThrowApiException }) => {
+ {
+ budgetId: TEST_ENTERPRISE_OFFER_ID,
+ budgetType: BUDGET_TYPES.ecommerce,
+ shouldCallApi: true,
+ shouldThrowApiException: false,
+ },
+ {
+ budgetId: TEST_ENTERPRISE_BUDGET_UUID,
+ budgetType: BUDGET_TYPES.subsidy,
+ shouldCallApi: true,
+ shouldThrowApiException: true,
+ },
+ {
+ budgetId: TEST_ENTERPRISE_BUDGET_UUID,
+ budgetType: BUDGET_TYPES.policy,
+ shouldCallApi: false,
+ shouldThrowApiException: false,
+ },
+ ])('should fetch summary data for enterprise offer (%s)', async ({
+ budgetId,
+ budgetType,
+ shouldThrowApiException,
+ shouldCallApi,
+ }) => {
const mockFetchError = 'mock fetch error';
if (shouldThrowApiException) {
- EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockRejectedValueOnce(mockFetchError);
+ mockFetchEnterpriseOfferSummarySpy.mockRejectedValue(mockFetchError);
} else {
- EnterpriseDataApiService.fetchEnterpriseOfferSummary.mockResolvedValueOnce({ data: mockOfferSummary });
+ mockFetchEnterpriseOfferSummarySpy.mockResolvedValue({ data: mockOfferSummary });
}
+ const mockBudget = {
+ id: budgetId,
+ source: budgetType,
+ };
+
const {
result,
waitForNextUpdate,
- } = renderHook(() => useSubsidySummaryAnalyticsApi(TEST_ENTERPRISE_UUID, mockEnterpriseOffer));
-
- expect(result.current).toEqual({
- subsidySummary: undefined,
- isLoading: true,
- });
-
- await waitForNextUpdate();
+ } = renderHook(() => useSubsidySummaryAnalyticsApi(
+ TEST_ENTERPRISE_UUID,
+ mockBudget,
+ ));
- expect(EnterpriseDataApiService.fetchEnterpriseOfferSummary).toHaveBeenCalled();
-
- if (shouldThrowApiException) {
- expect(logError).toHaveBeenCalledWith(mockFetchError);
+ if (shouldCallApi) {
expect(result.current).toEqual({
subsidySummary: undefined,
- isLoading: false,
+ isLoading: true,
});
+ await waitForNextUpdate();
+ expect(mockFetchEnterpriseOfferSummarySpy).toHaveBeenCalled();
+
+ if (shouldThrowApiException) {
+ expect(logError).toHaveBeenCalledWith(mockFetchError);
+ expect(result.current).toEqual({
+ subsidySummary: undefined,
+ isLoading: false,
+ });
+ } else {
+ const expectedResult = {
+ totalFunds: 5000,
+ redeemedFunds: 200,
+ redeemedFundsExecEd: NaN,
+ redeemedFundsOcm: NaN,
+ remainingFunds: 4800,
+ percentUtilized: 0.04,
+ offerId: 1,
+ budgetsSummary: [],
+ offerType: undefined,
+ };
+ expect(result.current).toEqual({
+ subsidySummary: expectedResult,
+ isLoading: false,
+ });
+ }
} else {
- const expectedResult = {
- totalFunds: 5000,
- redeemedFunds: 200,
- redeemedFundsExecEd: NaN,
- redeemedFundsOcm: NaN,
- remainingFunds: 4800,
- percentUtilized: 0.04,
- offerId: 1,
- budgetsSummary: [],
- offerType: undefined,
- };
- expect(result.current).toEqual({
- subsidySummary: expectedResult,
- isLoading: false,
- });
+ expect(EnterpriseDataApiService.fetchEnterpriseOfferSummary).not.toHaveBeenCalled();
}
});
});
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js b/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
index 1dde1b995c..f0aaee7826 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
+++ b/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
@@ -11,9 +11,9 @@ const useSubsidySummaryAnalyticsApi = (enterpriseUUID, budget) => {
const [subsidySummary, setSubsidySummary] = useState();
useEffect(() => {
- // If there is no budget, or the budget is an ecommerce offer or subsidy, fetch the
+ // If there is no budget, or the budget is NOT an ecommerce offer or subsidy, fetch the
// subsidy summary data from the analytics API.
- if (!budget || [BUDGET_TYPES.ecommerce, BUDGET_TYPES.subsidy].includes(budget.source)) {
+ if (![BUDGET_TYPES.ecommerce, BUDGET_TYPES.subsidy].includes(budget?.source)) {
setIsLoading(false);
return;
}
From e0f020bcc623eca59e3ff84e98be96c59ff6e2dc Mon Sep 17 00:00:00 2001
From: edX requirements bot
<49161187+edx-requirements-bot@users.noreply.github.com>
Date: Mon, 4 Dec 2023 15:39:18 -0500
Subject: [PATCH 089/124] chore: update browserslist DB (#1113)
Co-authored-by: abdullahwaheed
---
package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index fe96608470..eba758d745 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8448,9 +8448,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001564",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001564.tgz",
- "integrity": "sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==",
+ "version": "1.0.30001566",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
+ "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
"funding": [
{
"type": "opencollective",
From 39a006d0209838258c8aea4f3e8411e2582cd94c Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Sat, 2 Dec 2023 07:42:16 +0000
Subject: [PATCH 090/124] feat: adding idp metadata network form error handling
---
src/components/forms/FormContext.tsx | 1 +
src/components/forms/FormContextWrapper.tsx | 1 +
src/components/forms/FormWorkflow.tsx | 10 ++-
src/components/forms/data/reducer.test.ts | 4 +
src/components/forms/data/reducer.ts | 4 +-
.../SettingsLMSTab/LMSFormWorkflowConfig.tsx | 3 +
.../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 79 +++++++++++++------
.../steps/NewSSOConfigConfigureStep.tsx | 63 ++++++++++++++-
src/components/settings/data/constants.js | 3 +
9 files changed, 142 insertions(+), 26 deletions(-)
diff --git a/src/components/forms/FormContext.tsx b/src/components/forms/FormContext.tsx
index 9ff41f7cb1..95946a3f00 100644
--- a/src/components/forms/FormContext.tsx
+++ b/src/components/forms/FormContext.tsx
@@ -22,6 +22,7 @@ export type FormContext = {
errorMap?: { [name: string]: string[] };
stateMap?: { [name: string]: any };
currentStep?: FormWorkflowStep;
+ allSteps?: FormWorkflowStep[];
};
export const FormContextObject: Context = createContext({});
diff --git a/src/components/forms/FormContextWrapper.tsx b/src/components/forms/FormContextWrapper.tsx
index fa3c8a2b75..199817ebca 100644
--- a/src/components/forms/FormContextWrapper.tsx
+++ b/src/components/forms/FormContextWrapper.tsx
@@ -22,6 +22,7 @@ const FormContextWrapper = ({
const initializeAction: InitializeFormArguments = {
formFields: formData as FormConfigData,
currentStep: formWorkflowConfig.getCurrentStep(),
+ steps: formWorkflowConfig.steps,
};
const [formFieldsState, dispatch] = useReducer<
FormReducerType,
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index d17d131548..a60b017e64 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -42,6 +42,7 @@ export type FormWorkflowAwaitHandler = {
export type FormWorkflowButtonConfig = {
buttonText: string;
opensNewWindow: boolean;
+ preventDefaultErrorModal: boolean;
onClick?: (args: FormWorkflowHandlerArgs) => Promise | void;
awaitSuccess?: FormWorkflowAwaitHandler;
};
@@ -136,7 +137,12 @@ const FormWorkflow = ({
setNextInProgress(true);
const newFormFields: FormConfigData = await nextButtonConfig.onClick({
formFields,
- errHandler: setFormError,
+ errHandler: (error) => {
+ setFormError(error);
+ if (!!error) {
+ advance = false;
+ }
+ },
dispatch,
formFieldsChanged: !!isEdited,
});
@@ -228,7 +234,7 @@ const FormWorkflow = ({
return (
<>
diff --git a/src/components/forms/data/reducer.test.ts b/src/components/forms/data/reducer.test.ts
index 988102d634..5bb5f43ee6 100644
--- a/src/components/forms/data/reducer.test.ts
+++ b/src/components/forms/data/reducer.test.ts
@@ -19,6 +19,7 @@ const dummyButtonConfig: FormWorkflowButtonConfig = {
buttonText: 'Unimportant',
onClick: ({ formFields }: FormWorkflowHandlerArgs) => Promise.resolve(formFields as DummyFormFields),
opensNewWindow: false,
+ preventDefaultErrorModal: false,
};
const createDummyStep = (
@@ -65,6 +66,7 @@ const getTestInitializeFormArguments = () => {
formFields: { ...testFormFields },
validations: dummyFormFieldsValidations,
currentStep: steps[0],
+ steps: [...steps],
};
return testArgs;
};
@@ -77,6 +79,7 @@ describe('Form reducer tests', () => {
formFields: { ...formFields },
validations: dummyFormFieldsValidations,
currentStep: steps[0],
+ steps: [...steps],
};
expect(initializeForm(initializeFormArguments)).toEqual({
formFields,
@@ -84,6 +87,7 @@ describe('Form reducer tests', () => {
hasErrors: false,
currentStep: steps[0],
isEdited: false,
+ allSteps: [...steps],
});
});
diff --git a/src/components/forms/data/reducer.ts b/src/components/forms/data/reducer.ts
index afee5e8c8c..b105f53e35 100644
--- a/src/components/forms/data/reducer.ts
+++ b/src/components/forms/data/reducer.ts
@@ -53,6 +53,7 @@ export type InitializeFormArguments = {
formFields: FormFields;
validations?: FormFieldValidation[];
currentStep: FormWorkflowStep;
+ steps: FormWorkflowStep[];
};
export function initializeFormImpl(
@@ -78,7 +79,7 @@ export function initializeFormImpl(
export function initializeForm(action: InitializeFormArguments) {
const initialFormState: Pick<
FormContext,
- 'isEdited' | 'formFields' | 'currentStep'
+ 'isEdited' | 'formFields' | 'currentStep' | 'allSteps'
> = { isEdited: false };
if (action?.formFields) {
initialFormState.formFields = action.formFields;
@@ -86,6 +87,7 @@ export function initializeForm(action: InitializeFormArguments {},
+ preventDefaultErrorModal: false,
}),
},
];
@@ -92,6 +93,7 @@ export const LMSFormWorkflowConfig = ({
nextButtonConfig: () => ({
buttonText: 'Next',
opensNewWindow: false,
+ preventDefaultErrorModal: false,
}),
},
{
@@ -102,6 +104,7 @@ export const LMSFormWorkflowConfig = ({
nextButtonConfig: () => ({
buttonText: 'Next',
opensNewWindow: false,
+ preventDefaultErrorModal: false,
}),
},
);
diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
index 2d3cd7c4a1..ca7dd08e07 100644
--- a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
+++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
@@ -8,6 +8,8 @@ import SSOConfigConfirmStep from './steps/NewSSOConfigConfirmStep';
import LmsApiService from '../../../data/services/LmsApiService';
import handleErrors from '../utils';
import { snakeCaseDict } from '../../../utils';
+import { AxiosError } from 'axios';
+import { INVALID_IDP_METADATA_ERROR, RECORD_UNDER_CONFIGURATIONS_ERROR } from '../data/constants';
type SSOConfigSnakeCase = {
uuid?: string,
@@ -25,9 +27,9 @@ type SSOConfigSnakeCase = {
email_attribute: string,
username_attribute: string,
country_attribute: string,
- submitted_at: null,
- configured_at: null,
- validated_at: null,
+ submitted_at?: null,
+ configured_at?: null,
+ validated_at?: null,
odata_api_timeout_interval: null,
odata_api_root_url: string,
odata_company_id: string,
@@ -36,10 +38,11 @@ type SSOConfigSnakeCase = {
sapsf_private_key: string,
odata_client_id: string,
oauth_user_id: string,
- sp_metadata_url?: string
+ sp_metadata_url?: string,
+ record?: object,
};
-type SSOConfigCamelCase = {
+export type SSOConfigCamelCase = {
uuid?: string,
enterpriseCustomer: string,
isRemoved: boolean,
@@ -81,46 +84,72 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
const placeHolderButton = (buttonName?: string) => () => ({
buttonText: buttonName || 'Next',
opensNewWindow: false,
- onClick: () => {},
+ onClick: () => { },
+ preventDefaultErrorModal: false,
});
+ const advanceConnectStep = async ({
+ formFields,
+ errHandler,
+ }: FormWorkflowHandlerArgs) => {
+ errHandler?.('');
+ return { ...formFields };
+ };
+
+ const sanitizeAndCopyFormFields = (formFields: SSOConfigSnakeCase) => {
+ const copiedFormFields = { ...formFields };
+ return omit(copiedFormFields, ['record', 'sp_metadata_url', 'submitted_at', 'configured_at','validated_at']);
+ };
+
const saveChanges = async ({
formFields,
errHandler,
+ // @ts-ignore:next-line formFieldsChanged is only used in the below TODO
formFieldsChanged,
- }:FormWorkflowHandlerArgs) => {
+ }: FormWorkflowHandlerArgs) => {
let err = null;
- if (!formFieldsChanged) {
- // Don't submit if nothing has changed
- return formFields;
- }
+
+ // TODO : Accurately detect if form fields have changed
+ // if (!formFieldsChanged && !idpMetadataError) {
+ // // Don't submit if nothing has changed
+ // return formFields;
+ // }
let updatedFormFields: SSOConfigCamelCase = omit(formFields, ['idpConnectOption', 'spMetadataUrl', 'isPendingConfiguration']);
updatedFormFields.enterpriseCustomer = enterpriseId;
const submittedFormFields: SSOConfigSnakeCase = snakeCaseDict(updatedFormFields) as SSOConfigSnakeCase;
- if (submittedFormFields?.uuid) {
+ let copiedFormFields = sanitizeAndCopyFormFields(submittedFormFields);
+ if (copiedFormFields?.uuid) {
try {
const updateResponse = await LmsApiService.updateEnterpriseSsoOrchestrationRecord(
- submittedFormFields,
+ copiedFormFields,
formFields?.uuid,
);
updatedFormFields = updateResponse.data;
- } catch (error) {
+ } catch (error: AxiosError | any) {
err = handleErrors(error);
- setConfigureError(error);
+ if (error.message?.includes("Must provide valid IDP metadata url")) {
+ errHandler?.(INVALID_IDP_METADATA_ERROR);
+ } else if (error.message?.includes("Record has already been submitted for configuration.")) {
+ errHandler?.(RECORD_UNDER_CONFIGURATIONS_ERROR);
+ } else {
+ setConfigureError(error);
+ }
}
} else {
try {
- const createResponse = await LmsApiService.createEnterpriseSsoOrchestrationRecord(submittedFormFields);
+ const createResponse = await LmsApiService.createEnterpriseSsoOrchestrationRecord(copiedFormFields);
updatedFormFields.uuid = createResponse.data.record;
updatedFormFields.spMetadataUrl = createResponse.data.sp_metadata_url;
- } catch (error) {
+ } catch (error: AxiosError | any) {
err = handleErrors(error);
- setConfigureError(error);
+ if (error.message?.includes("Must provide valid IDP metadata url")) {
+ errHandler?.(INVALID_IDP_METADATA_ERROR);
+ } else {
+ setConfigureError(error);
+ }
}
}
- if (err && errHandler) {
- errHandler(err);
- }
+
const newFormFields = { ...formFields, ...updatedFormFields } as SSOConfigCamelCase;
return newFormFields;
};
@@ -131,7 +160,12 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
formComponent: SSOConfigConnectStep,
validations: SSOConfigConnectStepValidations,
stepName: 'Connect',
- nextButtonConfig: placeHolderButton(),
+ nextButtonConfig: () => ({
+ buttonText: 'Next',
+ opensNewWindow: false,
+ onClick: advanceConnectStep,
+ preventDefaultErrorModal: true,
+ }),
}, {
index: 1,
formComponent: SSOConfigConfigureStep,
@@ -141,6 +175,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
buttonText: 'Configure',
opensNewWindow: false,
onClick: saveChanges,
+ preventDefaultErrorModal: true,
}),
showBackButton: true,
showCancelButton: false,
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
index 30e347d1ef..ff238dfee2 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
@@ -1,11 +1,16 @@
import React from 'react';
import {
- Form, Container,
+ Alert, Button, Form, Container,
} from '@edx/paragon';
+import { Info } from '@edx/paragon/icons';
import ValidatedFormControl from '../../../forms/ValidatedFormControl';
import { FormContext, FormFieldValidation, useFormContext } from '../../../forms/FormContext';
import { urlValidation } from '../../../../utils';
+import { FormWorkflowStep } from '../../../forms/FormWorkflow';
+import { FORM_ERROR_MESSAGE, setStepAction } from '../../../forms/data/actions';
+import { INVALID_IDP_METADATA_ERROR, RECORD_UNDER_CONFIGURATIONS_ERROR } from '../../data/constants';
+import { SSOConfigCamelCase } from '../SSOFormWorkflowConfig';
const isSAPConfig = (fields) => fields.identityProvider === 'sap_success_factors';
@@ -35,6 +40,9 @@ export const validations: FormFieldValidation[] = [
const SSOConfigConfigureStep = () => {
const {
formFields,
+ dispatch,
+ allSteps,
+ stateMap,
}: FormContext = useFormContext();
const usingSAP = formFields?.identityProvider === 'sap_success_factors';
@@ -147,12 +155,65 @@ const SSOConfigConfigureStep = () => {
>
);
+ const returnToConnectStep = () => {
+ const connectStep = allSteps?.[0] as FormWorkflowStep;
+ dispatch?.(
+ setStepAction({ step: connectStep })
+ );
+ };
+
return (
);
};
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
index dbfc2c375a..63dffc765c 100644
--- a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -1,12 +1,12 @@
-import React from 'react';
-import PropTypes from 'prop-types';
import {
Chip,
} from '@edx/paragon';
-import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
-import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
+import PropTypes from 'prop-types';
import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
+import FailedCancellation from './assignments-status-chips/FailedCancellation';
import FailedSystem from './assignments-status-chips/FailedSystem';
+import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
+import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
const AssignmentStatusTableCell = ({ row }) => {
const { original } = row;
@@ -36,13 +36,18 @@ const AssignmentStatusTableCell = ({ row }) => {
if (learnerState === 'failed') {
// Determine which failure chip to display based on the error reason.
- if (errorReason === 'email_error') {
- return (
-
- );
+ if (errorReason.actionType === 'notified') {
+ if (errorReason.errorReason === 'email_error') {
+ return (
+
+ );
+ }
+ return ;
}
- return ;
+ if (errorReason.actionType === 'cancelled') {
+ return ;
+ }
}
// Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
@@ -54,7 +59,10 @@ AssignmentStatusTableCell.propTypes = {
original: PropTypes.shape({
learnerEmail: PropTypes.string,
learnerState: PropTypes.string.isRequired,
- errorReason: PropTypes.string,
+ errorReason: PropTypes.shape({
+ actionType: PropTypes.string,
+ errorReason: PropTypes.string,
+ }),
actions: PropTypes.arrayOf(PropTypes.shape({
actionType: PropTypes.string.isRequired,
errorReason: PropTypes.string,
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
index 37c3e8ef6d..cb45c1f4eb 100644
--- a/src/components/learner-credit-management/AssignmentTableCancel.jsx
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -2,13 +2,35 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { DoNotDisturbOn } from '@edx/paragon/icons';
+import CancelAssignmentModal from './CancelAssignmentModal';
+import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
-const AssignmentTableCancelAction = ({ selectedFlatRows, ...rest }) => (
- // eslint-disable-next-line no-console
- console.log('Cancel', selectedFlatRows, rest)}>
- {`Cancel (${selectedFlatRows.length})`}
-
-);
+const AssignmentTableCancelAction = ({ selectedFlatRows }) => {
+ const assignmentUuids = selectedFlatRows.map(row => row.id);
+ const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
+ const {
+ assignButtonState,
+ cancelContentAssignments,
+ close,
+ isOpen,
+ open,
+ } = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+
+ return (
+ <>
+
+ {`Cancel (${assignmentUuids.length})`}
+
+
+ >
+ );
+};
AssignmentTableCancelAction.propTypes = {
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
diff --git a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
index b7ae27b754..0702441137 100644
--- a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
@@ -1,10 +1,10 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { Container, Toast } from '@edx/paragon';
import Hero from '../Hero';
-import { useSuccessfulAssignmentToastContextValue } from './data';
+import { useSuccessfulAssignmentToastContextValue, useSuccessfulCancellationToastContextValue } from './data';
const PAGE_TITLE = 'Learner Credit Management';
@@ -20,22 +20,37 @@ const BudgetDetailPageWrapper = ({
const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
const helmetPageTitle = budgetDisplayName ? `${budgetDisplayName} - ${PAGE_TITLE}` : PAGE_TITLE;
- const successfulAssignmentToastContextValue = useSuccessfulAssignmentToastContextValue();
+ const successfulAssignmentToast = useSuccessfulAssignmentToastContextValue();
+ const successfulCancellationToast = useSuccessfulCancellationToastContextValue();
+
const {
isSuccessfulAssignmentAllocationToastOpen,
successfulAssignmentAllocationToastMessage,
closeToastForAssignmentAllocation,
- } = successfulAssignmentToastContextValue;
+ } = successfulAssignmentToast;
+
+ const {
+ isSuccessfulAssignmentCancellationToastOpen,
+ successfulAssignmentCancellationToastMessage,
+ closeToastForAssignmentCancellation,
+ } = successfulCancellationToast;
+
+ const values = useMemo(() => ({
+ successfulAssignmentToast,
+ successfulCancellationToast,
+ }), [successfulAssignmentToast, successfulCancellationToast]);
return (
-
+
{includeHero && }
{children}
{/**
- Successful assignment allocation Toast notification. It is rendered here to guarantee that the
+ Successful assignment allocation and cancellation Toast notifications. It is rendered here to guarantee that the
Toast component will not be unmounted when the user programmatically navigates to the "Activity"
tab, which will unmount the course cards that rendered the assignment modal. Thus, the Toast must
be rendered within the component tree that's common to both the "Activity" and "Overview" tabs.
@@ -46,6 +61,13 @@ const BudgetDetailPageWrapper = ({
>
{successfulAssignmentAllocationToastMessage}
+
+
+ {successfulAssignmentCancellationToastMessage}
+
);
};
diff --git a/src/components/learner-credit-management/CancelAssignmentModal.jsx b/src/components/learner-credit-management/CancelAssignmentModal.jsx
new file mode 100644
index 0000000000..2636fdb13d
--- /dev/null
+++ b/src/components/learner-credit-management/CancelAssignmentModal.jsx
@@ -0,0 +1,74 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import {
+ ActionRow, ModalDialog, StatefulButton,
+} from '@edx/paragon';
+import { DoNotDisturbOn } from '@edx/paragon/icons';
+import { BudgetDetailPageContext } from './BudgetDetailPageWrapper';
+
+const CancelAssignmentModal = ({
+ assignButtonState,
+ cancelContentAssignments,
+ close,
+ isOpen,
+ uuidCount,
+}) => {
+ const {
+ successfulCancellationToast: { displayToastForAssignmentCancellation },
+ } = useContext(BudgetDetailPageContext);
+
+ const handleOnClick = async () => {
+ await cancelContentAssignments();
+ displayToastForAssignmentCancellation(uuidCount);
+ };
+
+ return (
+
+
+
+ Cancel assignment?
+
+
+
+
+ This action cannot be undone.
+ The learner will be notified that you have canceled the assignment. The funds associated with
+ this course assignment will move from "assigned" back to "available".
+
+
+
+
+
+ Go back
+ 1 ? `Cancel assignments (${uuidCount})` : 'Cancel assignment',
+ pending: 'Canceling...',
+ complete: 'Canceled',
+ error: 'Try again',
+ }}
+ variant="danger"
+ state={assignButtonState}
+ onClick={handleOnClick}
+ />
+
+
+
+ );
+};
+
+CancelAssignmentModal.propTypes = {
+ assignButtonState: PropTypes.string.isRequired,
+ cancelContentAssignments: PropTypes.func.isRequired,
+ close: PropTypes.func.isRequired,
+ isOpen: PropTypes.bool.isRequired,
+ uuidCount: PropTypes.number,
+};
+
+export default CancelAssignmentModal;
diff --git a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
new file mode 100644
index 0000000000..692cdd4cfb
--- /dev/null
+++ b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Icon, IconButtonWithTooltip,
+} from '@edx/paragon';
+import { DoNotDisturbOn } from '@edx/paragon/icons';
+import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
+import CancelAssignmentModal from './CancelAssignmentModal';
+
+const PendingAssignmentCancelButton = ({ row }) => {
+ const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
+ const {
+ assignButtonState,
+ cancelContentAssignments,
+ close,
+ isOpen,
+ open,
+ } = useCancelContentAssignments(row.original.assignmentConfiguration, [row.original.uuid]);
+ return (
+ <>
+
+
+ >
+ );
+};
+
+PendingAssignmentCancelButton.propTypes = {
+ row: PropTypes.shape({
+ original: PropTypes.shape({
+ assignmentConfiguration: PropTypes.string.isRequired,
+ learnerEmail: PropTypes.string,
+ uuid: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+};
+
+export default PendingAssignmentCancelButton;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
new file mode 100644
index 0000000000..182e91df7b
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
@@ -0,0 +1,56 @@
+import React, { useState } from 'react';
+import { Chip, useToggle, Hyperlink } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+import BaseModalPopup from './BaseModalPopup';
+
+const FailedCancellation = () => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Failed: Cancellation
+
+
+
+ Failed: Cancellation
+
+
+ This assignment was not canceled. Something went wrong behind the scenes.
+
+
Suggested resolution steps
+
+
+ Wait and try to cancel this assignment again later
+
+
+ If the issue continues, contact customer support.
+
+
+ Get more troubleshooting help at{' '}
+
+ Help Center: Course Assignments
+
+
+
+
+
+
+ >
+ );
+};
+
+export default FailedCancellation;
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 1c819cffeb..e01816489e 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -41,7 +41,9 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
const [canAllocateAssignments, setCanAllocateAssignments] = useState(false);
const [assignButtonState, setAssignButtonState] = useState('default');
const [createAssignmentsErrorReason, setCreateAssignmentsErrorReason] = useState();
- const { displayToastForAssignmentAllocation } = useContext(BudgetDetailPageContext);
+ const {
+ successfulAssignmentToast: { displayToastForAssignmentAllocation },
+ } = useContext(BudgetDetailPageContext);
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid,
diff --git a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
index 73f6d19784..7840ecad59 100644
--- a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
@@ -107,10 +107,12 @@ const mockLearnerEmails = ['hello@example.com', 'world@example.com', 'dinesh@exa
const mockDisplaySuccessfulAssignmentToast = jest.fn();
const defaultBudgetDetailPageContextValue = {
- isSuccessfulAssignmentAllocationToastOpen: false,
- totalLearnersAssigned: undefined,
- displayToastForAssignmentAllocation: mockDisplaySuccessfulAssignmentToast,
- closeToastForAssignmentAllocation: jest.fn(),
+ successfulAssignmentToast: {
+ isSuccessfulAssignmentAllocationToastOpen: false,
+ totalLearnersAssigned: undefined,
+ displayToastForAssignmentAllocation: mockDisplaySuccessfulAssignmentToast,
+ closeToastForAssignmentAllocation: jest.fn(),
+ },
};
const CourseCardWrapper = ({
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index 61731bd38e..400f754b57 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -8,3 +8,4 @@ export { default as usePathToCatalogTab } from './usePathToCatalogTab';
export { default as useBudgetDetailActivityOverview } from './useBudgetDetailActivityOverview';
export { default as useIsLargeOrGreater } from './useIsLargeOrGreater';
export { default as useSuccessfulAssignmentToastContextValue } from './useSuccessfulAssignmentToastContextValue';
+export { default as useSuccessfulCancellationToastContextValue } from './useSuccessfulCancellationToastContextValue';
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
new file mode 100644
index 0000000000..8b9bec8d6f
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
@@ -0,0 +1,42 @@
+import { useCallback, useState } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
+import { logError } from '@edx/frontend-platform/logging';
+import { useToggle } from '@edx/paragon';
+
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import { learnerCreditManagementQueryKeys } from '../constants';
+import useBudgetId from './useBudgetId';
+
+const useCancelContentAssignments = (
+ assignmentConfigurationUuid,
+ assignmentUuids,
+) => {
+ const [isOpen, open, close] = useToggle(false);
+ const [assignButtonState, setAssignButtonState] = useState('default');
+ const queryClient = useQueryClient();
+ const { subsidyAccessPolicyId } = useBudgetId();
+
+ const cancelContentAssignments = useCallback(async () => {
+ setAssignButtonState('pending');
+ try {
+ await EnterpriseAccessApiService.cancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ setAssignButtonState('complete');
+ queryClient.invalidateQueries({
+ queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
+ });
+ } catch (err) {
+ logError(err);
+ setAssignButtonState('error');
+ }
+ }, [assignmentConfigurationUuid, assignmentUuids, queryClient, subsidyAccessPolicyId]);
+
+ return {
+ assignButtonState,
+ cancelContentAssignments,
+ close,
+ isOpen,
+ open,
+ };
+};
+
+export default useCancelContentAssignments;
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
new file mode 100644
index 0000000000..4044003fef
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
@@ -0,0 +1,141 @@
+import { useParams } from 'react-router-dom';
+import { renderHook } from '@testing-library/react-hooks/dom';
+import { act, waitFor } from '@testing-library/react';
+import { QueryClientProvider } from '@tanstack/react-query';
+
+import { logError } from '@edx/frontend-platform/logging';
+
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import useCancelContentAssignments from './useCancelContentAssignments';
+import { queryClient } from '../../../test/testUtils';
+
+const TEST_ASSIGNMENT_CONFIGURATION_UUID = 'test-assignment-configuration-uuid';
+const TEST_PENDING_ASSIGNMENT_UUID_1 = 'test-pending-assignment-uuid_1';
+const TEST_PENDING_ASSIGNMENT_UUID_2 = 'test-pending-assignment-uuid_2';
+
+const wrapper = ({ children }) => (
+ {children}
+);
+
+jest.mock('../../../../data/services/EnterpriseAccessApiService');
+jest.mock('@edx/frontend-platform/logging', () => ({
+ logError: jest.fn(),
+}));
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn(),
+}));
+
+describe('useCancelContentAssignments', () => {
+ beforeEach(() => {
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should send a post request to cancel a single pending assignment', async () => {
+ EnterpriseAccessApiService.cancelContentAssignments.mockResolvedValueOnce({ status: 200 });
+ const { result } = renderHook(
+ () => useCancelContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ TEST_PENDING_ASSIGNMENT_UUID_1,
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ assignButtonState: 'default',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => act(() => result.current.cancelContentAssignments()));
+ expect(
+ EnterpriseAccessApiService.cancelContentAssignments,
+ ).toHaveBeenCalled();
+ expect(logError).toBeCalledTimes(0);
+
+ expect(result.current).toEqual({
+ assignButtonState: 'complete',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+
+ it('should send a post request to cancel multiple pending assignments', async () => {
+ EnterpriseAccessApiService.cancelContentAssignments.mockResolvedValueOnce({ status: 200 });
+ const { result } = renderHook(
+ () => useCancelContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ [TEST_PENDING_ASSIGNMENT_UUID_1, TEST_PENDING_ASSIGNMENT_UUID_2],
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ assignButtonState: 'default',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => act(() => result.current.cancelContentAssignments()));
+ expect(
+ EnterpriseAccessApiService.cancelContentAssignments,
+ ).toHaveBeenCalled();
+ expect(logError).toBeCalledTimes(0);
+
+ expect(result.current).toEqual({
+ assignButtonState: 'complete',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+
+ it('should handle assignment cancellation error', async () => {
+ const error = new Error('An error occurred');
+ EnterpriseAccessApiService.cancelContentAssignments.mockRejectedValueOnce(error);
+ const { result } = renderHook(
+ () => useCancelContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ [TEST_PENDING_ASSIGNMENT_UUID_1, TEST_PENDING_ASSIGNMENT_UUID_2],
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ assignButtonState: 'default',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => act(() => result.current.cancelContentAssignments()));
+
+ expect(
+ EnterpriseAccessApiService.cancelContentAssignments,
+ ).toHaveBeenCalled();
+ expect(logError).toBeCalledTimes(1);
+
+ expect(result.current).toEqual({
+ assignButtonState: 'error',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js b/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js
index b86113f604..1bcee3dbf7 100644
--- a/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js
+++ b/src/components/learner-credit-management/data/hooks/useSuccessfulAssignmentToastContextValue.js
@@ -26,7 +26,7 @@ const useSuccessfulAssignmentToastContextValue = () => {
}
const successfulAssignmentAllocationToastMessage = toastMessages.join(' ');
- const successfulAssignmentToastContextValue = useMemo(() => ({
+ const successfulAssignmentToast = useMemo(() => ({
isSuccessfulAssignmentAllocationToastOpen: isToastOpen,
displayToastForAssignmentAllocation: handleDisplayToast,
closeToastForAssignmentAllocation: handleCloseToast,
@@ -42,7 +42,7 @@ const useSuccessfulAssignmentToastContextValue = () => {
successfulAssignmentAllocationToastMessage,
]);
- return successfulAssignmentToastContextValue;
+ return successfulAssignmentToast;
};
export default useSuccessfulAssignmentToastContextValue;
diff --git a/src/components/learner-credit-management/data/hooks/useSuccessfulCancellationToastContextValue.js b/src/components/learner-credit-management/data/hooks/useSuccessfulCancellationToastContextValue.js
new file mode 100644
index 0000000000..a2bdf89bba
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useSuccessfulCancellationToastContextValue.js
@@ -0,0 +1,41 @@
+import { useCallback, useMemo, useState } from 'react';
+
+const generateSuccessCancelMessage = (assignmentUuidCount) => {
+ if (assignmentUuidCount > 1) {
+ return `Assignments canceled (${assignmentUuidCount})`;
+ }
+
+ return 'Assignment canceled';
+};
+
+const useSuccessfulCancellationToastContextValue = () => {
+ const [isToastOpen, setIsToastOpen] = useState(false);
+ const [assignmentUuidCount, setAssignmentUuidCount] = useState();
+
+ const handleDisplayToast = useCallback((assignmentUuids) => {
+ setIsToastOpen(true);
+ setAssignmentUuidCount(assignmentUuids);
+ }, []);
+
+ const handleCloseToast = useCallback(() => {
+ setIsToastOpen(false);
+ }, []);
+
+ const successfulAssignmentCancellationToastMessage = generateSuccessCancelMessage(assignmentUuidCount);
+
+ const successfulCancellationToastContextValue = useMemo(() => ({
+ isSuccessfulAssignmentCancellationToastOpen: isToastOpen,
+ displayToastForAssignmentCancellation: handleDisplayToast,
+ closeToastForAssignmentCancellation: handleCloseToast,
+ successfulAssignmentCancellationToastMessage,
+ }), [
+ isToastOpen,
+ handleDisplayToast,
+ handleCloseToast,
+ successfulAssignmentCancellationToastMessage,
+ ]);
+
+ return successfulCancellationToastContextValue;
+};
+
+export default useSuccessfulCancellationToastContextValue;
diff --git a/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx b/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
index d46d96dec2..c05f643881 100644
--- a/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
+++ b/src/components/learner-credit-management/search/tests/CatalogSearchResults.test.jsx
@@ -175,10 +175,12 @@ describe('Main Catalogs view works as expected', () => {
test('all courses rendered when search results available', async () => {
const budgetDetailPageContextValue = {
- isSuccessfulAssignmentAllocationToastOpen: false,
- totalLearnersAssigned: undefined,
- displayToastForAssignmentAllocation: jest.fn(),
- closeToastForAssignmentAllocation: jest.fn(),
+ successfulAssignmentToast: {
+ isSuccessfulAssignmentAllocationToastOpen: false,
+ totalLearnersAssigned: undefined,
+ displayToastForAssignmentAllocation: jest.fn(),
+ closeToastForAssignmentAllocation: jest.fn(),
+ },
};
renderWithRouter(
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 5138d10080..ac73a03d64 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -12,6 +12,7 @@ import { renderWithRouter, sendEnterpriseTrackEvent } from '@edx/frontend-enterp
import { act } from 'react-dom/test-utils';
import { v4 as uuidv4 } from 'uuid';
import { faker } from '@faker-js/faker';
+import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import BudgetDetailPage from '../BudgetDetailPage';
import {
@@ -51,8 +52,11 @@ jest.mock('../data', () => ({
useSubsidyAccessPolicy: jest.fn(),
useBudgetDetailActivityOverview: jest.fn(),
useIsLargeOrGreater: jest.fn().mockReturnValue(true),
+ useCancelContentAssignments: jest.fn(),
}));
+jest.mock('../../../data/services/EnterpriseAccessApiService');
+
const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
const enterpriseSlug = 'test-enterprise';
@@ -141,6 +145,11 @@ const mockEnrollmentTransactionWithReversal = {
reversal: mockEnrollmentTransactionReversal,
};
+const mockFailedCancelledLearnerAction = {
+ actionType: 'cancelled',
+ completedAt: null,
+ errorReason: 'email_error',
+};
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
@@ -801,7 +810,10 @@ describe(' ', () => {
expectedModalPopupHeading: 'Failed: Bad email',
expectedModalPopupContent: `This course assignment failed because a notification to ${mockLearnerEmail} could not be sent.`,
actions: [mockSuccessfulLinkedLearnerAction, mockFailedNotifiedAction],
- errorReason: 'email_error',
+ errorReason: {
+ errorReason: 'email_error',
+ actionType: 'notified',
+ },
},
{
learnerState: 'failed',
@@ -810,7 +822,10 @@ describe(' ', () => {
expectedModalPopupHeading: 'Failed: Bad email',
expectedModalPopupContent: 'This course assignment failed because a notification to the learner could not be sent.',
actions: [mockSuccessfulLinkedLearnerAction, mockFailedNotifiedAction],
- errorReason: 'email_error',
+ errorReason: {
+ errorReason: 'email_error',
+ actionType: 'notified',
+ },
},
{
learnerState: 'failed',
@@ -819,7 +834,34 @@ describe(' ', () => {
expectedModalPopupHeading: 'Failed: System',
expectedModalPopupContent: 'Something went wrong behind the scenes.',
actions: [mockFailedLinkedLearnerAction],
- errorReason: 'internal_api_error',
+ errorReason: {
+ errorReason: 'internal_api_error',
+ actionType: 'notified',
+ },
+ },
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: Cancellation',
+ expectedModalPopupHeading: 'Failed: Cancellation',
+ expectedModalPopupContent: 'Something went wrong behind the scenes.',
+ actions: [mockFailedCancelledLearnerAction],
+ errorReason: {
+ errorReason: 'email_error',
+ actionType: 'cancelled',
+ },
+ },
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: Cancellation',
+ expectedModalPopupHeading: 'Failed: Cancellation',
+ expectedModalPopupContent: 'Something went wrong behind the scenes.',
+ actions: [mockFailedCancelledLearnerAction],
+ errorReason: {
+ errorReason: 'internal_api_error',
+ actionType: 'cancelled',
+ },
},
])('renders correct status chips with assigned table data (%s)', ({
learnerState,
@@ -1158,4 +1200,137 @@ describe(' ', () => {
expect(remindButton).toBeDisabled();
}
});
+
+ it('cancels assignments in bulk', async () => {
+ EnterpriseAccessApiService.cancelContentAssignments.mockResolvedValueOnce({ status: 200 });
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useBudgetRedemptions.mockReturnValue({
+ isLoading: false,
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 2,
+ results: [
+ {
+ uuid: 'test-uuid1',
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
+ {
+ uuid: 'test-uuid2',
+ contentKey: mockCourseKey,
+ contentQuantity: -29900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-11-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
+ ],
+ learnerStateCounts: [
+ { learnerState: 'waiting', count: 1 },
+ { learnerState: 'waiting', count: 1 },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ renderWithRouter( );
+ const cancelRowAction = screen.getByTitle('Toggle All Current Page Rows Selected');
+ expect(cancelRowAction).toBeInTheDocument();
+ userEvent.click(cancelRowAction);
+ const cancelBulkActionButton = screen.getByText('Cancel (2)');
+ expect(cancelBulkActionButton).toBeInTheDocument();
+ userEvent.click(cancelBulkActionButton);
+ const modalDialog = screen.getByRole('dialog');
+ expect(modalDialog).toBeInTheDocument();
+ const cancelDialogButton = getButtonElement('Cancel assignments (2)');
+ userEvent.click(cancelDialogButton);
+ expect(
+ EnterpriseAccessApiService.cancelContentAssignments,
+ ).toHaveBeenCalled();
+ await waitFor(
+ () => expect(screen.getByText('Assignments canceled (2)')).toBeInTheDocument(),
+ );
+ });
+
+ it('cancels a single assignment', async () => {
+ EnterpriseAccessApiService.cancelContentAssignments.mockResolvedValueOnce({ status: 200 });
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useBudgetRedemptions.mockReturnValue({
+ isLoading: false,
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
+ ],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ renderWithRouter( );
+ const cancelIconButton = screen.getByTestId('cancel-assignment-test-uuid');
+ expect(cancelIconButton).toBeInTheDocument();
+ userEvent.click(cancelIconButton);
+ const modalDialog = screen.getByRole('dialog');
+ expect(modalDialog).toBeInTheDocument();
+ const cancelDialogButton = getButtonElement('Cancel assignment');
+ userEvent.click(cancelDialogButton);
+ await waitFor(
+ () => expect(screen.getByText('Assignment canceled')).toBeInTheDocument(),
+ );
+ });
});
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
index e219ee25b2..a5d07d93ae 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
@@ -96,8 +96,10 @@ describe(' ', () => {
}) => {
const ToastContextController = () => {
const {
- displayToastForAssignmentAllocation,
- closeToastForAssignmentAllocation,
+ successfulAssignmentToast: {
+ displayToastForAssignmentAllocation,
+ closeToastForAssignmentAllocation,
+ },
} = useContext(BudgetDetailPageContext);
const handleDisplayToast = () => {
@@ -143,4 +145,63 @@ describe(' ', () => {
expect(screen.queryByText(expectedToastMessage)).not.toBeInTheDocument();
});
});
+
+ it.each([
+ {
+ assignmentUUIDs: 1,
+ },
+ {
+ assignmentUUIDs: 2,
+ },
+ ])('should render Toast notification for successful assignment cancellation (%s)', async ({
+ assignmentUUIDs,
+ }) => {
+ const ToastContextController = () => {
+ const {
+ successfulCancellationToast: {
+ displayToastForAssignmentCancellation,
+ closeToastForAssignmentCancellation,
+ },
+ } = useContext(BudgetDetailPageContext);
+
+ const handleDisplayToast = () => {
+ displayToastForAssignmentCancellation(assignmentUUIDs);
+ };
+
+ const handleCloseToast = () => {
+ closeToastForAssignmentCancellation();
+ };
+
+ return (
+
+ Open Toast
+ Close Toast
+
+ );
+ };
+ render( );
+
+ const toastMessages = [];
+ if (assignmentUUIDs > 1) {
+ toastMessages.push(`Assignments canceled (${assignmentUUIDs})`);
+ }
+ if (assignmentUUIDs === 1) {
+ toastMessages.push('Assignment canceled');
+ }
+ const expectedToastMessage = toastMessages.join(' ');
+
+ // Open Toast notification
+ userEvent.click(getButtonElement('Open Toast'));
+
+ // Verify Toast notification is rendered
+ expect(screen.getByText(expectedToastMessage)).toBeInTheDocument();
+
+ // Close Toast notification
+ userEvent.click(getButtonElement('Close Toast'));
+
+ // Verify Toast notification is no longer rendered
+ await waitFor(() => {
+ expect(screen.queryByText(expectedToastMessage)).not.toBeInTheDocument();
+ });
+ });
});
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index 94e4fcae95..9ac00c5a52 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -172,6 +172,17 @@ class EnterpriseAccessApiService {
return EnterpriseAccessApiService.apiClient().get(url);
}
+ /**
+ * Cancel content assignments for a specific AssignmentConfiguration.
+ */
+ static cancelContentAssignments(assignmentConfigurationUUID, assignmentUuids) {
+ const options = {
+ assignment_uuids: assignmentUuids,
+ };
+ const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/cancel/`;
+ return EnterpriseAccessApiService.apiClient().post(url, options);
+ }
+
/**
* Retrieve a specific subsidy access policy.
* @param {string} subsidyAccessPolicyUUID The UUID of the subsidy access policy to retrieve.
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index 7317e14639..f384281752 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -19,6 +19,7 @@ const mockLicenseRequestUUID = 'test-license-request-uuid';
const mockCouponCodeRequestUUID = 'test-coupon-code-request-uuid';
const mockAssignmentConfigurationUUID = 'test-assignment-configuration-uuid';
const mockSubsidyAccessPolicyUUID = 'test-subsidy-access-policy-uuid';
+const mockAssignmentUUIDs = ['test-assignment-uuid1', 'test-assignment-uuid-2'];
describe('EnterpriseAccessApiService', () => {
beforeEach(() => {
@@ -188,4 +189,15 @@ describe('EnterpriseAccessApiService', () => {
payload,
);
});
+
+ test('cancelContentAssignments calls enterprise-access cancel POST API to cancel assignments', () => {
+ const options = {
+ assignment_uuids: mockAssignmentUUIDs,
+ };
+ EnterpriseAccessApiService.cancelContentAssignments(mockAssignmentConfigurationUUID, mockAssignmentUUIDs);
+ expect(axios.post).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/cancel/`,
+ options,
+ );
+ });
});
From b990fec8bce87097cad839ea6adeb88843aebaf4 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Wed, 6 Dec 2023 13:55:08 -0500
Subject: [PATCH 095/124] feat: display only active budgets from
subsidy-access-policy (#1122)
---
src/data/services/EnterpriseAccessApiService.js | 1 +
src/data/services/tests/EnterpriseAccessApiService.test.js | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index 9ac00c5a52..cb7e0da9a5 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -167,6 +167,7 @@ class EnterpriseAccessApiService {
static listSubsidyAccessPolicies(enterpriseCustomerId) {
const queryParams = new URLSearchParams({
enterprise_customer_uuid: enterpriseCustomerId,
+ active: true,
});
const url = `${EnterpriseAccessApiService.baseUrl}/subsidy-access-policies/?${queryParams.toString()}`;
return EnterpriseAccessApiService.apiClient().get(url);
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index f384281752..51fe177d49 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -166,7 +166,7 @@ describe('EnterpriseAccessApiService', () => {
test('listSubsidyAccessPolicies calls enterprise-access to fetch subsidy access policies', () => {
EnterpriseAccessApiService.listSubsidyAccessPolicies(mockEnterpriseUUID);
expect(axios.get).toBeCalledWith(
- `${enterpriseAccessBaseUrl}/api/v1/subsidy-access-policies/?enterprise_customer_uuid=${mockEnterpriseUUID}`,
+ `${enterpriseAccessBaseUrl}/api/v1/subsidy-access-policies/?enterprise_customer_uuid=${mockEnterpriseUUID}&active=true`,
);
});
From 4f335005d932cab4826766578ceea0cb7dc08820 Mon Sep 17 00:00:00 2001
From: Marlon Keating <322346+marlonkeating@users.noreply.github.com>
Date: Wed, 6 Dec 2023 13:15:48 -0800
Subject: [PATCH 096/124] feat: Persist SSO Config 'authorized' checkbox state
(#1121)
---
.../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 37 +++++++++++--------
.../steps/NewSSOConfigAuthorizeStep.tsx | 10 ++---
.../tests/NewSSOConfigForm.test.jsx | 6 ++-
3 files changed, 29 insertions(+), 24 deletions(-)
diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
index ca7dd08e07..218811ac3e 100644
--- a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
+++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
@@ -1,5 +1,6 @@
import omit from 'lodash/omit';
+import { AxiosError } from 'axios';
import type { FormWorkflowHandlerArgs, FormWorkflowStep } from '../../forms/FormWorkflow';
import SSOConfigConnectStep, { validations as SSOConfigConnectStepValidations } from './steps/NewSSOConfigConnectStep';
import SSOConfigConfigureStep, { validations as SSOConfigConfigureStepValidations } from './steps/NewSSOConfigConfigureStep';
@@ -8,7 +9,6 @@ import SSOConfigConfirmStep from './steps/NewSSOConfigConfirmStep';
import LmsApiService from '../../../data/services/LmsApiService';
import handleErrors from '../utils';
import { snakeCaseDict } from '../../../utils';
-import { AxiosError } from 'axios';
import { INVALID_IDP_METADATA_ERROR, RECORD_UNDER_CONFIGURATIONS_ERROR } from '../data/constants';
type SSOConfigSnakeCase = {
@@ -40,6 +40,7 @@ type SSOConfigSnakeCase = {
oauth_user_id: string,
sp_metadata_url?: string,
record?: object,
+ marked_authorized: boolean
};
export type SSOConfigCamelCase = {
@@ -70,7 +71,8 @@ export type SSOConfigCamelCase = {
sapsfPrivateKey: string,
odataClientId: string,
oauthUserId: string,
- spMetadataUrl?: string
+ spMetadataUrl?: string,
+ markedAuthorized: boolean
};
type SSOConfigFormControlVariables = {
@@ -81,13 +83,6 @@ type SSOConfigFormControlVariables = {
type SSOConfigFormContextData = SSOConfigCamelCase & SSOConfigFormControlVariables;
export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
- const placeHolderButton = (buttonName?: string) => () => ({
- buttonText: buttonName || 'Next',
- opensNewWindow: false,
- onClick: () => { },
- preventDefaultErrorModal: false,
- });
-
const advanceConnectStep = async ({
formFields,
errHandler,
@@ -98,7 +93,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
const sanitizeAndCopyFormFields = (formFields: SSOConfigSnakeCase) => {
const copiedFormFields = { ...formFields };
- return omit(copiedFormFields, ['record', 'sp_metadata_url', 'submitted_at', 'configured_at','validated_at']);
+ return omit(copiedFormFields, ['record', 'sp_metadata_url', 'submitted_at', 'configured_at', 'validated_at']);
};
const saveChanges = async ({
@@ -117,7 +112,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
let updatedFormFields: SSOConfigCamelCase = omit(formFields, ['idpConnectOption', 'spMetadataUrl', 'isPendingConfiguration']);
updatedFormFields.enterpriseCustomer = enterpriseId;
const submittedFormFields: SSOConfigSnakeCase = snakeCaseDict(updatedFormFields) as SSOConfigSnakeCase;
- let copiedFormFields = sanitizeAndCopyFormFields(submittedFormFields);
+ const copiedFormFields = sanitizeAndCopyFormFields(submittedFormFields);
if (copiedFormFields?.uuid) {
try {
const updateResponse = await LmsApiService.updateEnterpriseSsoOrchestrationRecord(
@@ -127,9 +122,9 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
updatedFormFields = updateResponse.data;
} catch (error: AxiosError | any) {
err = handleErrors(error);
- if (error.message?.includes("Must provide valid IDP metadata url")) {
+ if (error.message?.includes('Must provide valid IDP metadata url')) {
errHandler?.(INVALID_IDP_METADATA_ERROR);
- } else if (error.message?.includes("Record has already been submitted for configuration.")) {
+ } else if (error.message?.includes('Record has already been submitted for configuration.')) {
errHandler?.(RECORD_UNDER_CONFIGURATIONS_ERROR);
} else {
setConfigureError(error);
@@ -142,7 +137,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
updatedFormFields.spMetadataUrl = createResponse.data.sp_metadata_url;
} catch (error: AxiosError | any) {
err = handleErrors(error);
- if (error.message?.includes("Must provide valid IDP metadata url")) {
+ if (error.message?.includes('Must provide valid IDP metadata url')) {
errHandler?.(INVALID_IDP_METADATA_ERROR);
} else {
setConfigureError(error);
@@ -184,7 +179,12 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
formComponent: SSOConfigAuthorizeStep,
validations: SSOConfigAuthorizeStepValidations,
stepName: 'Authorize',
- nextButtonConfig: placeHolderButton(),
+ nextButtonConfig: () => ({
+ buttonText: 'Next',
+ opensNewWindow: false,
+ onClick: saveChanges,
+ preventDefaultErrorModal: false,
+ }),
showBackButton: true,
showCancelButton: false,
}, {
@@ -192,7 +192,12 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
formComponent: SSOConfigConfirmStep,
validations: [],
stepName: 'Confirm and Test',
- nextButtonConfig: placeHolderButton('Finish'),
+ nextButtonConfig: () => ({
+ buttonText: 'Finish',
+ opensNewWindow: false,
+ onClick: () => {},
+ preventDefaultErrorModal: false,
+ }),
showBackButton: true,
showCancelButton: false,
},
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
index dd640f2450..88d280c983 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx
@@ -1,21 +1,20 @@
import React, { useContext } from 'react';
import { useParams } from 'react-router-dom';
import {
- Alert, Form, Hyperlink, Button, Row,
+ Alert, Hyperlink, Button, Row,
} from '@edx/paragon';
import { Info, Download } from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import { createSAMLURLs } from '../utils';
import { SSOConfigContext } from '../SSOConfigContext';
-import { setFormFieldAction } from '../../../forms/data/actions';
import { FormFieldValidation, useFormContext } from '../../../forms/FormContext';
import ValidatedFormCheckbox from '../../../forms/ValidatedFormCheckbox';
export const validations: FormFieldValidation[] = [
{
- formFieldId: 'confirmAuthorizedEdxServiceProvider',
+ formFieldId: 'markedAuthorized',
validator: (fields) => {
- const ret = !fields.confirmAuthorizedEdxServiceProvider && 'Please verify authorization of edX as a Service Provider.';
+ const ret = !fields.markedAuthorized && 'Please verify authorization of edX as a Service Provider.';
return ret;
},
},
@@ -40,7 +39,6 @@ const SSOConfigAuthorizeStep = () => {
ssoState,
} = useContext(SSOConfigContext);
const {
- dispatch,
formFields,
} = useFormContext();
const { enterpriseSlug } = useParams();
@@ -80,7 +78,7 @@ const SSOConfigAuthorizeStep = () => {
Return to this window and check the box once complete
-
+
I have authorized edX as a Service Provider
>
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
index 084157a67b..3f08d7cbe5 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx
@@ -421,7 +421,10 @@ describe('SAML Config Tab', () => {
test('navigate through new non-SAP sso workflow', async () => {
setupNewSSOStepper();
const mockCreateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'createEnterpriseSsoOrchestrationRecord');
- mockCreateEnterpriseSsoOrchestrationRecord.mockResolvedValue({ data: { record: 'fakeuuid', sp_metadata_url: 'https://fake.url' } });
+ const mockUpdateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord');
+ const mockReturnData = { data: { record: 'fakeuuid', sp_metadata_url: 'https://fake.url' } };
+ mockCreateEnterpriseSsoOrchestrationRecord.mockResolvedValue(mockReturnData);
+ mockUpdateEnterpriseSsoOrchestrationRecord.mockResolvedValue(mockReturnData);
jest.spyOn(Router, 'useParams').mockReturnValue({ enterpriseSlug: 'testslug' });
// Connect Step
await waitFor(() => {
@@ -504,7 +507,6 @@ describe('SAML Config Tab', () => {
}, []);
screen.queryByText(testMetadataUrl);
});
- // TODO: Test case where we go SAP route
test('navigate through new SAP sso workflow', async () => {
setupNewSSOStepper();
const mockCreateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'createEnterpriseSsoOrchestrationRecord');
From d1252adb1955c9a82ce425dd7f269b5806d65c08 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Wed, 6 Dec 2023 16:49:32 -0500
Subject: [PATCH 097/124] fix: ensure emails with uppercase letter passes
duplicate email validation (#1124)
---
.../cards/data/utils.js | 22 +++++++++++++++++--
.../cards/tests/CourseCard.test.jsx | 2 +-
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/components/learner-credit-management/cards/data/utils.js b/src/components/learner-credit-management/cards/data/utils.js
index 8dd63d436c..63136a412e 100644
--- a/src/components/learner-credit-management/cards/data/utils.js
+++ b/src/components/learner-credit-management/cards/data/utils.js
@@ -52,8 +52,26 @@ export const isEmailAddressesInputValueValid = ({
const remainingBalanceAfterAssignment = remainingBalance - totalAssignmentCost;
const hasEnoughBalanceForAssigment = remainingBalanceAfterAssignment >= 0;
- const invalidEmails = learnerEmails.filter((email) => !isEmail(email));
- const duplicateEmails = learnerEmails.filter((email, index) => learnerEmails.indexOf(email.toLowerCase()) !== index);
+ const lowerCasedEmails = [];
+ const invalidEmails = [];
+ const duplicateEmails = [];
+
+ learnerEmails.forEach((email) => {
+ const lowerCasedEmail = email.toLowerCase();
+
+ // Validate the email address
+ if (!isEmail(email)) {
+ invalidEmails.push(email);
+ }
+
+ // Check for duplicates (case-insensitive)
+ if (lowerCasedEmails.includes(lowerCasedEmail)) {
+ duplicateEmails.push(email);
+ }
+
+ // Add to list of lower-cased emails already handled
+ lowerCasedEmails.push(lowerCasedEmail);
+ });
const isValidInput = invalidEmails.length === 0 && duplicateEmails.length === 0;
const canAllocate = learnerEmailsCount > 0 && hasEnoughBalanceForAssigment && isValidInput;
diff --git a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
index 7840ecad59..517f14fbd5 100644
--- a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
@@ -548,7 +548,7 @@ describe('Course card works as expected', () => {
expectedValidationMessage: 'The total assignment cost exceeds your available Learner Credit budget balance of $100. Please remove learners and try again.',
},
{
- learnerEmails: ['a@a.com', 'b@b.com', 'c@c.com'],
+ learnerEmails: ['a@a.com', 'B@b.com', 'c@c.com'],
spendAvailableUsd: 1000,
expectedValidationMessage: undefined, // no validation error
},
From 2296c6c4e0f40842af3d58224bd9c55765dcc483 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Wed, 6 Dec 2023 17:17:37 -0500
Subject: [PATCH 098/124] fix: ensure pending balance is shown on budget cards
even if 0 (#1120)
---
.../EnterpriseSubsidiesContext/data/hooks.js | 2 ++
.../data/tests/hooks.test.jsx | 1 +
.../learner-credit-management/BudgetCard.jsx | 2 ++
.../learner-credit-management/SubBudgetCard.jsx | 4 +++-
.../data/hooks/useSubsidyAccessPolicy.js | 10 ++--------
.../tests/BudgetCard.test.jsx | 1 +
src/utils.js | 12 ++++++++++++
7 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index c576a19541..d30b9d2e3f 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -12,6 +12,7 @@ import SubsidyApiService from '../../../data/services/EnterpriseSubsidyApiServic
import { BUDGET_TYPES } from '../../EnterpriseApp/data/constants';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys } from '../../learner-credit-management/data';
+import { isAssignableSubsidyAccessPolicyType } from '../../../utils';
dayjs.extend(isBetween);
@@ -74,6 +75,7 @@ async function fetchEnterpriseBudgets({
spent: result.aggregates.amountRedeemedUsd,
pending: result.aggregates.amountAllocatedUsd,
},
+ isAssignable: isAssignableSubsidyAccessPolicyType(result),
});
});
enterpriseSubsidyResults?.forEach((result) => {
diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx
index 4a8b09fa08..6fc0c3bb38 100644
--- a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx
+++ b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.jsx
@@ -243,6 +243,7 @@ describe('useEnterpriseBudgets', () => {
end: mockBudgetEnd,
isCurrent: true,
source: BUDGET_TYPES.policy,
+ isAssignable: false,
aggregates: {
available: 700,
spent: 200,
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index db5478548c..19d70f79a1 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -40,6 +40,7 @@ const BudgetCard = ({
pending={budget.aggregates.pending}
displayName={budget.name}
enterpriseSlug={enterpriseSlug}
+ isAssignable={budget.isAssignable}
/>
);
}
@@ -94,6 +95,7 @@ BudgetCard.propTypes = {
spent: PropTypes.number.isRequired,
pending: PropTypes.number,
}),
+ isAssignable: PropTypes.bool,
}).isRequired,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
index 1a6125bdc4..acd5a33560 100644
--- a/src/components/learner-credit-management/SubBudgetCard.jsx
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -31,6 +31,7 @@ const SubBudgetCard = ({
displayName,
enterpriseSlug,
isLoading,
+ isAssignable,
}) => {
const { isFetchingBudgets } = useContext(EnterpriseSubsidiesContext);
const budgetLabel = getBudgetStatus(start, end);
@@ -81,7 +82,7 @@ const SubBudgetCard = ({
{isFetchingBudgets ? : formatPrice(available)}
- {pending > 0 && (
+ {isAssignable && (
Pending
@@ -128,6 +129,7 @@ SubBudgetCard.propTypes = {
available: PropTypes.number,
pending: PropTypes.number,
displayName: PropTypes.string,
+ isAssignable: PropTypes.bool,
};
export default SubBudgetCard;
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
index 7058cdcb1a..620c66d3dd 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
+++ b/src/components/learner-credit-management/data/hooks/useSubsidyAccessPolicy.js
@@ -3,13 +3,7 @@ import { camelCaseObject } from '@edx/frontend-platform/utils';
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys } from '../constants';
-
-const determineBudgetAssignability = (subsidyAccessPolicy) => {
- const policyType = subsidyAccessPolicy?.policyType;
- const isAssignable = !!subsidyAccessPolicy?.assignmentConfiguration;
- const assignableSubsidyAccessPolicyTypes = ['AssignedLearnerCreditAccessPolicy'];
- return isAssignable && assignableSubsidyAccessPolicyTypes.includes(policyType);
-};
+import { isAssignableSubsidyAccessPolicyType } from '../../../../utils';
/**
* Retrieves a subsidy access policy by UUID from the API.
@@ -21,7 +15,7 @@ const getSubsidyAccessPolicy = async ({ queryKey }) => {
const subsidyAccessPolicyUUID = queryKey[2];
const response = await EnterpriseAccessApiService.retrieveSubsidyAccessPolicy(subsidyAccessPolicyUUID);
const subsidyAccessPolicy = camelCaseObject(response.data);
- subsidyAccessPolicy.isAssignable = determineBudgetAssignability(subsidyAccessPolicy);
+ subsidyAccessPolicy.isAssignable = isAssignableSubsidyAccessPolicyType(subsidyAccessPolicy);
return subsidyAccessPolicy;
};
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index 50e8972145..b030cb6d17 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -212,6 +212,7 @@ describe(' ', () => {
pending: isAssignableBudget ? mockBudgetAggregates.pending : undefined,
spent: mockBudgetAggregates.spent,
},
+ isAssignable: isAssignableBudget,
};
useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
diff --git a/src/utils.js b/src/utils.js
index 207d25c8bc..f3fa4e103e 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -412,6 +412,17 @@ function defaultQueryClientRetryHandler(failureCount, err) {
return true;
}
+/**
+ * Determines whether a subsidy access policy is assignable, based on its policy type
+ * and the presence of an assignment configuration.
+ */
+function isAssignableSubsidyAccessPolicyType(policy) {
+ const policyType = policy?.policyType;
+ const isAssignable = !!policy?.assignmentConfiguration;
+ const assignableSubsidyAccessPolicyTypes = ['AssignedLearnerCreditAccessPolicy'];
+ return isAssignable && assignableSubsidyAccessPolicyTypes.includes(policyType);
+}
+
export {
camelCaseDict,
camelCaseDictArray,
@@ -446,4 +457,5 @@ export {
pollAsync,
isNotValidNumberString,
defaultQueryClientRetryHandler,
+ isAssignableSubsidyAccessPolicyType,
};
From 7b59077a041b30654c7e66e53b0deac705149ea7 Mon Sep 17 00:00:00 2001
From: Kira Miller <31229189+kiram15@users.noreply.github.com>
Date: Wed, 6 Dec 2023 15:36:01 -0700
Subject: [PATCH 099/124] feat: add remind functionality (#1123)
* feat: add remind functionality
* fix: adding additional test coverage
* fix: PR requests
* fix: final review
---
.../AssignmentRowActionTableCell.jsx | 22 +--
.../AssignmentStatusTableCell.jsx | 5 +-
.../AssignmentTableCancel.jsx | 4 +-
.../AssignmentTableRemind.jsx | 38 +++--
.../BudgetDetailPageWrapper.jsx | 28 +++-
.../CancelAssignmentModal.jsx | 8 +-
.../PendingAssignmentCancelButton.jsx | 4 +-
.../PendingAssignmentRemindButton.jsx | 50 ++++++
.../RemindAssignmentModal.jsx | 71 ++++++++
.../FailedReminder.jsx | 56 +++++++
.../cards/NewAssignmentModalButton.jsx | 1 +
.../data/hooks/index.js | 1 +
.../data/hooks/useCancelContentAssignments.js | 10 +-
.../useCancelContentAssignments.test.jsx | 12 +-
.../data/hooks/useRemindContentAssignments.js | 42 +++++
.../useRemindContentAssignments.test.jsx | 141 ++++++++++++++++
.../useSuccessfulReminderToastContextValue.js | 39 +++++
.../tests/BudgetDetailPage.test.jsx | 151 ++++++++++++++++++
.../tests/BudgetDetailPageWrapper.test.jsx | 59 +++++++
.../services/EnterpriseAccessApiService.js | 11 ++
.../tests/EnterpriseAccessApiService.test.js | 11 ++
21 files changed, 710 insertions(+), 54 deletions(-)
create mode 100644 src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
create mode 100644 src/components/learner-credit-management/RemindAssignmentModal.jsx
create mode 100644 src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
create mode 100644 src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/useSuccessfulReminderToastContextValue.js
diff --git a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
index 74398b32c5..fbe36161fe 100644
--- a/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentRowActionTableCell.jsx
@@ -1,31 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {
- Icon, IconButton, OverlayTrigger, Stack, Tooltip,
-} from '@edx/paragon';
-import { Mail } from '@edx/paragon/icons';
+import { Stack } from '@edx/paragon';
+import PendingAssignmentRemindButton from './PendingAssignmentRemindButton';
import PendingAssignmentCancelButton from './PendingAssignmentCancelButton';
const AssignmentRowActionTableCell = ({ row }) => {
const isLearnerStateWaiting = row.original.learnerState === 'waiting';
- const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
return (
{isLearnerStateWaiting && (
- Remind learner}
- >
- console.log(`Reminding ${row.original.uuid}`)}
- data-testid={`remind-assignment-${row.original.uuid}`}
- />
-
+
)}
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
index 63dffc765c..10cb79caf9 100644
--- a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -4,6 +4,7 @@ import {
import PropTypes from 'prop-types';
import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
import FailedCancellation from './assignments-status-chips/FailedCancellation';
+import FailedReminder from './assignments-status-chips/FailedReminder';
import FailedSystem from './assignments-status-chips/FailedSystem';
import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
@@ -44,10 +45,12 @@ const AssignmentStatusTableCell = ({ row }) => {
}
return ;
}
-
if (errorReason.actionType === 'cancelled') {
return ;
}
+ if (errorReason.actionType === 'reminded') {
+ return ;
+ }
}
// Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
index cb45c1f4eb..a160934ef4 100644
--- a/src/components/learner-credit-management/AssignmentTableCancel.jsx
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -9,7 +9,7 @@ const AssignmentTableCancelAction = ({ selectedFlatRows }) => {
const assignmentUuids = selectedFlatRows.map(row => row.id);
const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
const {
- assignButtonState,
+ cancelButtonState,
cancelContentAssignments,
close,
isOpen,
@@ -25,7 +25,7 @@ const AssignmentTableCancelAction = ({ selectedFlatRows }) => {
cancelContentAssignments={cancelContentAssignments}
close={close}
isOpen={isOpen}
- assignButtonState={assignButtonState}
+ cancelButtonState={cancelButtonState}
uuidCount={assignmentUuids.length}
/>
>
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
index cc62c9a703..e63fbfac72 100644
--- a/src/components/learner-credit-management/AssignmentTableRemind.jsx
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -2,18 +2,38 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { Mail } from '@edx/paragon/icons';
+import useRemindContentAssignments from './data/hooks/useRemindContentAssignments';
+import RemindAssignmentModal from './RemindAssignmentModal';
-const AssignmentTableRemindAction = ({ selectedFlatRows, ...rest }) => {
+const AssignmentTableRemindAction = ({ selectedFlatRows }) => {
+ const assignmentUuids = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').map(({ id }) => id);
+ const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
const selectedRemindableRows = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').length;
+ const {
+ remindButtonState,
+ remindContentAssignments,
+ close,
+ isOpen,
+ open,
+ } = useRemindContentAssignments(assignmentConfigurationUuid, assignmentUuids);
return (
- console.log('Remind', selectedFlatRows, rest)}
- >
- {`Remind (${selectedRemindableRows})`}
-
+ <>
+
+ {`Remind (${selectedRemindableRows})`}
+
+
+ >
);
};
diff --git a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
index 0702441137..41904c2938 100644
--- a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
@@ -4,7 +4,7 @@ import { Helmet } from 'react-helmet';
import { Container, Toast } from '@edx/paragon';
import Hero from '../Hero';
-import { useSuccessfulAssignmentToastContextValue, useSuccessfulCancellationToastContextValue } from './data';
+import { useSuccessfulAssignmentToastContextValue, useSuccessfulCancellationToastContextValue, useSuccessfulReminderToastContextValue } from './data';
const PAGE_TITLE = 'Learner Credit Management';
@@ -22,6 +22,7 @@ const BudgetDetailPageWrapper = ({
const successfulAssignmentToast = useSuccessfulAssignmentToastContextValue();
const successfulCancellationToast = useSuccessfulCancellationToastContextValue();
+ const successfulReminderToast = useSuccessfulReminderToastContextValue();
const {
isSuccessfulAssignmentAllocationToastOpen,
@@ -35,10 +36,17 @@ const BudgetDetailPageWrapper = ({
closeToastForAssignmentCancellation,
} = successfulCancellationToast;
+ const {
+ isSuccessfulAssignmentReminderToastOpen,
+ successfulAssignmentReminderToastMessage,
+ closeToastForAssignmentReminder,
+ } = successfulReminderToast;
+
const values = useMemo(() => ({
successfulAssignmentToast,
successfulCancellationToast,
- }), [successfulAssignmentToast, successfulCancellationToast]);
+ successfulReminderToast,
+ }), [successfulAssignmentToast, successfulCancellationToast, successfulReminderToast]);
return (
{/**
- Successful assignment allocation and cancellation Toast notifications. It is rendered here to guarantee that the
- Toast component will not be unmounted when the user programmatically navigates to the "Activity"
- tab, which will unmount the course cards that rendered the assignment modal. Thus, the Toast must
- be rendered within the component tree that's common to both the "Activity" and "Overview" tabs.
+ Successful assignment allocation, reminder, and cancellation Toast notifications. It is rendered
+ here to guarantee that the Toast component will not be unmounted when the user programmatically
+ navigates to the "Activity" tab, which will unmount the course cards that rendered the assignment
+ modal. Thus, the Toast must be rendered within the component tree that's common to both the
+ "Activity" and "Overview" tabs.
*/}
{successfulAssignmentCancellationToastMessage}
+
+
+ {successfulAssignmentReminderToastMessage}
+
);
};
diff --git a/src/components/learner-credit-management/CancelAssignmentModal.jsx b/src/components/learner-credit-management/CancelAssignmentModal.jsx
index 2636fdb13d..cce7b896b1 100644
--- a/src/components/learner-credit-management/CancelAssignmentModal.jsx
+++ b/src/components/learner-credit-management/CancelAssignmentModal.jsx
@@ -7,7 +7,7 @@ import { DoNotDisturbOn } from '@edx/paragon/icons';
import { BudgetDetailPageContext } from './BudgetDetailPageWrapper';
const CancelAssignmentModal = ({
- assignButtonState,
+ cancelButtonState,
cancelContentAssignments,
close,
isOpen,
@@ -46,7 +46,7 @@ const CancelAssignmentModal = ({
Go back
1 ? `Cancel assignments (${uuidCount})` : 'Cancel assignment',
pending: 'Canceling...',
@@ -54,7 +54,7 @@ const CancelAssignmentModal = ({
error: 'Try again',
}}
variant="danger"
- state={assignButtonState}
+ state={cancelButtonState}
onClick={handleOnClick}
/>
@@ -64,7 +64,7 @@ const CancelAssignmentModal = ({
};
CancelAssignmentModal.propTypes = {
- assignButtonState: PropTypes.string.isRequired,
+ cancelButtonState: PropTypes.string.isRequired,
cancelContentAssignments: PropTypes.func.isRequired,
close: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
diff --git a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
index 692cdd4cfb..5b2144b149 100644
--- a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
+++ b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
@@ -10,7 +10,7 @@ import CancelAssignmentModal from './CancelAssignmentModal';
const PendingAssignmentCancelButton = ({ row }) => {
const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
const {
- assignButtonState,
+ cancelButtonState,
cancelContentAssignments,
close,
isOpen,
@@ -29,7 +29,7 @@ const PendingAssignmentCancelButton = ({ row }) => {
variant="danger"
/>
{
+ const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
+ const {
+ remindButtonState,
+ remindContentAssignments,
+ close,
+ isOpen,
+ open,
+ } = useRemindContentAssignments(row.original.assignmentConfiguration, [row.original.uuid]);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+PendingAssignmentRemindButton.propTypes = {
+ row: PropTypes.shape({
+ original: PropTypes.shape({
+ assignmentConfiguration: PropTypes.string.isRequired,
+ learnerEmail: PropTypes.string,
+ uuid: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+};
+
+export default PendingAssignmentRemindButton;
diff --git a/src/components/learner-credit-management/RemindAssignmentModal.jsx b/src/components/learner-credit-management/RemindAssignmentModal.jsx
new file mode 100644
index 0000000000..013535e2b9
--- /dev/null
+++ b/src/components/learner-credit-management/RemindAssignmentModal.jsx
@@ -0,0 +1,71 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import {
+ ActionRow, ModalDialog, StatefulButton,
+} from '@edx/paragon';
+import { Mail } from '@edx/paragon/icons';
+import { BudgetDetailPageContext } from './BudgetDetailPageWrapper';
+
+const RemindAssignmentModal = ({
+ remindButtonState, remindContentAssignments, close, isOpen, uuidCount,
+}) => {
+ const {
+ successfulReminderToast: { displayToastForAssignmentReminder },
+ } = useContext(BudgetDetailPageContext);
+
+ const handleOnClick = async () => {
+ await remindContentAssignments();
+ displayToastForAssignmentReminder(uuidCount);
+ };
+
+ return (
+
+
+
+ Remind learner?
+
+
+
+ You are sending a reminder email to the selected learner
+ to take the next step on the course you assigned.
+
+ When your learner completes enrollment, the associated
+ "assigned" funds will be marked as "spent".
+
+
+
+
+
+ Go back
+ 1 ? `Send reminders (${uuidCount})` : 'Send reminder',
+ pending: 'Reminding...',
+ complete: 'Reminded',
+ error: 'Try again',
+ }}
+ variant="danger"
+ state={remindButtonState}
+ onClick={handleOnClick}
+ />
+
+
+
+ );
+};
+
+RemindAssignmentModal.propTypes = {
+ remindButtonState: PropTypes.string.isRequired,
+ remindContentAssignments: PropTypes.func.isRequired,
+ close: PropTypes.func.isRequired,
+ isOpen: PropTypes.bool.isRequired,
+ uuidCount: PropTypes.number,
+};
+
+export default RemindAssignmentModal;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
new file mode 100644
index 0000000000..d3b9533bb9
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
@@ -0,0 +1,56 @@
+import React, { useState } from 'react';
+import { Chip, useToggle, Hyperlink } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+import BaseModalPopup from './BaseModalPopup';
+
+const FailedReminder = () => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Failed: Reminder
+
+
+
+ Failed: Reminder
+
+
+ Your reminder email did not send. Something went wrong behind the scenes.
+
+
Suggested resolution steps
+
+
+ Wait and try to send this reminder again later, or send an email directly outside of the system.
+
+
+ If the issue continues, contact customer support.
+
+
+ Get more troubleshooting help at{' '}
+
+ Help Center: Course Assignments
+ .
+
+
+
+
+
+ >
+ );
+};
+
+export default FailedReminder;
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index e01816489e..47a3899735 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -197,6 +197,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_HELP_CENTER,
)}
destination="https://edx.org"
+ showLaunchIcon
target="_blank"
>
Help Center: Course Assignments
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index 400f754b57..d979f3867a 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -9,3 +9,4 @@ export { default as useBudgetDetailActivityOverview } from './useBudgetDetailAct
export { default as useIsLargeOrGreater } from './useIsLargeOrGreater';
export { default as useSuccessfulAssignmentToastContextValue } from './useSuccessfulAssignmentToastContextValue';
export { default as useSuccessfulCancellationToastContextValue } from './useSuccessfulCancellationToastContextValue';
+export { default as useSuccessfulReminderToastContextValue } from './useSuccessfulReminderToastContextValue';
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
index 8b9bec8d6f..189462a776 100644
--- a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
@@ -12,26 +12,26 @@ const useCancelContentAssignments = (
assignmentUuids,
) => {
const [isOpen, open, close] = useToggle(false);
- const [assignButtonState, setAssignButtonState] = useState('default');
+ const [cancelButtonState, setCancelButtonState] = useState('default');
const queryClient = useQueryClient();
const { subsidyAccessPolicyId } = useBudgetId();
const cancelContentAssignments = useCallback(async () => {
- setAssignButtonState('pending');
+ setCancelButtonState('pending');
try {
await EnterpriseAccessApiService.cancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
- setAssignButtonState('complete');
+ setCancelButtonState('complete');
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
});
} catch (err) {
logError(err);
- setAssignButtonState('error');
+ setCancelButtonState('error');
}
}, [assignmentConfigurationUuid, assignmentUuids, queryClient, subsidyAccessPolicyId]);
return {
- assignButtonState,
+ cancelButtonState,
cancelContentAssignments,
close,
isOpen,
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
index 4044003fef..e8bcad9513 100644
--- a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
@@ -49,7 +49,7 @@ describe('useCancelContentAssignments', () => {
);
expect(result.current).toEqual({
- assignButtonState: 'default',
+ cancelButtonState: 'default',
cancelContentAssignments: expect.any(Function),
close: expect.any(Function),
isOpen: false,
@@ -63,7 +63,7 @@ describe('useCancelContentAssignments', () => {
expect(logError).toBeCalledTimes(0);
expect(result.current).toEqual({
- assignButtonState: 'complete',
+ cancelButtonState: 'complete',
cancelContentAssignments: expect.any(Function),
close: expect.any(Function),
isOpen: false,
@@ -82,7 +82,7 @@ describe('useCancelContentAssignments', () => {
);
expect(result.current).toEqual({
- assignButtonState: 'default',
+ cancelButtonState: 'default',
cancelContentAssignments: expect.any(Function),
close: expect.any(Function),
isOpen: false,
@@ -96,7 +96,7 @@ describe('useCancelContentAssignments', () => {
expect(logError).toBeCalledTimes(0);
expect(result.current).toEqual({
- assignButtonState: 'complete',
+ cancelButtonState: 'complete',
cancelContentAssignments: expect.any(Function),
close: expect.any(Function),
isOpen: false,
@@ -116,7 +116,7 @@ describe('useCancelContentAssignments', () => {
);
expect(result.current).toEqual({
- assignButtonState: 'default',
+ cancelButtonState: 'default',
cancelContentAssignments: expect.any(Function),
close: expect.any(Function),
isOpen: false,
@@ -131,7 +131,7 @@ describe('useCancelContentAssignments', () => {
expect(logError).toBeCalledTimes(1);
expect(result.current).toEqual({
- assignButtonState: 'error',
+ cancelButtonState: 'error',
cancelContentAssignments: expect.any(Function),
close: expect.any(Function),
isOpen: false,
diff --git a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
new file mode 100644
index 0000000000..bc55743a30
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
@@ -0,0 +1,42 @@
+import { useCallback, useState } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
+import { logError } from '@edx/frontend-platform/logging';
+import { useToggle } from '@edx/paragon';
+
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import { learnerCreditManagementQueryKeys } from '../constants';
+import useBudgetId from './useBudgetId';
+
+const useRemindContentAssignments = (
+ assignmentConfigurationUuid,
+ assignmentUuids,
+) => {
+ const [isOpen, open, close] = useToggle(false);
+ const [remindButtonState, setRemindButtonState] = useState('default');
+ const queryClient = useQueryClient();
+ const { subsidyAccessPolicyId } = useBudgetId();
+
+ const remindContentAssignments = useCallback(async () => {
+ setRemindButtonState('pending');
+ try {
+ await EnterpriseAccessApiService.remindContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ setRemindButtonState('complete');
+ queryClient.invalidateQueries({
+ queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
+ });
+ } catch (err) {
+ logError(err);
+ setRemindButtonState('error');
+ }
+ }, [assignmentConfigurationUuid, assignmentUuids, queryClient, subsidyAccessPolicyId]);
+
+ return {
+ remindButtonState,
+ remindContentAssignments,
+ close,
+ isOpen,
+ open,
+ };
+};
+
+export default useRemindContentAssignments;
diff --git a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx
new file mode 100644
index 0000000000..88133f99f1
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx
@@ -0,0 +1,141 @@
+import { useParams } from 'react-router-dom';
+import { renderHook } from '@testing-library/react-hooks/dom';
+import { waitFor } from '@testing-library/react';
+import { QueryClientProvider } from '@tanstack/react-query';
+
+import { logError } from '@edx/frontend-platform/logging';
+
+import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
+import useRemindContentAssignments from './useRemindContentAssignments';
+import { queryClient } from '../../../test/testUtils';
+
+const TEST_ASSIGNMENT_CONFIGURATION_UUID = 'test-assignment-configuration-uuid';
+const TEST_PENDING_ASSIGNMENT_UUID_1 = 'test-pending-assignment-uuid_1';
+const TEST_PENDING_ASSIGNMENT_UUID_2 = 'test-pending-assignment-uuid_2';
+
+const wrapper = ({ children }) => (
+ {children}
+);
+
+jest.mock('../../../../data/services/EnterpriseAccessApiService');
+jest.mock('@edx/frontend-platform/logging', () => ({
+ logError: jest.fn(),
+}));
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn(),
+}));
+
+describe('useRemindContentAssignments', () => {
+ beforeEach(() => {
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should send a post request to remind a single pending assignment', async () => {
+ EnterpriseAccessApiService.remindContentAssignments.mockResolvedValueOnce({ status: 200 });
+ const { result } = renderHook(
+ () => useRemindContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ TEST_PENDING_ASSIGNMENT_UUID_1,
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ remindButtonState: 'default',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => result.current.remindContentAssignments());
+ expect(
+ EnterpriseAccessApiService.remindContentAssignments,
+ ).toHaveBeenCalled();
+ expect(logError).toBeCalledTimes(0);
+
+ expect(result.current).toEqual({
+ remindButtonState: 'complete',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+
+ it('should send a post request to remind multiple pending assignments', async () => {
+ EnterpriseAccessApiService.remindContentAssignments.mockResolvedValueOnce({ status: 200 });
+ const { result } = renderHook(
+ () => useRemindContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ [TEST_PENDING_ASSIGNMENT_UUID_1, TEST_PENDING_ASSIGNMENT_UUID_2],
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ remindButtonState: 'default',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => result.current.remindContentAssignments());
+ expect(
+ EnterpriseAccessApiService.remindContentAssignments,
+ ).toHaveBeenCalled();
+ expect(logError).toBeCalledTimes(0);
+
+ expect(result.current).toEqual({
+ remindButtonState: 'complete',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+
+ it('should handle assignment reminder error', async () => {
+ const error = new Error('An error occurred');
+ EnterpriseAccessApiService.remindContentAssignments.mockRejectedValueOnce(error);
+ const { result } = renderHook(
+ () => useRemindContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ [TEST_PENDING_ASSIGNMENT_UUID_1, TEST_PENDING_ASSIGNMENT_UUID_2],
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ remindButtonState: 'default',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => result.current.remindContentAssignments());
+
+ expect(
+ EnterpriseAccessApiService.remindContentAssignments,
+ ).toHaveBeenCalled();
+ expect(logError).toBeCalledTimes(1);
+
+ expect(result.current).toEqual({
+ remindButtonState: 'error',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/useSuccessfulReminderToastContextValue.js b/src/components/learner-credit-management/data/hooks/useSuccessfulReminderToastContextValue.js
new file mode 100644
index 0000000000..27faaa9918
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useSuccessfulReminderToastContextValue.js
@@ -0,0 +1,39 @@
+import { useCallback, useMemo, useState } from 'react';
+
+const generateSuccessRemindMessage = (assignmentUuidCount) => {
+ if (assignmentUuidCount > 1) {
+ return `Reminders sent (${assignmentUuidCount})`;
+ }
+ return 'Reminder sent';
+};
+
+const useSuccessfulReminderToastContextValue = () => {
+ const [isToastOpen, setIsToastOpen] = useState(false);
+ const [assignmentUuidCount, setAssignmentUuidCount] = useState();
+
+ const handleDisplayToast = useCallback((assignmentUuids) => {
+ setIsToastOpen(true);
+ setAssignmentUuidCount(assignmentUuids);
+ }, []);
+
+ const handleCloseToast = useCallback(() => {
+ setIsToastOpen(false);
+ }, []);
+
+ const successfulAssignmentReminderToastMessage = generateSuccessRemindMessage(assignmentUuidCount);
+
+ const successfulReminderToastContextValue = useMemo(() => ({
+ isSuccessfulAssignmentReminderToastOpen: isToastOpen,
+ displayToastForAssignmentReminder: handleDisplayToast,
+ closeToastForAssignmentReminder: handleCloseToast,
+ successfulAssignmentReminderToastMessage,
+ }), [
+ isToastOpen,
+ handleDisplayToast,
+ handleCloseToast,
+ successfulAssignmentReminderToastMessage,
+ ]);
+ return successfulReminderToastContextValue;
+};
+
+export default useSuccessfulReminderToastContextValue;
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index ac73a03d64..aee974de7c 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -150,6 +150,13 @@ const mockFailedCancelledLearnerAction = {
completedAt: null,
errorReason: 'email_error',
};
+
+const mockFailedReminderLearnerAction = {
+ actionType: 'reminded',
+ completedAt: null,
+ errorReason: 'email_error',
+};
+
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
@@ -863,6 +870,18 @@ describe(' ', () => {
actionType: 'cancelled',
},
},
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: Reminder',
+ expectedModalPopupHeading: 'Failed: Reminder',
+ expectedModalPopupContent: 'Something went wrong behind the scenes.',
+ actions: [mockFailedReminderLearnerAction],
+ errorReason: {
+ errorReason: 'internal_api_error',
+ actionType: 'reminded',
+ },
+ },
])('renders correct status chips with assigned table data (%s)', ({
learnerState,
hasLearnerEmail,
@@ -1277,6 +1296,82 @@ describe(' ', () => {
);
});
+ it('reminds assignments in bulk', async () => {
+ EnterpriseAccessApiService.remindContentAssignments.mockResolvedValueOnce({ status: 200 });
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useBudgetRedemptions.mockReturnValue({
+ isLoading: false,
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 2,
+ results: [
+ {
+ uuid: 'test-uuid1',
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
+ {
+ uuid: 'test-uuid2',
+ contentKey: mockCourseKey,
+ contentQuantity: -29900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-11-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
+ ],
+ learnerStateCounts: [
+ { learnerState: 'waiting', count: 1 },
+ { learnerState: 'waiting', count: 1 },
+ ],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ renderWithRouter( );
+ const remindRowAction = screen.getByTitle('Toggle All Current Page Rows Selected');
+ expect(remindRowAction).toBeInTheDocument();
+ userEvent.click(remindRowAction);
+ const remindBulkActionButton = screen.getByText('Remind (2)');
+ expect(remindBulkActionButton).toBeInTheDocument();
+ userEvent.click(remindBulkActionButton);
+ const modalDialog = screen.getByRole('dialog');
+ expect(modalDialog).toBeInTheDocument();
+ const remindDialogButton = getButtonElement('Send reminders (2)');
+ userEvent.click(remindDialogButton);
+ expect(
+ EnterpriseAccessApiService.remindContentAssignments,
+ ).toHaveBeenCalled();
+ await waitFor(
+ () => expect(screen.getByText('Reminders sent (2)')).toBeInTheDocument(),
+ );
+ });
+
it('cancels a single assignment', async () => {
EnterpriseAccessApiService.cancelContentAssignments.mockResolvedValueOnce({ status: 200 });
useParams.mockReturnValue({
@@ -1333,4 +1428,60 @@ describe(' ', () => {
() => expect(screen.getByText('Assignment canceled')).toBeInTheDocument(),
);
});
+ it('reminds a single assignment', async () => {
+ EnterpriseAccessApiService.remindContentAssignments.mockResolvedValueOnce({ status: 200 });
+ useParams.mockReturnValue({
+ budgetId: mockSubsidyAccessPolicyUUID,
+ activeTabKey: 'activity',
+ });
+ useBudgetRedemptions.mockReturnValue({
+ isLoading: false,
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ data: mockAssignableSubsidyAccessPolicy,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: { count: 1 },
+ spentTransactions: { count: 0 },
+ },
+ });
+ useBudgetContentAssignments.mockReturnValue({
+ isLoading: false,
+ contentAssignments: {
+ count: 1,
+ results: [
+ {
+ uuid: 'test-uuid',
+ contentKey: mockCourseKey,
+ contentQuantity: -19900,
+ learnerState: 'waiting',
+ recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
+ ],
+ learnerStateCounts: [{ learnerState: 'waiting', count: 1 }],
+ numPages: 1,
+ currentPage: 1,
+ },
+ fetchContentAssignments: jest.fn(),
+ });
+ renderWithRouter( );
+ const remindIconButton = screen.getByTestId('remind-assignment-test-uuid');
+ expect(remindIconButton).toBeInTheDocument();
+ userEvent.click(remindIconButton);
+ const modalDialog = screen.getByRole('dialog');
+ expect(modalDialog).toBeInTheDocument();
+ const remindDialogButton = getButtonElement('Send reminder');
+ userEvent.click(remindDialogButton);
+ await waitFor(
+ () => expect(screen.getByText('Reminder sent')).toBeInTheDocument(),
+ );
+ });
});
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
index a5d07d93ae..96c7ed5fc2 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
@@ -204,4 +204,63 @@ describe(' ', () => {
expect(screen.queryByText(expectedToastMessage)).not.toBeInTheDocument();
});
});
+
+ it.each([
+ {
+ assignmentUUIDs: 1,
+ },
+ {
+ assignmentUUIDs: 2,
+ },
+ ])('should render Toast notification for successful assignment reminder (%s)', async ({
+ assignmentUUIDs,
+ }) => {
+ const ToastContextController = () => {
+ const {
+ successfulReminderToast: {
+ displayToastForAssignmentReminder,
+ closeToastForAssignmentReminder,
+ },
+ } = useContext(BudgetDetailPageContext);
+
+ const handleDisplayToast = () => {
+ displayToastForAssignmentReminder(assignmentUUIDs);
+ };
+
+ const handleCloseToast = () => {
+ closeToastForAssignmentReminder();
+ };
+
+ return (
+
+ Open Toast
+ Close Toast
+
+ );
+ };
+ render( );
+
+ const toastMessages = [];
+ if (assignmentUUIDs > 1) {
+ toastMessages.push(`Reminders sent (${assignmentUUIDs})`);
+ }
+ if (assignmentUUIDs === 1) {
+ toastMessages.push('Reminder sent');
+ }
+ const expectedToastMessage = toastMessages.join(' ');
+
+ // Open Toast notification
+ userEvent.click(getButtonElement('Open Toast'));
+
+ // Verify Toast notification is rendered
+ expect(screen.getByText(expectedToastMessage)).toBeInTheDocument();
+
+ // Close Toast notification
+ userEvent.click(getButtonElement('Close Toast'));
+
+ // Verify Toast notification is no longer rendered
+ await waitFor(() => {
+ expect(screen.queryByText(expectedToastMessage)).not.toBeInTheDocument();
+ });
+ });
});
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index cb7e0da9a5..8a38cb2b14 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -184,6 +184,17 @@ class EnterpriseAccessApiService {
return EnterpriseAccessApiService.apiClient().post(url, options);
}
+ /**
+ * Remind content assignments for a specific AssignmentConfiguration.
+ */
+ static remindContentAssignments(assignmentConfigurationUUID, assignmentUuids) {
+ const options = {
+ assignment_uuids: assignmentUuids,
+ };
+ const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/remind/`;
+ return EnterpriseAccessApiService.apiClient().post(url, options);
+ }
+
/**
* Retrieve a specific subsidy access policy.
* @param {string} subsidyAccessPolicyUUID The UUID of the subsidy access policy to retrieve.
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index 51fe177d49..f2007a56f3 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -200,4 +200,15 @@ describe('EnterpriseAccessApiService', () => {
options,
);
});
+
+ test('remindContentAssignments calls enterprise-access cancel POST API to remind learners', () => {
+ const options = {
+ assignment_uuids: mockAssignmentUUIDs,
+ };
+ EnterpriseAccessApiService.remindContentAssignments(mockAssignmentConfigurationUUID, mockAssignmentUUIDs);
+ expect(axios.post).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/remind/`,
+ options,
+ );
+ });
});
From 492c0bcb2d2005e644de4197c633ea1b2b881df4 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Thu, 7 Dec 2023 08:22:41 -0500
Subject: [PATCH 100/124] fix: disables refetch on window focus for
useEnterpriseBudgets useQuery hook call (#1125)
---
src/components/EnterpriseSubsidiesContext/data/hooks.js | 3 +++
src/components/ProductTours/data/hooks.js | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index d30b9d2e3f..58655e1e39 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -117,6 +117,9 @@ export const useEnterpriseBudgets = ({
enterpriseId,
enablePortalLearnerCreditManagementScreen,
}),
+ // Disables the ability to re-fetch on window focus due to re-fetching behavior causing
+ // ProductTours component to rerender despite localStorage flagging
+ refetchOnWindowFocus: false,
});
export const useCustomerAgreement = ({ enterpriseId }) => {
diff --git a/src/components/ProductTours/data/hooks.js b/src/components/ProductTours/data/hooks.js
index daff89f8b1..858ecfaf64 100644
--- a/src/components/ProductTours/data/hooks.js
+++ b/src/components/ProductTours/data/hooks.js
@@ -9,9 +9,9 @@ import { SubsidyRequestsContext } from '../../subsidy-requests';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
export const usePortalAppearanceTour = ({ enablePortalAppearance }) => {
- const dismissedLearnerCreditTourCookie = global.localStorage.getItem(PORTAL_APPEARANCE_TOUR_COOKIE_NAME);
+ const dismissedPortalAppearanceTour = global.localStorage.getItem(PORTAL_APPEARANCE_TOUR_COOKIE_NAME);
// Only show tour if feature is enabled, hide cookie is undefined or false or not in the settings page
- const showPortalAppearanceTour = enablePortalAppearance && !dismissedLearnerCreditTourCookie;
+ const showPortalAppearanceTour = enablePortalAppearance && !dismissedPortalAppearanceTour;
const [portalAppearanceTourEnabled, setPortalAppearanceTourEnabled] = useState(showPortalAppearanceTour);
return [portalAppearanceTourEnabled, setPortalAppearanceTourEnabled];
};
From d727e731cfc6f0651e0e6fa13632eeb6b65eb92b Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Thu, 7 Dec 2023 09:02:09 -0500
Subject: [PATCH 101/124] Revert "fix: disables refetch on window focus for
useEnterpriseBudgets useQuery hook call (#1125)" (#1127)
This reverts commit 492c0bcb2d2005e644de4197c633ea1b2b881df4.
---
src/components/EnterpriseSubsidiesContext/data/hooks.js | 3 ---
src/components/ProductTours/data/hooks.js | 4 ++--
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/components/EnterpriseSubsidiesContext/data/hooks.js b/src/components/EnterpriseSubsidiesContext/data/hooks.js
index 58655e1e39..d30b9d2e3f 100644
--- a/src/components/EnterpriseSubsidiesContext/data/hooks.js
+++ b/src/components/EnterpriseSubsidiesContext/data/hooks.js
@@ -117,9 +117,6 @@ export const useEnterpriseBudgets = ({
enterpriseId,
enablePortalLearnerCreditManagementScreen,
}),
- // Disables the ability to re-fetch on window focus due to re-fetching behavior causing
- // ProductTours component to rerender despite localStorage flagging
- refetchOnWindowFocus: false,
});
export const useCustomerAgreement = ({ enterpriseId }) => {
diff --git a/src/components/ProductTours/data/hooks.js b/src/components/ProductTours/data/hooks.js
index 858ecfaf64..daff89f8b1 100644
--- a/src/components/ProductTours/data/hooks.js
+++ b/src/components/ProductTours/data/hooks.js
@@ -9,9 +9,9 @@ import { SubsidyRequestsContext } from '../../subsidy-requests';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
export const usePortalAppearanceTour = ({ enablePortalAppearance }) => {
- const dismissedPortalAppearanceTour = global.localStorage.getItem(PORTAL_APPEARANCE_TOUR_COOKIE_NAME);
+ const dismissedLearnerCreditTourCookie = global.localStorage.getItem(PORTAL_APPEARANCE_TOUR_COOKIE_NAME);
// Only show tour if feature is enabled, hide cookie is undefined or false or not in the settings page
- const showPortalAppearanceTour = enablePortalAppearance && !dismissedPortalAppearanceTour;
+ const showPortalAppearanceTour = enablePortalAppearance && !dismissedLearnerCreditTourCookie;
const [portalAppearanceTourEnabled, setPortalAppearanceTourEnabled] = useState(showPortalAppearanceTour);
return [portalAppearanceTourEnabled, setPortalAppearanceTourEnabled];
};
From f9bfbefb6e36f09f8aec2df5c757c7dd9ecb98fc Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Tue, 5 Dec 2023 22:54:06 +0000
Subject: [PATCH 102/124] feat: adding sso orchestrator failure and timeout
handling and general formatting cleanup
---
src/components/forms/ValidatedFormRadio.tsx | 6 +-
.../SettingsSSOTab/NewExistingSSOConfigs.jsx | 35 ++++++++
.../SettingsSSOTab/NewSSOConfigAlerts.jsx | 84 +++++++++++++++++--
.../SettingsSSOTab/NewSSOConfigCard.jsx | 18 ++--
.../settings/SettingsSSOTab/index.jsx | 6 +-
.../steps/NewSSOConfigConfigureStep.tsx | 2 +-
.../steps/NewSSOConfigConfirmStep.tsx | 2 +-
.../steps/NewSSOConfigConnectStep.tsx | 30 +++----
.../tests/NewExistingSSOConfigs.test.jsx | 68 +++++++++++++++
.../tests/NewSSOConfigAlerts.test.jsx | 18 ++++
.../settings/SettingsSSOTab/utils.js | 6 +-
src/components/settings/settings.scss | 4 +
12 files changed, 243 insertions(+), 36 deletions(-)
diff --git a/src/components/forms/ValidatedFormRadio.tsx b/src/components/forms/ValidatedFormRadio.tsx
index bfc29fb2d2..4f4a6f406f 100644
--- a/src/components/forms/ValidatedFormRadio.tsx
+++ b/src/components/forms/ValidatedFormRadio.tsx
@@ -2,7 +2,7 @@ import React, { ReactElement } from 'react';
import omit from 'lodash/omit';
import isString from 'lodash/isString';
-import { Form } from '@edx/paragon';
+import { Form, Stack } from '@edx/paragon';
import { setFormFieldAction } from './data/actions';
import { useFormContext } from './FormContext';
@@ -65,7 +65,9 @@ const ValidatedFormRadio = (props: ValidatedFormRadioProps) => {
isInline={formRadioProps.isInline}
value={value}
>
- {createOptions(formRadioProps.options)}
+
+ {createOptions(formRadioProps.options)}
+
{formRadioProps.fieldInstructions && (
{formRadioProps.fieldInstructions}
diff --git a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
index bdfc1b910f..792e6913f7 100644
--- a/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
+++ b/src/components/settings/SettingsSSOTab/NewExistingSSOConfigs.jsx
@@ -26,6 +26,8 @@ const NewExistingSSOConfigs = ({
const [inProgressConfigs, setInProgressConfigs] = useState([]);
const [untestedConfigs, setUntestedConfigs] = useState([]);
const [liveConfigs, setLiveConfigs] = useState([]);
+ const [erroredConfigs, setErroredConfigs] = useState([]);
+ const [timedOutConfigs, setTimedOutConfigs] = useState([]);
const [notConfiguredConfigs, setNotConfiguredConfigs] = useState([]);
const [queryForTestedConfigs, setQueryForTestedConfigs] = useState(false);
const [queryForConfiguredConfigs, setQueryForConfiguredConfigs] = useState(false);
@@ -88,6 +90,18 @@ const NewExistingSSOConfigs = ({
return null;
};
+ function checkConfiguring(config) {
+ return !config.configured_at || config.submitted_at > config.configured_at;
+ }
+
+ function checkErrored(config) {
+ return config.errored_at && (config.submitted_at < config.errored_at);
+ }
+
+ function checkTimedOut(config) {
+ return config.submitted_at && checkConfiguring(config) && !config.is_pending_configuration && !checkErrored(config);
+ }
+
useEffect(() => {
const [active, inactive] = _.partition(configs, config => config.active);
const inProgress = configs.filter(isInProgressConfig);
@@ -97,6 +111,15 @@ const NewExistingSSOConfigs = ({
);
const notConfigured = configs.filter(config => !config.configured_at);
+ const handleCheckTimedOut = (config) => (
+ config.submitted_at && checkConfiguring(config) && !config.is_pending_configuration && !checkErrored(config)
+ );
+
+ const timedOut = configs.filter(handleCheckTimedOut);
+ const errored = configs.filter(checkErrored);
+ setTimedOutConfigs(timedOut);
+ setErroredConfigs(errored);
+
if (live.length >= 1) {
setLiveConfigs(live);
openAlerts();
@@ -137,6 +160,15 @@ const NewExistingSSOConfigs = ({
config => (config.submitted_at && !config.configured_at) || (config.configured_at < config.submitted_at),
);
const untested = res.data.filter(config => !config.validated_at || config.validated_at < config.configured_at);
+ const timedOut = res.data.filter(checkTimedOut);
+ const errored = res.data.filter(checkErrored);
+ if (timedOut.length >= 1) {
+ setTimedOutConfigs(timedOut);
+ }
+
+ if (errored.length >= 1) {
+ setErroredConfigs(errored);
+ }
if (queryForConfiguredConfigs) {
if (inProgress.length === 0) {
@@ -174,6 +206,9 @@ const NewExistingSSOConfigs = ({
untestedConfigs={untestedConfigs}
notConfigured={notConfiguredConfigs}
closeAlerts={closeAlerts}
+ timedOutConfigs={timedOutConfigs}
+ erroredConfigs={erroredConfigs}
+ setIsStepperOpen={setIsStepperOpen}
/>
)}
{renderCards('Active', activeConfigs)}
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
index 746c228bbb..0a625449dd 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigAlerts.jsx
@@ -1,11 +1,12 @@
-import React from 'react';
+import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
- CheckCircle, Warning,
+ CheckCircle, Info, Warning,
} from '@edx/paragon/icons';
-import { Alert } from '@edx/paragon';
+import { Alert, Button } from '@edx/paragon';
import Cookies from 'universal-cookie';
+import { SSOConfigContext } from './SSOConfigContext';
export const SSO_SETUP_COMPLETION_COOKIE_NAME = 'dismissed-sso-completion-alert';
const SSO_ALERT_OVERRIDE_PARAM = 'sso_alert_override';
@@ -19,7 +20,19 @@ const NewSSOConfigAlerts = ({
contactEmail,
closeAlerts,
enterpriseSlug,
+ timedOutConfigs,
+ erroredConfigs,
+ setIsStepperOpen,
}) => {
+ const { setProviderConfig } = useContext(SSOConfigContext);
+
+ const configureOnClick = (config) => {
+ setProviderConfig(config);
+ setIsStepperOpen(true);
+ };
+ const onTimedOutConfigureClick = () => configureOnClick(timedOutConfigs[0]);
+ const onErroredConfigClick = () => configureOnClick(erroredConfigs[0]);
+
const dismissSetupCompleteAlert = () => {
ssoCookies.set(
SSO_SETUP_COMPLETION_COOKIE_NAME,
@@ -32,13 +45,63 @@ const NewSSOConfigAlerts = ({
const searchParams = new URLSearchParams(window.location.search);
const dismissedSSOSetupCompletionCookie = ssoCookies.get(SSO_SETUP_COMPLETION_COOKIE_NAME) === 'true';
const hideSSOLiveAlert = dismissedSSOSetupCompletionCookie && !searchParams.get(SSO_ALERT_OVERRIDE_PARAM);
+
+ const [showTimeoutAlert, setShowTimeoutAlert] = useState(true);
+ const [showErrorAlert, setShowErrorAlert] = useState(true);
+
+ if (timedOutConfigs.length >= 1) {
+ return (
+ Configure,
+ ]}
+ dismissible
+ show={showTimeoutAlert}
+ onClose={() => { setShowTimeoutAlert(false); }}
+ stacked
+ >
+ SSO Configuration timed out
+
+ Your SSO configuration failed due to an internal error. Please try again by selecting “Configure” below and
+ {' '}verifying your integration details. Then reconfigure, reauthorize, and test your connection.
+
+
+ );
+ }
+
+ if (erroredConfigs.length >= 1) {
+ return (
+ Configure,
+ ]}
+ dismissible
+ onClose={() => { setShowErrorAlert(false); }}
+ stacked
+ >
+ SSO Configuration failed
+
+ Please verify integration details have been entered correctly. Select “Configure” below and verify your
+ {' '}integration details. Then reconfigure, reauthorize, and test your connection.
+
+
+ );
+ }
+
return (
<>
{inProgressConfigs.length >= 1 && (
@@ -53,21 +116,21 @@ const NewSSOConfigAlerts = ({
You need to test your SSO connection
- Your SSO configuration has completed,
+ Your SSO configuration has been completed,
and you should have received an email with the following instructions:
- 1. Copy the URL for your learner Portal dashboard below:
+ 1: Copy the URL for your Learner Portal dashboard below:
http://courses.edx.org/dashboard?tpa_hint={enterpriseSlug}
2: Launch a new incognito or private window and paste the copied URL into the URL bar to load your
- learner Portal dashboard.
+ Learner Portal dashboard.
3: When prompted, enter login credentials supported by your IDP to test your connection to edX.
@@ -82,7 +145,7 @@ const NewSSOConfigAlerts = ({
!hideSSOLiveAlert) && (
({
diff --git a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
index bcdacdaa2c..47a5141c1d 100644
--- a/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
+++ b/src/components/settings/SettingsSSOTab/NewSSOConfigCard.jsx
@@ -19,8 +19,14 @@ const NewSSOConfigCard = ({
}) => {
const VALIDATED = config.validated_at;
const ENABLED = config.active;
- const CONFIGURED = config.configured_at && (config.submitted_at < config.configured_at);
- const SUBMITTED = config.submitted_at;
+ const CONFIGURED = config.configured_at && (config.submitted_at < config.configured_at) && (
+ !config.errored_at || (config.errored_at && config.configured_at > config.errored_at)
+ );
+ const SUBMITTED = config.submitted_at && (
+ !config.errored_at || (config.errored_at && config.submitted_at > config.errored_at)
+ );
+ const ERRORED = config.errored_at;
+ const TIMED_OUT = SUBMITTED && !CONFIGURED && !config.is_pending_configuration;
const { setProviderConfig } = useContext(SSOConfigContext);
@@ -130,7 +136,7 @@ const NewSSOConfigCard = ({
const renderCardButton = () => (
<>
- {!VALIDATED && CONFIGURED && (
+ {((!VALIDATED && CONFIGURED) || ((TIMED_OUT) || (ERRORED))) && (
onConfigureClick(config)}
@@ -172,7 +178,7 @@ const NewSSOConfigCard = ({
Last modified {convertToReadableDate(config.modified)}
)}
- actions={(!SUBMITTED || CONFIGURED) && (
+ actions={((!SUBMITTED || CONFIGURED) || (ERRORED || TIMED_OUT)) && (
)}
- {(!ENABLED || !VALIDATED) && (
+ {((!ENABLED || !VALIDATED) || (ERRORED || TIMED_OUT)) && (
onDeleteClick(config)}
@@ -225,6 +231,8 @@ NewSSOConfigCard.propTypes = {
validated_at: PropTypes.string,
configured_at: PropTypes.string,
submitted_at: PropTypes.string,
+ errored_at: PropTypes.string,
+ is_pending_configuration: PropTypes.bool,
}).isRequired,
setLoading: PropTypes.func.isRequired,
setRefreshBool: PropTypes.func.isRequired,
diff --git a/src/components/settings/SettingsSSOTab/index.jsx b/src/components/settings/SettingsSSOTab/index.jsx
index e98c94840b..e9b0bf5825 100644
--- a/src/components/settings/SettingsSSOTab/index.jsx
+++ b/src/components/settings/SettingsSSOTab/index.jsx
@@ -14,7 +14,7 @@ import NewSSOConfigForm from './NewSSOConfigForm';
import { SSOConfigContext, SSOConfigContextProvider } from './SSOConfigContext';
import LmsApiService from '../../../data/services/LmsApiService';
import { features } from '../../../config';
-import { isInProgressConfig } from './utils';
+import { isInProgressConfig, checkErroredOrTimedOutConfig } from './utils';
const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
const {
@@ -59,7 +59,9 @@ const SettingsSSOTab = ({ enterpriseId, setHasSSOConfig }) => {
if (AUTH0_SELF_SERVICE_INTEGRATION) {
const newButtonVisible = existingConfigs?.length > 0 && (providerConfig === null);
- const newButtonDisabled = existingConfigs.some(isInProgressConfig);
+ const newButtonDisabled = existingConfigs.some(isInProgressConfig) && (
+ !existingConfigs.some(checkErroredOrTimedOutConfig)
+ );
return (
{
formId="displayName"
type="text"
floatingLabel="Display Name (Optional)"
- fieldInstructions="Create a custom display name for this SSO integration"
+ fieldInstructions="Create a custom display name for this SSO integration."
/>
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx
index f43bdeeb71..0ba54ea78f 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfirmStep.tsx
@@ -44,7 +44,7 @@ const SSOConfigConfirmStep = () => (
Select the "Finish" button below or close this form via the
- "X" in the upper right corner while you wait for your
+ {' '}"X" in the upper right corner while you wait for your
configuration email. Your SSO testing status will display on the following SSO settings screen.
>
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
index ac33ed1c18..bd0139db9a 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-import { Container, Dropzone, Form } from '@edx/paragon';
+import { Container, Dropzone, Form, Stack } from '@edx/paragon';
import ValidatedFormRadio from '../../../forms/ValidatedFormRadio';
import ValidatedFormControl from '../../../forms/ValidatedFormControl';
@@ -73,9 +73,9 @@ const SSOConfigConnectStep = () => {
+ Let's get started
+ What is your organization's SSO Identity Provider?
+
{
/>
- Connect edX to your Identity Provider
- Select a method to connect edX to your Identity Provider
-
+ Connect edX to your Identity Provider
+ Select a method to connect edX to your Identity Provider
+
{
{showUrlEntry && (
-
-
-
+
+
+
)}
{showXmlUpload
diff --git a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
index 6e0fcece64..bfb2c8fc9a 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewExistingSSOConfigs.test.jsx
@@ -40,6 +40,8 @@ const inactiveConfig = [
configured_at: '2022-05-12T19:51:25Z',
validated_at: '2022-06-12T19:51:25Z',
submitted_at: '2022-04-12T19:51:25Z',
+ is_pending_configuration: false,
+ errored_at: null,
},
];
const activeConfig = [
@@ -51,6 +53,8 @@ const activeConfig = [
configured_at: '2022-05-12T19:51:25Z',
validated_at: '2022-06-12T19:51:25Z',
submitted_at: '2022-04-12T19:51:25Z',
+ is_pending_configuration: false,
+ errored_at: null,
},
];
const unvalidatedConfig = [
@@ -62,6 +66,8 @@ const unvalidatedConfig = [
configured_at: '2022-04-12T19:51:25Z',
validated_at: null,
submitted_at: '2022-04-12T19:51:25Z',
+ is_pending_configuration: false,
+ errored_at: null,
},
];
const inProgressConfig = [
@@ -73,6 +79,8 @@ const inProgressConfig = [
configured_at: '2021-04-12T19:51:25Z',
validated_at: null,
submitted_at: '2022-04-12T19:51:25Z',
+ is_pending_configuration: true,
+ errored_at: null,
},
];
const notConfiguredConfig = [
@@ -84,6 +92,32 @@ const notConfiguredConfig = [
configured_at: null,
validated_at: null,
submitted_at: '2022-04-12T19:51:25Z',
+ is_pending_configuration: true,
+ errored_at: null,
+ },
+];
+const timedOutConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ active: false,
+ modified: '2022-04-12T19:51:25Z',
+ configured_at: null,
+ validated_at: null,
+ submitted_at: '2022-04-12T19:51:25Z',
+ is_pending_configuration: false,
+ errored_at: null,
+ },
+];
+const erroredConfig = [
+ {
+ uuid: 'ecc16800-c1cc-4cdb-93aa-186f71b026ca',
+ active: false,
+ modified: null,
+ configured_at: null,
+ validated_at: null,
+ submitted_at: '2022-04-10T19:51:25Z',
+ is_pending_configuration: false,
+ errored_at: '2022-04-12T19:51:25Z',
},
];
@@ -331,4 +365,38 @@ describe('New Existing SSO Configs tests', () => {
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
expect(mockSetPollingNetworkError).toHaveBeenCalledTimes(1);
});
+ test('detects timed out configs', async () => {
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockImplementation(() => Promise.resolve({
+ data: timedOutConfig,
+ }));
+ setupNewExistingSSOConfigs(timedOutConfig);
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'SSO Configuration timed out',
+ ),
+ ).toBeInTheDocument());
+ const button = screen.getByTestId('sso-timeout-alert-configure');
+ act(() => {
+ userEvent.click(button);
+ });
+ expect(mockSetProviderConfig).toHaveBeenCalledTimes(1);
+ });
+ test('detects errored configs', async () => {
+ const spy = jest.spyOn(LmsApiService, 'listEnterpriseSsoOrchestrationRecords');
+ spy.mockImplementation(() => Promise.resolve({
+ data: erroredConfig,
+ }));
+ setupNewExistingSSOConfigs(erroredConfig);
+ await waitFor(() => expect(
+ screen.queryByText(
+ 'SSO Configuration failed',
+ ),
+ ).toBeInTheDocument());
+ const button = screen.getByTestId('sso-errored-alert-configure');
+ act(() => {
+ userEvent.click(button);
+ });
+ expect(mockSetProviderConfig).toHaveBeenCalledTimes(1);
+ });
});
diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
index 1ca8978463..150114538f 100644
--- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
+++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigAlerts.test.jsx
@@ -58,6 +58,9 @@ describe('New SSO Config Alerts Tests', () => {
untestedConfigs={[{ display_name: 'untested' }]}
notConfigured={[]}
closeAlerts={jest.fn()}
+ timedOutConfigs={[]}
+ erroredConfigs={[]}
+ setIsStepperOpen={jest.fn()}
/>,
@@ -90,6 +93,9 @@ describe('New SSO Config Alerts Tests', () => {
untestedConfigs={[]}
notConfigured={[{ display_name: 'not configured' }]}
closeAlerts={jest.fn()}
+ timedOutConfigs={[]}
+ erroredConfigs={[]}
+ setIsStepperOpen={jest.fn()}
/>,
@@ -113,6 +119,9 @@ describe('New SSO Config Alerts Tests', () => {
untestedConfigs={[{ display_name: 'untested' }]}
notConfigured={[]}
closeAlerts={jest.fn()}
+ timedOutConfigs={[]}
+ erroredConfigs={[]}
+ setIsStepperOpen={jest.fn()}
/>,
@@ -150,6 +159,9 @@ describe('New SSO Config Alerts Tests', () => {
untestedConfigs={[]}
notConfigured={[]}
closeAlerts={jest.fn()}
+ timedOutConfigs={[]}
+ erroredConfigs={[]}
+ setIsStepperOpen={jest.fn()}
/>,
@@ -173,6 +185,9 @@ describe('New SSO Config Alerts Tests', () => {
untestedConfigs={[{ display_name: 'untested' }]}
notConfigured={[]}
closeAlerts={mockCloseAlerts}
+ timedOutConfigs={[]}
+ erroredConfigs={[]}
+ setIsStepperOpen={jest.fn()}
/>,
@@ -199,6 +214,9 @@ describe('New SSO Config Alerts Tests', () => {
untestedConfigs={[]}
notConfigured={[]}
closeAlerts={jest.fn()}
+ timedOutConfigs={[]}
+ erroredConfigs={[]}
+ setIsStepperOpen={jest.fn()}
/>,
diff --git a/src/components/settings/SettingsSSOTab/utils.js b/src/components/settings/SettingsSSOTab/utils.js
index e9fd44a082..b5d0d8f7d5 100644
--- a/src/components/settings/SettingsSSOTab/utils.js
+++ b/src/components/settings/SettingsSSOTab/utils.js
@@ -31,6 +31,10 @@ function isInProgressConfig(config) {
|| config.configured_at < config.submitted_at;
}
+function checkErroredOrTimedOutConfig(config) {
+ return config.errored_at || (config.submitted_at && !config.configured_at && !config.is_pending_configuration);
+}
+
export {
- updateSamlProviderData, deleteSamlProviderData, createSAMLURLs, isInProgressConfig,
+ updateSamlProviderData, deleteSamlProviderData, createSAMLURLs, isInProgressConfig, checkErroredOrTimedOutConfig,
};
diff --git a/src/components/settings/settings.scss b/src/components/settings/settings.scss
index 99079b1b41..9730540a88 100644
--- a/src/components/settings/settings.scss
+++ b/src/components/settings/settings.scss
@@ -14,6 +14,10 @@
max-height: 71px;
}
+.sso-alert-width {
+ width: 73% !important;
+}
+
.lms-card-hover {
&:hover {
box-shadow: $box-shadow;
From b42132d21328968b6213886cbe85397b02ed7f35 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Thu, 7 Dec 2023 15:38:23 -0500
Subject: [PATCH 103/124] fix: modifies product tours state to fix re-rendering
error (#1128)
* fix: modifies product tours state to fix re-rendering error
* chore: useMemo to address linting errors
* chore: fix more linting errors
* fix: return stale time to original value
* fix: remove unnecessary useState
* fix: remove unnecessary useState
* fix: consolidate logic of Product around removing the useState
---
.../EnterpriseSubsidiesContext/index.jsx | 1 +
src/components/ProductTours/ProductTours.jsx | 8 ++++----
src/components/ProductTours/data/hooks.js | 20 +++++++------------
3 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/src/components/EnterpriseSubsidiesContext/index.jsx b/src/components/EnterpriseSubsidiesContext/index.jsx
index 116d494c44..82d3f9612b 100644
--- a/src/components/EnterpriseSubsidiesContext/index.jsx
+++ b/src/components/EnterpriseSubsidiesContext/index.jsx
@@ -18,6 +18,7 @@ export const useEnterpriseSubsidiesContext = ({
enterpriseId,
isTopDownAssignmentEnabled,
});
+
const {
budgets = [],
canManageLearnerCredit = false,
diff --git a/src/components/ProductTours/ProductTours.jsx b/src/components/ProductTours/ProductTours.jsx
index af76826796..901770cf2c 100644
--- a/src/components/ProductTours/ProductTours.jsx
+++ b/src/components/ProductTours/ProductTours.jsx
@@ -32,10 +32,10 @@ const ProductTours = ({
const enablePortalAppearance = features.SETTINGS_PAGE_APPEARANCE_TAB;
const history = useHistory();
const enabledFeatures = {
- [PORTAL_APPEARANCE_TOUR_COOKIE_NAME]: usePortalAppearanceTour({ enablePortalAppearance })[0],
- [BROWSE_AND_REQUEST_TOUR_COOKIE_NAME]: useBrowseAndRequestTour({ enableLearnerPortal })[0],
- [LEARNER_CREDIT_COOKIE_NAME]: useLearnerCreditTour()[0],
- [HIGHLIGHTS_COOKIE_NAME]: useHighlightsTour(FEATURE_CONTENT_HIGHLIGHTS)[0],
+ [PORTAL_APPEARANCE_TOUR_COOKIE_NAME]: usePortalAppearanceTour({ enablePortalAppearance }),
+ [BROWSE_AND_REQUEST_TOUR_COOKIE_NAME]: useBrowseAndRequestTour({ enableLearnerPortal }),
+ [LEARNER_CREDIT_COOKIE_NAME]: useLearnerCreditTour(),
+ [HIGHLIGHTS_COOKIE_NAME]: useHighlightsTour(FEATURE_CONTENT_HIGHLIGHTS),
};
const newFeatureTourCheckpoints = {
[PORTAL_APPEARANCE_TOUR_COOKIE_NAME]: portalAppearanceTour({
diff --git a/src/components/ProductTours/data/hooks.js b/src/components/ProductTours/data/hooks.js
index daff89f8b1..0c01b946a6 100644
--- a/src/components/ProductTours/data/hooks.js
+++ b/src/components/ProductTours/data/hooks.js
@@ -1,4 +1,4 @@
-import { useContext, useState } from 'react';
+import { useContext } from 'react';
import {
BROWSE_AND_REQUEST_TOUR_COOKIE_NAME,
PORTAL_APPEARANCE_TOUR_COOKIE_NAME,
@@ -9,11 +9,10 @@ import { SubsidyRequestsContext } from '../../subsidy-requests';
import { EnterpriseSubsidiesContext } from '../../EnterpriseSubsidiesContext';
export const usePortalAppearanceTour = ({ enablePortalAppearance }) => {
- const dismissedLearnerCreditTourCookie = global.localStorage.getItem(PORTAL_APPEARANCE_TOUR_COOKIE_NAME);
+ const dismissedPortalAppearanceTourCookie = global.localStorage.getItem(PORTAL_APPEARANCE_TOUR_COOKIE_NAME);
// Only show tour if feature is enabled, hide cookie is undefined or false or not in the settings page
- const showPortalAppearanceTour = enablePortalAppearance && !dismissedLearnerCreditTourCookie;
- const [portalAppearanceTourEnabled, setPortalAppearanceTourEnabled] = useState(showPortalAppearanceTour);
- return [portalAppearanceTourEnabled, setPortalAppearanceTourEnabled];
+ const showPortalAppearanceTour = enablePortalAppearance && !dismissedPortalAppearanceTourCookie;
+ return showPortalAppearanceTour;
};
export const useBrowseAndRequestTour = ({
@@ -25,9 +24,7 @@ export const useBrowseAndRequestTour = ({
// not in settings page, and subsidy requests are not already enabled
const showBrowseAndRequestTour = enableLearnerPortal && enterpriseSubsidyTypesForRequests.length > 0
&& !dismissedBrowseAndRequestTourCookie && !subsidyRequestConfiguration?.subsidyRequestsEnabled;
-
- const [browseAndRequestTourEnabled, setBrowseAndRequestTourEnabled] = useState(showBrowseAndRequestTour);
- return [browseAndRequestTourEnabled, setBrowseAndRequestTourEnabled];
+ return showBrowseAndRequestTour;
};
export const useLearnerCreditTour = () => {
@@ -36,15 +33,12 @@ export const useLearnerCreditTour = () => {
// Only show tour if feature is enabled, the enterprise is eligible for the feature,
// hide cookie is undefined or false, not in learner credit page
const showLearnerCreditTour = canManageLearnerCredit && !dismissedLearnerCreditTourCookie;
-
- const [learnerCreditTourEnabled, setBrowseAndRequestTourEnabled] = useState(showLearnerCreditTour);
- return [learnerCreditTourEnabled, setBrowseAndRequestTourEnabled];
+ return showLearnerCreditTour;
};
export const useHighlightsTour = (enableHighlights) => {
const dismissedHighlightsTourCookie = global.localStorage.getItem(HIGHLIGHTS_COOKIE_NAME);
// Only show tour if feature is enabled, hide cookie is undefined or false or not in the settings page
const showHighlightsTour = enableHighlights && !dismissedHighlightsTourCookie;
- const [highlightsTourEnabled, setHighlightsTourEnabled] = useState(showHighlightsTour);
- return [highlightsTourEnabled, setHighlightsTourEnabled];
+ return showHighlightsTour;
};
From c5d44f099d1bc762b058a026166d9c8e3e89b702 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Fri, 8 Dec 2023 11:37:42 -0500
Subject: [PATCH 104/124] fix: adds sane fallback to if learnerState is failed
and no error reason exist (#1129)
* fix: adds sane fallback to if learnerState is failed and no error reason exist
* fix: update comments
* chore: PR fixes
* chore: testing
---
.../AssignmentStatusTableCell.jsx | 8 ++++++--
.../tests/BudgetDetailPage.test.jsx | 9 +++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
index 10cb79caf9..af24ed54fb 100644
--- a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -8,6 +8,7 @@ import FailedReminder from './assignments-status-chips/FailedReminder';
import FailedSystem from './assignments-status-chips/FailedSystem';
import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
+import { capitalizeFirstLetter } from '../../utils';
const AssignmentStatusTableCell = ({ row }) => {
const { original } = row;
@@ -16,7 +17,6 @@ const AssignmentStatusTableCell = ({ row }) => {
learnerState,
errorReason,
} = original;
-
// Learner state is not available for this assignment, so don't display anything.
if (!learnerState) {
return null;
@@ -36,6 +36,10 @@ const AssignmentStatusTableCell = ({ row }) => {
}
if (learnerState === 'failed') {
+ // If learnerState is failed but no error reason is defined, return a failed system chip.
+ if (!errorReason) {
+ return ;
+ }
// Determine which failure chip to display based on the error reason.
if (errorReason.actionType === 'notified') {
if (errorReason.errorReason === 'email_error') {
@@ -54,7 +58,7 @@ const AssignmentStatusTableCell = ({ row }) => {
}
// Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
- return {`${learnerState.charAt(0).toUpperCase()}${learnerState.substr(1)}`} ;
+ return {`${capitalizeFirstLetter(learnerState)}`} ;
};
AssignmentStatusTableCell.propTypes = {
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index aee974de7c..b477212f03 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -846,6 +846,15 @@ describe(' ', () => {
actionType: 'notified',
},
},
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: System',
+ expectedModalPopupHeading: 'Failed: System',
+ expectedModalPopupContent: 'Something went wrong behind the scenes.',
+ actions: [mockFailedLinkedLearnerAction],
+ errorReason: null,
+ },
{
learnerState: 'failed',
hasLearnerEmail: true,
From 383319b3928aad4e32421b2267abef4328bd7041 Mon Sep 17 00:00:00 2001
From: Troy Sankey
Date: Wed, 6 Dec 2023 17:30:34 -0800
Subject: [PATCH 105/124] feat: use configurable URL in cards/chips within the
learner-credit-management tool
ENT-7907
---
.env.development | 3 +++
.../learner-credit-management/BudgetDetailAssignments.jsx | 7 ++++++-
.../learner-credit-management/BudgetDetailRedemptions.jsx | 7 ++++++-
.../assignments-status-chips/FailedBadEmail.jsx | 3 ++-
.../assignments-status-chips/FailedCancellation.jsx | 4 +++-
.../assignments-status-chips/FailedSystem.jsx | 3 ++-
.../assignments-status-chips/WaitingForLearner.jsx | 3 ++-
.../cards/NewAssignmentModalButton.jsx | 5 +++--
src/index.jsx | 1 +
9 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/.env.development b/.env.development
index 92e64b6562..c63505b9aa 100644
--- a/.env.development
+++ b/.env.development
@@ -1,3 +1,4 @@
+APP_ID='admin-portal'
NODE_ENV='development'
BASE_URL='http://localhost:1991'
LMS_BASE_URL='http://localhost:18000'
@@ -17,6 +18,7 @@ ENTERPRISE_LEARNER_PORTAL_URL='http://localhost:8734'
ENTERPRISE_SUPPORT_URL='https://edx.org'
ENTERPRISE_SUPPORT_REVOKE_LICENSE_URL='https://edx.org'
ENTERPRISE_SUPPORT_PROGRAM_OPTIMIZATION_URL='https://edx.org'
+ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL='https://edx.org'
SEGMENT_KEY=''
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
USER_INFO_COOKIE_NAME='edx-user-info'
@@ -53,3 +55,4 @@ USE_API_CACHE='true'
SUBSCRIPTION_LPR='true'
PLOTLY_SERVER_URL='http://localhost:8050'
AUTH0_SELF_SERVICE_INTEGRATION='true'
+MFE_CONFIG_API_URL='http://localhost:18000/api/mfe_config/v1'
diff --git a/src/components/learner-credit-management/BudgetDetailAssignments.jsx b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
index b7caecce6b..41bcbe1275 100644
--- a/src/components/learner-credit-management/BudgetDetailAssignments.jsx
+++ b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
@@ -1,6 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
+import { Hyperlink } from '@edx/paragon';
+import { getConfig } from '@edx/frontend-platform/config';
import BudgetAssignmentsTable from './BudgetAssignmentsTable';
import AssignMoreCoursesEmptyStateMinimal from './AssignMoreCoursesEmptyStateMinimal';
@@ -42,7 +44,10 @@ const BudgetDetailAssignments = ({
Assigned
Assigned activity earmarks funds in your budget so you can't overspend. For funds to move
- from assigned to spent, your learners must complete enrollment.
+ from assigned to spent, your learners must complete enrollment.{' '}
+
+ Learn more
+
{
Spent
- Spent activity is driven by completed enrollments.
+ Spent activity is driven by completed enrollments.{' '}
+
+ Learn more
+
{(enterpriseOfferId || (subsidyAccessPolicyId && !enterpriseFeatures.topDownAssignmentRealTimeLcm)) && (
<>
Enrollment data is automatically updated every 12 hours.
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
index 22228de922..b6664a8768 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Chip, Hyperlink, useToggle } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
@@ -40,7 +41,7 @@ const FailedBadEmail = ({ learnerEmail }) => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
.
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
index 182e91df7b..a46a4f69d5 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
@@ -1,6 +1,8 @@
import React, { useState } from 'react';
import { Chip, useToggle, Hyperlink } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
+
import BaseModalPopup from './BaseModalPopup';
const FailedCancellation = () => {
@@ -41,7 +43,7 @@ const FailedCancellation = () => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
index cfee2cb7bb..43ae45e4b0 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Chip, Hyperlink, useToggle } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
@@ -37,7 +38,7 @@ const FailedSystem = () => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
.
diff --git a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
index 50a06dc522..f1176b3317 100644
--- a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Chip, Hyperlink, useToggle } from '@edx/paragon';
import { Timelapse } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
import { ASSIGNMENT_ENROLLMENT_DEADLINE } from '../data';
@@ -39,7 +40,7 @@ const WaitingForLearner = ({ learnerEmail }) => {
Need help?
Learn more about learner enrollment in assigned courses at{' '}
-
+
Help Center: Course Assignments
.
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 47a3899735..eb2a089129 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -12,8 +12,9 @@ import {
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { camelCaseObject, snakeCaseObject } from '@edx/frontend-platform/utils';
-
import { connect } from 'react-redux';
+import { getConfig } from '@edx/frontend-platform/config';
+
import AssignmentModalContent from './AssignmentModalContent';
import EnterpriseAccessApiService from '../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys, useBudgetId, useSubsidyAccessPolicy } from '../data';
@@ -196,7 +197,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
enterpriseId,
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_HELP_CENTER,
)}
- destination="https://edx.org"
+ destination={getConfig().ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL}
showLaunchIcon
target="_blank"
>
diff --git a/src/index.jsx b/src/index.jsx
index 39bebbc913..dc5c5c07f2 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -39,6 +39,7 @@ initialize({
FEATURE_LEARNER_CREDIT_MANAGEMENT: process.env.FEATURE_LEARNER_CREDIT_MANAGEMENT || hasFeatureFlagEnabled('LEARNER_CREDIT_MANAGEMENT') || null,
FEATURE_CONTENT_HIGHLIGHTS: process.env.FEATURE_CONTENT_HIGHLIGHTS || hasFeatureFlagEnabled('CONTENT_HIGHLIGHTS') || null,
ENTERPRISE_SUPPORT_PROGRAM_OPTIMIZATION_URL: process.env.ENTERPRISE_SUPPORT_PROGRAM_OPTIMIZATION_URL || null,
+ ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL: process.env.ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL || null,
});
},
},
From 7b3ea4b0b6b6f4dbc93d4e6a9859df03b8136aea Mon Sep 17 00:00:00 2001
From: Cyril Nxumalo <80963114+zwidekalanga@users.noreply.github.com>
Date: Fri, 8 Dec 2023 21:25:04 +0200
Subject: [PATCH 106/124] feat: [Budget Detail Header] Display aggregates and
budget overview (#1110)
Co-authored-by: Adam Stankiewicz
---
.../learner-credit-management/BudgetCard.jsx | 2 +-
.../BudgetDetailAssignments.jsx | 19 +-
.../BudgetDetailCatalogTabContents.jsx | 20 +-
.../BudgetDetailPageBreadcrumbs.jsx | 31 +++
.../BudgetDetailPageHeader.jsx | 124 ++++++++---
.../BudgetDetailPageOverviewAvailability.jsx | 120 +++++++++++
.../BudgetDetailPageOverviewUtilization.jsx | 127 ++++++++++++
.../BudgetDetailRedemptions.jsx | 19 +-
.../data/constants.js | 2 +
.../data/hooks/index.js | 2 +
.../hooks/tests/useEnterpriseOffer.test.jsx | 100 +++++++++
.../useSubsidySummaryAnalyticsApi.test.js | 3 +-
.../data/hooks/useBudgetDetailHeaderData.js | 73 +++++++
.../data/hooks/useEnterpriseOffer.js | 20 ++
.../hooks/useSubsidySummaryAnalyticsApi.js | 8 +-
.../data/tests/constants.js | 97 +++++++++
.../search/CatalogSearch.jsx | 2 +-
.../styles/index.scss | 5 +-
.../tests/BudgetDetailPage.test.jsx | 196 +++++++++++++++++-
src/data/services/EcommerceApiService.js | 10 +
20 files changed, 933 insertions(+), 47 deletions(-)
create mode 100644 src/components/learner-credit-management/BudgetDetailPageBreadcrumbs.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
create mode 100644 src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/tests/useEnterpriseOffer.test.jsx
create mode 100644 src/components/learner-credit-management/data/hooks/useBudgetDetailHeaderData.js
create mode 100644 src/components/learner-credit-management/data/hooks/useEnterpriseOffer.js
diff --git a/src/components/learner-credit-management/BudgetCard.jsx b/src/components/learner-credit-management/BudgetCard.jsx
index 19d70f79a1..b3dbaf470c 100644
--- a/src/components/learner-credit-management/BudgetCard.jsx
+++ b/src/components/learner-credit-management/BudgetCard.jsx
@@ -25,7 +25,7 @@ const BudgetCard = ({
const {
isLoading: isLoadingSubsidySummaryAnalyticsApi,
subsidySummary: subsidySummaryAnalyticsApi,
- } = useSubsidySummaryAnalyticsApi(enterpriseUUID, budget);
+ } = useSubsidySummaryAnalyticsApi(enterpriseUUID, budget.id, budget.source);
// Subsidy Access Policies will always have a single budget, so we can render a single card
// without relying on `useSubsidySummaryAnalyticsApi`.
diff --git a/src/components/learner-credit-management/BudgetDetailAssignments.jsx b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
index 41bcbe1275..99a367ac04 100644
--- a/src/components/learner-credit-management/BudgetDetailAssignments.jsx
+++ b/src/components/learner-credit-management/BudgetDetailAssignments.jsx
@@ -1,9 +1,10 @@
-import React from 'react';
+import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Hyperlink } from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform/config';
+import { useHistory } from 'react-router-dom';
import BudgetAssignmentsTable from './BudgetAssignmentsTable';
import AssignMoreCoursesEmptyStateMinimal from './AssignMoreCoursesEmptyStateMinimal';
import { useBudgetContentAssignments, useBudgetId, useSubsidyAccessPolicy } from './data';
@@ -14,8 +15,13 @@ const BudgetDetailAssignments = ({
enterpriseFeatures,
enterpriseId,
}) => {
+ const assignedHeadingRef = useRef();
const { subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const history = useHistory();
+
+ const { location } = history;
+ const { state: locationState } = location;
const isAssignableBudget = !!subsidyAccessPolicy?.isAssignable;
const assignmentConfigurationUUID = subsidyAccessPolicy?.assignmentConfiguration?.uuid;
const isTopDownAssignmentEnabled = enterpriseFeatures.topDownAssignmentRealTimeLcm;
@@ -29,6 +35,15 @@ const BudgetDetailAssignments = ({
enterpriseId,
});
+ useEffect(() => {
+ if (locationState?.budgetActivityScrollToKey === 'assigned') {
+ assignedHeadingRef.current?.scrollIntoView({ behavior: 'smooth' });
+ const newState = { ...locationState };
+ delete newState.budgetActivityScrollToKey;
+ history.replace({ ...location, state: newState });
+ }
+ }, [history, location, locationState]);
+
if (!isTopDownAssignmentEnabled || !isAssignableBudget) {
return null;
}
@@ -41,7 +56,7 @@ const BudgetDetailAssignments = ({
return (
- Assigned
+ Assigned
Assigned activity earmarks funds in your budget so you can't overspend. For funds to move
from assigned to spent, your learners must complete enrollment.{' '}
diff --git a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
index ee67ecb6de..af044e0a0f 100644
--- a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
@@ -1,9 +1,10 @@
-import React from 'react';
+import React, { useEffect, useRef } from 'react';
import { InstantSearch } from 'react-instantsearch-dom';
import algoliasearch from 'algoliasearch/lite';
import { Row, Col } from '@edx/paragon';
import { SearchData, SEARCH_FACET_FILTERS } from '@edx/frontend-enterprise-catalog-search';
+import { useHistory } from 'react-router';
import CatalogSearch from './search/CatalogSearch';
import {
LANGUAGE_REFINEMENT,
@@ -12,6 +13,11 @@ import {
import { configuration } from '../../config';
const BudgetDetailCatalogTabContents = () => {
+ const history = useHistory();
+ const { location } = history;
+ const { state: locationState } = location;
+ const catalogContainerRef = useRef();
+
const language = {
attribute: LANGUAGE_REFINEMENT,
title: 'Language',
@@ -31,8 +37,18 @@ const BudgetDetailCatalogTabContents = () => {
configuration.ALGOLIA.APP_ID,
configuration.ALGOLIA.SEARCH_API_KEY,
);
+
+ useEffect(() => {
+ if (locationState?.budgetActivityScrollToKey === 'catalog') {
+ catalogContainerRef.current?.scrollIntoView({ behavior: 'smooth' });
+ const newState = { ...locationState };
+ delete newState.budgetActivityScrollToKey;
+ history.replace({ ...location, state: newState });
+ }
+ }, [history, location, locationState]);
+
return (
-
+
(
+
+
+
+);
+
+const mapStateToProps = state => ({
+ enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+});
+
+BudgetDetailPageBreadcrumbs.propTypes = {
+ enterpriseSlug: PropTypes.string.isRequired,
+ budgetDisplayName: PropTypes.string.isRequired,
+};
+
+export default connect(mapStateToProps)(BudgetDetailPageBreadcrumbs);
diff --git a/src/components/learner-credit-management/BudgetDetailPageHeader.jsx b/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
index ad84c6724e..2ea4b409e1 100644
--- a/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
@@ -1,50 +1,114 @@
import React from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import { Link } from 'react-router-dom';
import {
- Row, Col, Breadcrumb, Stack,
+ Stack, Card, Badge, Skeleton,
} from '@edx/paragon';
-import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
-import { useBudgetId, useSubsidyAccessPolicy } from './data';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import {
+ useBudgetId,
+ useSubsidyAccessPolicy,
+ useBudgetDetailHeaderData,
+ useEnterpriseOffer,
+ formatDate,
+ useSubsidySummaryAnalyticsApi,
+} from './data';
+
+import BudgetDetailPageBreadcrumbs from './BudgetDetailPageBreadcrumbs';
+import BudgetDetailPageOverviewAvailability from './BudgetDetailPageOverviewAvailability';
+import BudgetDetailPageOverviewUtilization from './BudgetDetailPageOverviewUtilization';
+import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';
+
+const BudgetStatusBadge = ({
+ badgeVariant, status, term, date,
+}) => (
+
+ { status }
+ {term} { formatDate(date) }
+
+);
+
+BudgetStatusBadge.propTypes = {
+ badgeVariant: PropTypes.string.isRequired,
+ status: PropTypes.string.isRequired,
+ term: PropTypes.string.isRequired,
+ date: PropTypes.string.isRequired,
+};
-const BudgetDetailPageHeader = ({ enterpriseSlug }) => {
- const { subsidyAccessPolicyId } = useBudgetId();
+const BudgetDetailPageHeader = ({ enterpriseUUID }) => {
+ const { subsidyAccessPolicyId, enterpriseOfferId } = useBudgetId();
+ const budgetType = (enterpriseOfferId !== null) ? BUDGET_TYPES.ecommerce : BUDGET_TYPES.policy;
+
+ const { isLoading: isLoadingSubsidySummary, subsidySummary } = useSubsidySummaryAnalyticsApi(
+ enterpriseUUID,
+ enterpriseOfferId,
+ budgetType,
+ );
+
+ const { isLoading: isLoadingEnterpriseOffer, data: enterpriseOfferMetadata } = useEnterpriseOffer(enterpriseOfferId);
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
- const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
+
+ const policyOrOfferId = subsidyAccessPolicyId || enterpriseOfferId;
+ const {
+ budgetId,
+ budgetDisplayName,
+ budgetTotalSummary,
+ budgetAggregates,
+ status,
+ badgeVariant,
+ term,
+ date,
+ isAssignable,
+ } = useBudgetDetailHeaderData({
+ subsidyAccessPolicy,
+ subsidySummary,
+ budgetId: policyOrOfferId,
+ enterpriseOfferMetadata,
+ });
+
+ if (!subsidyAccessPolicy && (isLoadingSubsidySummary || isLoadingEnterpriseOffer)) {
+ return (
+
+
+ Loading budget header data
+
+ );
+ }
+
+ if (subsidyAccessPolicy === null && subsidySummary === null) {
+ return null;
+ }
+
return (
-
-
-
+
+
+ { budgetDisplayName }
+
+
+
-
-
- {budgetDisplayName && (
-
-
- {budgetDisplayName}
-
-
- )}
+
+
);
};
const mapStateToProps = state => ({
- enterpriseSlug: state.portalConfiguration.enterpriseSlug,
+ enterpriseUUID: state.portalConfiguration.enterpriseId,
});
BudgetDetailPageHeader.propTypes = {
- enterpriseSlug: PropTypes.string.isRequired,
+ enterpriseUUID: PropTypes.string.isRequired,
};
export default connect(mapStateToProps)(BudgetDetailPageHeader);
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
new file mode 100644
index 0000000000..761fa1085d
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
@@ -0,0 +1,120 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Button, Col, Hyperlink, ProgressBar, Row, Stack,
+} from '@edx/paragon';
+import { Add } from '@edx/paragon/icons';
+import { generatePath, useRouteMatch, Link } from 'react-router-dom';
+import { formatPrice } from './data';
+import { configuration } from '../../config';
+
+const BudgetDetail = ({ available, utilized, limit }) => {
+ const currentProgressBarLimit = (available / limit) * 100;
+
+ return (
+
+ Available
+
+ {formatPrice(available)}
+
+ Utilized {formatPrice(utilized)}
+
+
+
+
+
+ {formatPrice(limit)} limit
+
+
+
+ );
+};
+
+BudgetDetail.propTypes = {
+ available: PropTypes.number.isRequired,
+ utilized: PropTypes.number.isRequired,
+ limit: PropTypes.number.isRequired,
+};
+
+const BudgetActions = ({ budgetId, isAssignable }) => {
+ const routeMatch = useRouteMatch();
+ const supportUrl = configuration.ENTERPRISE_SUPPORT_URL;
+
+ if (!isAssignable) {
+ return (
+
+
+
Get people learning using this budget
+
+ Funds from this budget are set to autoallocate to registered learners based on
+ settings configured with your support team.
+
+
+ Contact support
+
+
+
+ );
+ }
+
+ return (
+
+
+
Get people learning using this budget
+
+ New course assignment
+
+
+
+ );
+};
+
+BudgetActions.propTypes = {
+ budgetId: PropTypes.string.isRequired,
+ isAssignable: PropTypes.bool.isRequired,
+};
+
+const BudgetDetailPageOverviewAvailability = (
+ {
+ budgetId,
+ isAssignable,
+ budgetTotalSummary: { available, utilized, limit },
+ },
+) => (
+
+
+
+
+
+
+
+
+
+
+);
+
+const budgetTotalSummaryShape = {
+ utilized: PropTypes.number.isRequired,
+ available: PropTypes.number.isRequired,
+ limit: PropTypes.number.isRequired,
+};
+
+BudgetDetailPageOverviewAvailability.propTypes = {
+ budgetId: PropTypes.string.isRequired,
+ budgetTotalSummary: PropTypes.shape(budgetTotalSummaryShape).isRequired,
+ isAssignable: PropTypes.bool.isRequired,
+};
+
+export default BudgetDetailPageOverviewAvailability;
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
new file mode 100644
index 0000000000..5fcd15022b
--- /dev/null
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
@@ -0,0 +1,127 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Stack, Collapsible, Row, Col, Button,
+} from '@edx/paragon';
+import { ArrowDownward } from '@edx/paragon/icons';
+
+import {
+ generatePath, useRouteMatch, Link,
+} from 'react-router-dom';
+import { formatPrice } from './data';
+
+const BudgetDetailPageOverviewUtilization = (
+ {
+ budgetId,
+ budgetTotalSummary: { utilized },
+ budgetAggregates,
+ isAssignable,
+ },
+) => {
+ const routeMatch = useRouteMatch();
+
+ const { amountAllocatedUsd, amountRedeemedUsd } = budgetAggregates;
+
+ if (budgetId === null || utilized <= 0 || !isAssignable) {
+ return null;
+ }
+
+ const renderActivityLink = ({ amount, type }) => {
+ if (amount <= 0) {
+ return null;
+ }
+
+ const linkText = (type === 'assigned') ? 'View assigned activity' : 'View spent activity';
+
+ return (
+
+ {linkText}
+
+ );
+ };
+
+ return (
+ Utilization details}
+ >
+
+
+
+
+ Utilized
+
+ {formatPrice(utilized)}
+
+ Your total utilization includes both assigned funds (earmarked for future enrollment) and spent
+ funds (redeemed for enrollment).
+
+
+
+ Amount assigned
+
+ {formatPrice(amountAllocatedUsd)}
+
+
+ {
+ renderActivityLink({
+ amount: amountAllocatedUsd,
+ type: 'assigned',
+ })
+ }
+
+
+
+ Amount spent
+
+ {formatPrice(amountRedeemedUsd)}
+
+
+ {
+ renderActivityLink({
+ amount: amountRedeemedUsd,
+ type: 'spent',
+ })
+ }
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const budgetTotalSummaryShape = {
+ utilized: PropTypes.number.isRequired,
+ available: PropTypes.number.isRequired,
+ limit: PropTypes.number.isRequired,
+};
+
+const budgetAggregatesShape = {
+ amountAllocatedUsd: PropTypes.number.isRequired,
+ amountRedeemedUsd: PropTypes.number.isRequired,
+};
+
+BudgetDetailPageOverviewUtilization.propTypes = {
+ budgetId: PropTypes.string.isRequired,
+ budgetTotalSummary: PropTypes.shape(budgetTotalSummaryShape).isRequired,
+ budgetAggregates: PropTypes.shape(budgetAggregatesShape).isRequired,
+ isAssignable: PropTypes.bool.isRequired,
+};
+
+export default BudgetDetailPageOverviewUtilization;
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index 54a2c4810f..7fe5d41fe8 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -1,14 +1,19 @@
-import React from 'react';
+import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Hyperlink } from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform/config';
+import { useHistory } from 'react-router';
import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { useBudgetId, useBudgetRedemptions } from './data';
const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
+ const history = useHistory();
+ const { location } = history;
+ const { state: locationState } = location;
const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
+ const spentHeadingRef = useRef();
const {
isLoading,
budgetRedemptions,
@@ -19,9 +24,19 @@ const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
subsidyAccessPolicyId,
enterpriseFeatures.topDownAssignmentRealTimeLcm,
);
+
+ useEffect(() => {
+ if (locationState?.budgetActivityScrollToKey === 'spent') {
+ spentHeadingRef.current?.scrollIntoView({ behavior: 'smooth' });
+ const newState = { ...locationState };
+ delete newState.budgetActivityScrollToKey;
+ history.replace({ ...location, state: newState });
+ }
+ }, [history, location, locationState]);
+
return (
- Spent
+ Spent
Spent activity is driven by completed enrollments.{' '}
diff --git a/src/components/learner-credit-management/data/constants.js b/src/components/learner-credit-management/data/constants.js
index 004b905118..6d82c02338 100644
--- a/src/components/learner-credit-management/data/constants.js
+++ b/src/components/learner-credit-management/data/constants.js
@@ -70,6 +70,8 @@ export const learnerCreditManagementQueryKeys = {
all: ['learner-credit-management'],
budgets: (enterpriseId) => [...learnerCreditManagementQueryKeys.all, 'budgets', enterpriseId],
budget: (budgetId) => [...learnerCreditManagementQueryKeys.all, 'budget', budgetId],
+ // Used when fetching enterprise offer metadata when viewing the budget detail page for enterprise offer
+ budgetEnterpriseOffer: (budgetId) => [...learnerCreditManagementQueryKeys.budget(budgetId), 'ecommerce'],
budgetActivity: (budgetId) => [...learnerCreditManagementQueryKeys.budget(budgetId), 'activity'],
budgetActivityOverview: (budgetId) => [...learnerCreditManagementQueryKeys.budgetActivity(budgetId), 'overview'],
};
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index d979f3867a..63681a4030 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -4,9 +4,11 @@ export { default as useBudgetRedemptions } from './useBudgetRedemptions';
export { default as useBudgetContentAssignments } from './useBudgetContentAssignments';
export { default as useBudgetId } from './useBudgetId';
export { default as useSubsidyAccessPolicy } from './useSubsidyAccessPolicy';
+export { default as useBudgetDetailHeaderData } from './useBudgetDetailHeaderData';
export { default as usePathToCatalogTab } from './usePathToCatalogTab';
export { default as useBudgetDetailActivityOverview } from './useBudgetDetailActivityOverview';
export { default as useIsLargeOrGreater } from './useIsLargeOrGreater';
export { default as useSuccessfulAssignmentToastContextValue } from './useSuccessfulAssignmentToastContextValue';
export { default as useSuccessfulCancellationToastContextValue } from './useSuccessfulCancellationToastContextValue';
export { default as useSuccessfulReminderToastContextValue } from './useSuccessfulReminderToastContextValue';
+export { default as useEnterpriseOffer } from './useEnterpriseOffer';
diff --git a/src/components/learner-credit-management/data/hooks/tests/useEnterpriseOffer.test.jsx b/src/components/learner-credit-management/data/hooks/tests/useEnterpriseOffer.test.jsx
new file mode 100644
index 0000000000..a5ed946d48
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/tests/useEnterpriseOffer.test.jsx
@@ -0,0 +1,100 @@
+import { QueryClientProvider } from '@tanstack/react-query';
+import { renderHook } from '@testing-library/react-hooks';
+
+import EcommerceApiService from '../../../../../data/services/EcommerceApiService';
+import useEnterpriseOffer from '../useEnterpriseOffer'; // Import the hook
+import { queryClient } from '../../../../test/testUtils';
+
+const mockEnterpriseOfferUUID = '9af340a9-48de-4d94-976d-e2282b9eb7f3';
+
+// Mock the EcommerceApiService
+jest.mock('../../../../../data/services/EcommerceApiService', () => ({
+ fetchEnterpriseOffer: jest.fn().mockResolvedValue({
+ data: {
+ id: 99511,
+ startDatetime: '2022-09-01T00:00:00Z',
+ endDatetime: '2024-09-01T00:00:00Z',
+ displayName: 'Test enterprise',
+ // Other properties...
+ },
+ }),
+}));
+
+const wrapper = ({ children }) => (
+ {children}
+);
+
+describe('useEnterpriseOffer', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fetch and return enterprise offer (%s)', async () => {
+ // Mock the policy type in response based on isAssignable
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseOffer(mockEnterpriseOfferUUID),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.isLoading).toBe(false);
+ expect(result.current.isError).toBe(false);
+ expect(result.current.data).toEqual({
+ id: 99511,
+ startDatetime: '2022-09-01T00:00:00Z',
+ endDatetime: '2024-09-01T00:00:00Z',
+ displayName: 'Test enterprise',
+ });
+ });
+
+ it('should handle errors gracefully', async () => {
+ // Mock an error response from the API
+ jest.spyOn(EcommerceApiService, 'fetchEnterpriseOffer').mockRejectedValueOnce(new Error('Mock API Error'));
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => useEnterpriseOffer(mockEnterpriseOfferUUID),
+ { wrapper },
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.isLoading).toBe(false);
+ expect(result.current.isError).toBe(true);
+ expect(result.current.error.message).toBe('Mock API Error');
+ });
+
+ it.each([
+ {
+ enterpriseOfferId: undefined,
+ expectedData: undefined,
+ },
+ {
+ enterpriseOfferId: mockEnterpriseOfferUUID,
+ expectedData: {
+ id: 99511,
+ startDatetime: '2022-09-01T00:00:00Z',
+ endDatetime: '2024-09-01T00:00:00Z',
+ displayName: 'Test enterprise',
+ // Other expected properties...
+ },
+ },
+ ])('should enable/disable the query based on subsidyAccessPolicyId (%s)', async ({
+ enterpriseOfferId,
+ expectedData,
+ }) => {
+ const { result, waitForNextUpdate } = renderHook(() => useEnterpriseOffer(enterpriseOfferId), { wrapper });
+
+ if (expectedData !== undefined) {
+ await waitForNextUpdate();
+ expect(result.current.isLoading).toBe(false);
+ } else {
+ expect(result.current.isLoading).toBe(true);
+ }
+
+ expect(result.current.isInitialLoading).toBe(false);
+ expect(result.current.isError).toBe(false);
+ expect(result.current.data).toEqual(expectedData);
+ });
+});
diff --git a/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js b/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
index 3fa26919c9..b079df60a9 100644
--- a/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
+++ b/src/components/learner-credit-management/data/hooks/tests/useSubsidySummaryAnalyticsApi.test.js
@@ -88,7 +88,8 @@ describe('useSubsidySummaryAnalyticsApi', () => {
waitForNextUpdate,
} = renderHook(() => useSubsidySummaryAnalyticsApi(
TEST_ENTERPRISE_UUID,
- mockBudget,
+ mockBudget.id,
+ mockBudget.source,
));
if (shouldCallApi) {
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetDetailHeaderData.js b/src/components/learner-credit-management/data/hooks/useBudgetDetailHeaderData.js
new file mode 100644
index 0000000000..06047d3e1f
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useBudgetDetailHeaderData.js
@@ -0,0 +1,73 @@
+import { getBudgetStatus } from '../utils';
+
+const transformSubsidySummaryToPolicy = (subsidySummary, metadata) => {
+ if (!subsidySummary) { return null; }
+
+ return {
+ displayName: metadata.displayName,
+ subsidyActiveDatetime: metadata.startDatetime,
+ subsidyExpirationDatetime: metadata.endDatetime,
+ aggregates: {
+ spendAvailableUsd: subsidySummary.remainingBalance,
+ amountAllocatedUsd: 0,
+ amountRedeemedUsd: subsidySummary.amountOfOfferSpent,
+ },
+ spendLimit: subsidySummary.maxDiscount * 100,
+ isAssignable: false,
+ };
+};
+
+const assignBudgetStatus = (policy) => {
+ const {
+ status, badgeVariant, term, date,
+ } = getBudgetStatus(
+ policy.subsidyActiveDatetime,
+ policy.subsidyExpirationDatetime,
+ );
+
+ return {
+ status, badgeVariant, term, date,
+ };
+};
+
+const assignBudgetDetails = (policy) => {
+ const { spendAvailableUsd, amountAllocatedUsd, amountRedeemedUsd } = policy.aggregates;
+
+ const available = spendAvailableUsd;
+ const limit = policy.spendLimit / 100;
+ const utilized = policy.isAssignable
+ ? (amountAllocatedUsd + amountRedeemedUsd)
+ : amountRedeemedUsd;
+
+ return { budgetTotalSummary: { available, limit, utilized } };
+};
+
+const useBudgetDetailHeaderData = ({
+ subsidyAccessPolicy, subsidySummary, budgetId, enterpriseOfferMetadata,
+}) => {
+ const policy = subsidyAccessPolicy || transformSubsidySummaryToPolicy(subsidySummary, enterpriseOfferMetadata);
+
+ if (policy == null) {
+ return {};
+ }
+
+ const defaultData = {
+ budgetId,
+ budgetTotalSummary: {
+ available: 0,
+ utilized: 0,
+ limit: 0,
+ },
+ budgetAggregates: policy.aggregates || {},
+ isAssignable: policy.isAssignable || false,
+ budgetDisplayName: policy.displayName || 'Overview',
+ };
+
+ return {
+ ...defaultData,
+ ...assignBudgetStatus(policy),
+ ...assignBudgetDetails(policy),
+ };
+};
+
+export default useBudgetDetailHeaderData;
diff --git a/src/components/learner-credit-management/data/hooks/useEnterpriseOffer.js b/src/components/learner-credit-management/data/hooks/useEnterpriseOffer.js
new file mode 100644
index 0000000000..453e27843f
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useEnterpriseOffer.js
@@ -0,0 +1,20 @@
+// modify the query keys map to include a queryKey for `budgetEnterpriseOffer` that depends on `.budget()`.
+import { useQuery } from '@tanstack/react-query';
+import { camelCaseObject } from '@edx/frontend-platform/utils';
+import EcommerceApiService from '../../../../data/services/EcommerceApiService';
+import { learnerCreditManagementQueryKeys } from '../constants';
+
+const getEnterpriseOffer = async ({ queryKey }) => {
+ const enterpriseOfferId = queryKey[2];
+ const response = await EcommerceApiService.fetchEnterpriseOffer(enterpriseOfferId);
+ return camelCaseObject(response.data);
+};
+
+// Hook to fetch an individual enterprise offer from ecommerce.
+const useEnterpriseOffer = (enterpriseOfferId) => useQuery({
+ queryKey: learnerCreditManagementQueryKeys.budgetEnterpriseOffer(enterpriseOfferId),
+ queryFn: getEnterpriseOffer,
+ enabled: !!enterpriseOfferId,
+});
+
+export default useEnterpriseOffer;
diff --git a/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js b/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
index f0aaee7826..9b559817a2 100644
--- a/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
+++ b/src/components/learner-credit-management/data/hooks/useSubsidySummaryAnalyticsApi.js
@@ -6,14 +6,14 @@ import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataAp
import { transformSubsidySummary } from '../utils';
import { BUDGET_TYPES } from '../../../EnterpriseApp/data/constants';
-const useSubsidySummaryAnalyticsApi = (enterpriseUUID, budget) => {
+const useSubsidySummaryAnalyticsApi = (enterpriseUUID, budgetId, budgetSource) => {
const [isLoading, setIsLoading] = useState(true);
const [subsidySummary, setSubsidySummary] = useState();
useEffect(() => {
// If there is no budget, or the budget is NOT an ecommerce offer or subsidy, fetch the
// subsidy summary data from the analytics API.
- if (![BUDGET_TYPES.ecommerce, BUDGET_TYPES.subsidy].includes(budget?.source)) {
+ if (![BUDGET_TYPES.ecommerce, BUDGET_TYPES.subsidy].includes(budgetSource)) {
setIsLoading(false);
return;
}
@@ -23,7 +23,7 @@ const useSubsidySummaryAnalyticsApi = (enterpriseUUID, budget) => {
setIsLoading(true);
const response = await EnterpriseDataApiService.fetchEnterpriseOfferSummary(
enterpriseUUID,
- budget.id,
+ budgetId,
);
const data = camelCaseObject(response.data);
const transformedSubsidySummary = transformSubsidySummary(data);
@@ -36,7 +36,7 @@ const useSubsidySummaryAnalyticsApi = (enterpriseUUID, budget) => {
};
fetchData();
- }, [enterpriseUUID, budget]);
+ }, [enterpriseUUID, budgetId, budgetSource]);
return {
isLoading,
diff --git a/src/components/learner-credit-management/data/tests/constants.js b/src/components/learner-credit-management/data/tests/constants.js
index 285a0e7e82..69503fbc46 100644
--- a/src/components/learner-credit-management/data/tests/constants.js
+++ b/src/components/learner-credit-management/data/tests/constants.js
@@ -3,13 +3,75 @@ export const mockSubsidyAccessPolicyUUID = 'c17de32e-b80b-468f-b994-85e68fd32751
export const mockAssignableSubsidyAccessPolicy = {
uuid: mockSubsidyAccessPolicyUUID,
+ subsidyActiveDatetime: '2023-11-01T13:06:46Z',
+ subsidyExpirationDatetime: '2024-02-29T13:06:59Z',
policyType: 'AssignedLearnerCreditAccessPolicy',
+ displayName: 'Assignable Learner Credit',
+ spendLimit: 10000 * 100,
assignmentConfiguration: {
uuid: 'test-uuid',
},
+ aggregates: {
+ spendAvailableUsd: 10000,
+ amountAllocatedUsd: 100,
+ amountRedeemedUsd: 350,
+ },
+ isAssignable: true,
+ subsidyUuid: 'mock-subsidy-uuid',
+};
+
+export const mockAssignableSubsidyAccessPolicyWithNoUtilization = {
+ uuid: mockSubsidyAccessPolicyUUID,
+ subsidyActiveDatetime: '2023-11-01T13:06:46Z',
+ subsidyExpirationDatetime: '2024-02-29T13:06:59Z',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
displayName: 'Assignable Learner Credit',
+ spendLimit: 10000 * 100,
+ assignmentConfiguration: {
+ uuid: 'test-uuid',
+ },
aggregates: {
spendAvailableUsd: 10000,
+ amountAllocatedUsd: 0,
+ amountRedeemedUsd: 0,
+ },
+ isAssignable: true,
+ subsidyUuid: 'mock-subsidy-uuid',
+};
+
+export const mockAssignableSubsidyAccessPolicyWithSpendNoAllocations = {
+ uuid: mockSubsidyAccessPolicyUUID,
+ subsidyActiveDatetime: '2023-11-01T13:06:46Z',
+ subsidyExpirationDatetime: '2024-02-29T13:06:59Z',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ displayName: 'Assignable Learner Credit',
+ spendLimit: 10000 * 100,
+ assignmentConfiguration: {
+ uuid: 'test-uuid',
+ },
+ aggregates: {
+ spendAvailableUsd: 10000,
+ amountAllocatedUsd: 0,
+ amountRedeemedUsd: 5000,
+ },
+ isAssignable: true,
+ subsidyUuid: 'mock-subsidy-uuid',
+};
+
+export const mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed = {
+ uuid: mockSubsidyAccessPolicyUUID,
+ subsidyActiveDatetime: '2023-11-01T13:06:46Z',
+ subsidyExpirationDatetime: '2024-02-29T13:06:59Z',
+ policyType: 'AssignedLearnerCreditAccessPolicy',
+ displayName: 'Assignable Learner Credit',
+ spendLimit: 10000 * 100,
+ assignmentConfiguration: {
+ uuid: 'test-uuid',
+ },
+ aggregates: {
+ spendAvailableUsd: 10000,
+ amountAllocatedUsd: 0,
+ amountRedeemedUsd: 5000,
},
isAssignable: true,
subsidyUuid: 'mock-subsidy-uuid',
@@ -17,11 +79,46 @@ export const mockAssignableSubsidyAccessPolicy = {
export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
uuid: mockSubsidyAccessPolicyUUID,
+ subsidyActiveDatetime: '2023-11-01T13:06:46Z',
+ subsidyExpirationDatetime: '2024-02-29T13:06:59Z',
policyType: 'PerLearnerSpendCreditAccessPolicy',
displayName: 'Per Learner Spend Limit',
+ spendLimit: 10000 * 100,
aggregates: {
spendAvailableUsd: 10000,
+ amountAllocatedUsd: 100,
+ amountRedeemedUsd: 350,
},
isAssignable: false,
subsidyUuid: 'mock-subsidy-uuid',
};
+
+export const mockSubsidySummary = {
+ offerId: '84014',
+ budgets: [],
+ enterpriseCustomerUuid: '852eac48-b5a9-4849-8490-743f3f2deabf',
+ enterpriseName: 'Executive Education (2U) Integration QA',
+ sumAmountLearnerPaid: 0.0,
+ sumCoursePrice: 0.0,
+ status: 'Open',
+ offerType: 'Site',
+ dateCreated: '2022-09-23T12:37:32Z',
+ emailsForUsageAlert: '',
+ discountType: 'percent_discount',
+ discountValue: 100.0,
+ maxDiscount: 50000.0,
+ totalDiscountEcommerce: 42024.0,
+ amountOfOfferSpent: 0.0,
+ percentOfOfferSpent: 0.0,
+ remainingBalance: 50000.0,
+ amountOfferSpentOcm: 0.0,
+ amountOfferSpentExecEd: 0.0,
+ exportTimestamp: '2023-12-04T06:47:54Z',
+};
+
+export const mockEnterpriseOfferMetadata = {
+ id: 99511,
+ startDatetime: '2022-09-01T00:00:00Z',
+ endDatetime: '2024-09-01T00:00:00Z',
+ displayName: 'Test enterprise',
+};
diff --git a/src/components/learner-credit-management/search/CatalogSearch.jsx b/src/components/learner-credit-management/search/CatalogSearch.jsx
index a557a66dc3..f7174d2cc4 100644
--- a/src/components/learner-credit-management/search/CatalogSearch.jsx
+++ b/src/components/learner-credit-management/search/CatalogSearch.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import algoliasearch from 'algoliasearch/lite';
import { Configure, InstantSearch } from 'react-instantsearch-dom';
@@ -19,6 +18,7 @@ const CatalogSearch = () => {
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const searchFilters = `enterprise_catalog_uuids:${ENABLE_TESTING(subsidyAccessPolicy.catalogUuid)} AND content_type:course`;
const displayName = subsidyAccessPolicy.displayName ? `${subsidyAccessPolicy.displayName} catalog` : 'Overview';
+
return (
({
useBudgetRedemptions: jest.fn(),
useBudgetContentAssignments: jest.fn(),
useSubsidyAccessPolicy: jest.fn(),
+ useSubsidySummaryAnalyticsApi: jest.fn(),
+ useEnterpriseOffer: jest.fn(),
useBudgetDetailActivityOverview: jest.fn(),
useIsLargeOrGreater: jest.fn().mockReturnValue(true),
useCancelContentAssignments: jest.fn(),
@@ -183,6 +192,16 @@ const BudgetDetailPageWrapper = ({
describe(' ', () => {
beforeEach(() => {
jest.resetAllMocks();
+
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {},
+ });
+
+ useEnterpriseOffer.mockReturnValue({
+ isLoading: false,
+ data: {},
+ });
});
it('renders page not found messaging if budget is a subsidy access policy, but the REST API returns a 404', () => {
@@ -219,6 +238,7 @@ describe(' ', () => {
isLoading: false,
data: mockEmptyStateBudgetDetailActivityOverview,
});
+
const expectedDisplayName = displayName || 'Overview';
renderWithRouter( );
@@ -230,6 +250,180 @@ describe(' ', () => {
expect(screen.getByText(expectedDisplayName, { selector: 'h2' }));
});
+ it.each([
+ {
+ subsidyAccessPolicy: null,
+ subsidySummary: null,
+ expected: null,
+ isLoading: true,
+ },
+ {
+ subsidyAccessPolicy: null,
+ subsidySummary: null,
+ expected: null,
+ isLoading: false,
+ },
+ {
+ subsidyAccessPolicy: mockAssignableSubsidyAccessPolicy,
+ subsidySummary: null,
+ expected: {
+ displayName: mockAssignableSubsidyAccessPolicy.displayName,
+ spend: formatPrice(mockAssignableSubsidyAccessPolicy.aggregates.spendAvailableUsd),
+ utilized: formatPrice(
+ mockAssignableSubsidyAccessPolicy.aggregates.amountAllocatedUsd
+ + mockAssignableSubsidyAccessPolicy.aggregates.amountRedeemedUsd,
+ ),
+ limit: formatPrice(mockAssignableSubsidyAccessPolicy.spendLimit / 100),
+ allocated: formatPrice(mockAssignableSubsidyAccessPolicy.aggregates.amountAllocatedUsd),
+ redeemed: formatPrice(mockAssignableSubsidyAccessPolicy.aggregates.amountRedeemedUsd),
+ },
+ isLoading: false,
+ },
+ {
+ subsidyAccessPolicy: mockAssignableSubsidyAccessPolicyWithNoUtilization,
+ subsidySummary: null,
+ expected: {
+ displayName: mockAssignableSubsidyAccessPolicyWithNoUtilization.displayName,
+ spend: formatPrice(mockAssignableSubsidyAccessPolicyWithNoUtilization.aggregates.spendAvailableUsd),
+ utilized: formatPrice(
+ mockAssignableSubsidyAccessPolicyWithNoUtilization.aggregates.amountAllocatedUsd
+ + mockAssignableSubsidyAccessPolicyWithNoUtilization.aggregates.amountRedeemedUsd,
+ ),
+ limit: formatPrice(mockAssignableSubsidyAccessPolicyWithNoUtilization.spendLimit / 100),
+ allocated: formatPrice(mockAssignableSubsidyAccessPolicyWithNoUtilization.aggregates.amountAllocatedUsd),
+ redeemed: formatPrice(mockAssignableSubsidyAccessPolicyWithNoUtilization.aggregates.amountRedeemedUsd),
+ },
+ isLoading: false,
+ },
+ {
+ subsidyAccessPolicy: mockAssignableSubsidyAccessPolicyWithSpendNoAllocations,
+ subsidySummary: null,
+ expected: {
+ displayName: mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.displayName,
+ spend: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.aggregates.spendAvailableUsd),
+ utilized: formatPrice(
+ mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.aggregates.amountAllocatedUsd
+ + mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.aggregates.amountRedeemedUsd,
+ ),
+ limit: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.spendLimit / 100),
+ allocated: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.aggregates.amountAllocatedUsd),
+ redeemed: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoAllocations.aggregates.amountRedeemedUsd),
+ },
+ isLoading: false,
+ },
+ {
+ subsidyAccessPolicy: mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed,
+ subsidySummary: null,
+ expected: {
+ displayName: mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.displayName,
+ spend: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.aggregates.spendAvailableUsd),
+ utilized: formatPrice(
+ mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.aggregates.amountAllocatedUsd
+ + mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.aggregates.amountRedeemedUsd,
+ ),
+ limit: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.spendLimit / 100),
+ allocated: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.aggregates.amountAllocatedUsd),
+ redeemed: formatPrice(mockAssignableSubsidyAccessPolicyWithSpendNoRedeemed.aggregates.amountRedeemedUsd),
+ },
+ isLoading: false,
+ },
+ {
+ subsidyAccessPolicy: mockPerLearnerSpendLimitSubsidyAccessPolicy,
+ subsidySummary: null,
+ expected: {
+ displayName: mockPerLearnerSpendLimitSubsidyAccessPolicy.displayName,
+ spend: formatPrice(mockPerLearnerSpendLimitSubsidyAccessPolicy.aggregates.spendAvailableUsd),
+ utilized: formatPrice(mockPerLearnerSpendLimitSubsidyAccessPolicy.aggregates.amountRedeemedUsd),
+ limit: formatPrice(mockPerLearnerSpendLimitSubsidyAccessPolicy.spendLimit / 100),
+ allocated: formatPrice(mockPerLearnerSpendLimitSubsidyAccessPolicy.aggregates.amountAllocatedUsd),
+ redeemed: formatPrice(mockPerLearnerSpendLimitSubsidyAccessPolicy.aggregates.amountRedeemedUsd),
+ },
+ isLoading: false,
+ },
+ {
+ subsidyAccessPolicy: null,
+ subsidySummary: mockSubsidySummary,
+ expected: {
+ displayName: mockEnterpriseOfferMetadata.displayName,
+ spend: formatPrice(mockSubsidySummary.remainingBalance),
+ utilized: formatPrice(mockSubsidySummary.amountOfOfferSpent),
+ limit: formatPrice(mockSubsidySummary.maxDiscount),
+ allocated: formatPrice(0),
+ redeemed: formatPrice(mockSubsidySummary.amountOfOfferSpent),
+ },
+ isLoading: false,
+ },
+ ])('render budget banner data (%s)', async ({
+ subsidyAccessPolicy, subsidySummary, expected, isLoading,
+ }) => {
+ useParams.mockReturnValue({
+ budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
+ activeTabKey: 'activity',
+ });
+ useSubsidyAccessPolicy.mockReturnValue({
+ isInitialLoading: false,
+ isLoading,
+ data: subsidyAccessPolicy,
+ });
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading,
+ subsidySummary,
+ });
+ useBudgetDetailActivityOverview.mockReturnValue({
+ isLoading: false,
+ data: {
+ contentAssignments: undefined,
+ spentTransactions: { count: 0 },
+ },
+ });
+ useEnterpriseOffer.mockReturnValue({
+ isLoading: false,
+ data: mockEnterpriseOfferMetadata,
+ });
+ useBudgetRedemptions.mockReturnValue({
+ isLoading: false,
+ budgetRedemptions: mockEmptyBudgetRedemptions,
+ fetchBudgetRedemptions: jest.fn(),
+ });
+
+ renderWithRouter( );
+
+ if (isLoading) {
+ expect(screen.getByTestId('budget-detail-skeleton'));
+ }
+
+ if (subsidyAccessPolicy?.isAssignable) {
+ const redeemed = subsidyAccessPolicy.aggregates.amountRedeemedUsd;
+ const allocated = subsidyAccessPolicy.aggregates.amountAllocatedUsd;
+
+ const utilized = redeemed + allocated;
+
+ if (utilized > 0) {
+ userEvent.click(screen.getByText('Utilization details'));
+
+ expect(screen.getByTestId('budget-utilization-amount')).toHaveTextContent(expected.utilized);
+ expect(screen.getByTestId('budget-utilization-assigned')).toHaveTextContent(expected.allocated);
+ expect(screen.getByTestId('budget-utilization-spent')).toHaveTextContent(expected.redeemed);
+
+ if (allocated <= 0) {
+ expect(screen.queryByText('View assigned activity')).not.toBeInTheDocument();
+ }
+
+ if (redeemed <= 0) {
+ expect(screen.queryByText('View spent activity')).not.toBeInTheDocument();
+ }
+ }
+ }
+
+ if ((subsidySummary || subsidySummary) && !isLoading) {
+ expect(screen.getByText(expected.displayName, { selector: 'h2' }));
+
+ expect(screen.getByTestId('budget-detail-available')).toHaveTextContent(expected.spend);
+ expect(screen.getByTestId('budget-detail-utilized')).toHaveTextContent(`Utilized ${expected.utilized}`);
+ expect(screen.getByTestId('budget-detail-limit')).toHaveTextContent(expected.limit);
+ }
+ });
+
it.each([
{ isLargeViewport: true },
{ isLargeViewport: false },
diff --git a/src/data/services/EcommerceApiService.js b/src/data/services/EcommerceApiService.js
index feeed25000..d599f7649c 100644
--- a/src/data/services/EcommerceApiService.js
+++ b/src/data/services/EcommerceApiService.js
@@ -88,6 +88,16 @@ class EcommerceApiService {
return EcommerceApiService.apiClient().post(url, options);
}
+ static fetchEnterpriseOffer(budgetId, options) {
+ const { enterpriseId } = store.getState().portalConfiguration;
+ let url = `${EcommerceApiService.ecommerceBaseUrl}/api/v2/enterprise/${enterpriseId}/enterprise-admin-offers/${budgetId}/`;
+ if (options) {
+ const queryParams = new URLSearchParams(snakeCaseObject(options));
+ url += `?${queryParams.toString()}`;
+ }
+ return EcommerceApiService.apiClient().get(url);
+ }
+
static fetchEnterpriseOffers(options) {
const { enterpriseId } = store.getState().portalConfiguration;
let url = `${EcommerceApiService.ecommerceBaseUrl}/api/v2/enterprise/${enterpriseId}/enterprise-admin-offers/`;
From 5aa596cfdbc126a22508fa5c17c4a6c79b327404 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 8 Dec 2023 16:56:16 -0500
Subject: [PATCH 107/124] fix: use correct aggregates properties and resolve JS
error (#1130)
---
.env.development-stage | 1 +
.../BudgetDetailPage.jsx | 17 ++++-
.../BudgetDetailPageHeader.jsx | 25 ++++---
.../BudgetDetailPageOverviewAvailability.jsx | 38 ++++++----
.../BudgetDetailPageOverviewUtilization.jsx | 49 ++++++------
.../BudgetDetailPageWrapper.jsx | 28 +++++--
.../BudgetDetailRedemptions.jsx | 9 ++-
.../data/hooks/useBudgetDetailHeaderData.js | 74 +++++++++++--------
.../data/hooks/useBudgetRedemptions.js | 4 +-
.../data/tests/constants.js | 27 ++-----
.../tests/BudgetDetailPage.test.jsx | 20 +++--
11 files changed, 173 insertions(+), 119 deletions(-)
diff --git a/.env.development-stage b/.env.development-stage
index 39accee150..9fda466b1b 100644
--- a/.env.development-stage
+++ b/.env.development-stage
@@ -70,6 +70,7 @@ ACCOUNT_PROFILE_URL="https://profile.stage.edx.org"
SUPPORT_URL="https://support.edx.org"
ENTERPRISE_SUPPORT_URL="https://business-support.edx.org/hc/en-us"
ENTERPRISE_SUPPORT_PROGRAM_OPTIMIZATION_URL="https://business.edx.org/hubfs/Onboarding%20and%20Engagement/Onboarding%20Assets/Admin%20Resources/Program%20Optimization.pdf?hsLang=en"
+ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL='http://stage.edx.org'
CONTACT_URL="https://courses.stage.edx.org/support/contact_us"
OPEN_SOURCE_URL="https://open.edx.org"
TERMS_OF_SERVICE_URL="https://stage.edx.org/edx-terms-service"
diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx
index 3a7192e5e4..49b0feda86 100644
--- a/src/components/learner-credit-management/BudgetDetailPage.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPage.jsx
@@ -1,22 +1,28 @@
import React from 'react';
import { Skeleton, Stack } from '@edx/paragon';
-import { useBudgetId, useSubsidyAccessPolicy } from './data';
+import { useBudgetId, useEnterpriseOffer, useSubsidyAccessPolicy } from './data';
import BudgetDetailTabsAndRoutes from './BudgetDetailTabsAndRoutes';
import BudgetDetailPageWrapper from './BudgetDetailPageWrapper';
import BudgetDetailPageHeader from './BudgetDetailPageHeader';
import NotFoundPage from '../NotFoundPage';
const BudgetDetailPage = () => {
- const { subsidyAccessPolicyId } = useBudgetId();
+ const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
const {
data: subsidyAccessPolicy,
isInitialLoading: isSubsidyAccessPolicyInitialLoading,
isError: isSubsidyAccessPolicyError,
error,
} = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ data: enterpriseOffer,
+ isInitialLoading: isEnterpriseOfferInitialLoading,
+ } = useEnterpriseOffer(enterpriseOfferId);
+
+ const isLoading = isSubsidyAccessPolicyInitialLoading || isEnterpriseOfferInitialLoading;
- if (isSubsidyAccessPolicyInitialLoading) {
+ if (isLoading) {
return (
@@ -35,7 +41,10 @@ const BudgetDetailPage = () => {
}
return (
-
+
diff --git a/src/components/learner-credit-management/BudgetDetailPageHeader.jsx b/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
index 2ea4b409e1..6269ba44fa 100644
--- a/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageHeader.jsx
@@ -28,14 +28,7 @@ const BudgetStatusBadge = ({
);
-BudgetStatusBadge.propTypes = {
- badgeVariant: PropTypes.string.isRequired,
- status: PropTypes.string.isRequired,
- term: PropTypes.string.isRequired,
- date: PropTypes.string.isRequired,
-};
-
-const BudgetDetailPageHeader = ({ enterpriseUUID }) => {
+const BudgetDetailPageHeader = ({ enterpriseUUID, enterpriseFeatures }) => {
const { subsidyAccessPolicyId, enterpriseOfferId } = useBudgetId();
const budgetType = (enterpriseOfferId !== null) ? BUDGET_TYPES.ecommerce : BUDGET_TYPES.policy;
@@ -64,6 +57,7 @@ const BudgetDetailPageHeader = ({ enterpriseUUID }) => {
subsidySummary,
budgetId: policyOrOfferId,
enterpriseOfferMetadata,
+ isTopDownAssignmentEnabled: enterpriseFeatures.topDownAssignmentRealTimeLcm,
});
if (!subsidyAccessPolicy && (isLoadingSubsidySummary || isLoadingEnterpriseOffer)) {
@@ -75,10 +69,6 @@ const BudgetDetailPageHeader = ({ enterpriseUUID }) => {
);
}
- if (subsidyAccessPolicy === null && subsidySummary === null) {
- return null;
- }
-
return (
@@ -105,10 +95,21 @@ const BudgetDetailPageHeader = ({ enterpriseUUID }) => {
const mapStateToProps = state => ({
enterpriseUUID: state.portalConfiguration.enterpriseId,
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
});
BudgetDetailPageHeader.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }).isRequired,
+};
+
+BudgetStatusBadge.propTypes = {
+ badgeVariant: PropTypes.string.isRequired,
+ status: PropTypes.string.isRequired,
+ term: PropTypes.string.isRequired,
+ date: PropTypes.string.isRequired,
};
export default connect(mapStateToProps)(BudgetDetailPageHeader);
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
index 761fa1085d..78d32ef5f4 100644
--- a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
@@ -1,7 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { connect } from 'react-redux';
import {
- Button, Col, Hyperlink, ProgressBar, Row, Stack,
+ Button, Col, Hyperlink, ProgressBar, Row, Stack, useMediaQuery, breakpoints,
} from '@edx/paragon';
import { Add } from '@edx/paragon/icons';
import { generatePath, useRouteMatch, Link } from 'react-router-dom';
@@ -40,13 +42,15 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
const routeMatch = useRouteMatch();
const supportUrl = configuration.ENTERPRISE_SUPPORT_URL;
+ const isLargeScreenOrGreater = useMediaQuery({ query: `(min-width: ${breakpoints.small.minWidth}px)` });
+
if (!isAssignable) {
return (
-
+
Get people learning using this budget
- Funds from this budget are set to autoallocate to registered learners based on
+ Funds from this budget are set to auto-allocate to registered learners based on
settings configured with your support team.
@@ -58,8 +62,8 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
}
return (
-
-
+
+
Get people learning using this budget
(
+const BudgetDetailPageOverviewAvailability = ({
+ budgetId,
+ isAssignable,
+ budgetTotalSummary: { available, utilized, limit },
+ enterpriseFeatures,
+}) => (
@@ -98,7 +101,7 @@ const BudgetDetailPageOverviewAvailability = (
@@ -115,6 +118,13 @@ BudgetDetailPageOverviewAvailability.propTypes = {
budgetId: PropTypes.string.isRequired,
budgetTotalSummary: PropTypes.shape(budgetTotalSummaryShape).isRequired,
isAssignable: PropTypes.bool.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }).isRequired,
};
-export default BudgetDetailPageOverviewAvailability;
+const mapStateToProps = state => ({
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
+});
+
+export default connect(mapStateToProps)(BudgetDetailPageOverviewAvailability);
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
index 5fcd15022b..2276670b81 100644
--- a/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
import {
Stack, Collapsible, Row, Col, Button,
} from '@edx/paragon';
@@ -10,19 +11,18 @@ import {
} from 'react-router-dom';
import { formatPrice } from './data';
-const BudgetDetailPageOverviewUtilization = (
- {
- budgetId,
- budgetTotalSummary: { utilized },
- budgetAggregates,
- isAssignable,
- },
-) => {
+const BudgetDetailPageOverviewUtilization = ({
+ budgetId,
+ budgetTotalSummary: { utilized },
+ budgetAggregates,
+ isAssignable,
+ enterpriseFeatures,
+}) => {
const routeMatch = useRouteMatch();
const { amountAllocatedUsd, amountRedeemedUsd } = budgetAggregates;
- if (budgetId === null || utilized <= 0 || !isAssignable) {
+ if (!budgetId || !enterpriseFeatures.topDownAssignmentRealTimeLcm || utilized <= 0 || !isAssignable) {
return null;
}
@@ -67,19 +67,17 @@ const BudgetDetailPageOverviewUtilization = (
Your total utilization includes both assigned funds (earmarked for future enrollment) and spent
funds (redeemed for enrollment).
-
+
Amount assigned
{formatPrice(amountAllocatedUsd)}
- {
- renderActivityLink({
- amount: amountAllocatedUsd,
- type: 'assigned',
- })
- }
+ {renderActivityLink({
+ amount: amountAllocatedUsd,
+ type: 'assigned',
+ })}
@@ -88,12 +86,10 @@ const BudgetDetailPageOverviewUtilization = (
{formatPrice(amountRedeemedUsd)}
- {
- renderActivityLink({
- amount: amountRedeemedUsd,
- type: 'spent',
- })
- }
+ {renderActivityLink({
+ amount: amountRedeemedUsd,
+ type: 'spent',
+ })}
@@ -122,6 +118,13 @@ BudgetDetailPageOverviewUtilization.propTypes = {
budgetTotalSummary: PropTypes.shape(budgetTotalSummaryShape).isRequired,
budgetAggregates: PropTypes.shape(budgetAggregatesShape).isRequired,
isAssignable: PropTypes.bool.isRequired,
+ enterpriseFeatures: PropTypes.shape({
+ topDownAssignmentRealTimeLcm: PropTypes.bool,
+ }).isRequired,
};
-export default BudgetDetailPageOverviewUtilization;
+const mapStateToProps = state => ({
+ enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
+});
+
+export default connect(mapStateToProps)(BudgetDetailPageOverviewUtilization);
diff --git a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
index 41904c2938..222c9ff0f3 100644
--- a/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageWrapper.jsx
@@ -10,14 +10,28 @@ const PAGE_TITLE = 'Learner Credit Management';
export const BudgetDetailPageContext = React.createContext();
+function getBudgetDisplayName({
+ subsidyAccessPolicy,
+ enterpriseOffer,
+}) {
+ let displayName = 'Overview';
+ if (subsidyAccessPolicy?.displayName) {
+ displayName = subsidyAccessPolicy.displayName;
+ } else if (enterpriseOffer?.displayName) {
+ displayName = enterpriseOffer.displayName;
+ }
+ return displayName;
+}
+
const BudgetDetailPageWrapper = ({
subsidyAccessPolicy,
+ enterpriseOffer,
includeHero,
children,
}) => {
// display name is an optional field, and may not be set for all budgets so fallback to "Overview"
// similar to the display name logic for budgets on the overview page route.
- const budgetDisplayName = subsidyAccessPolicy?.displayName || 'Overview';
+ const budgetDisplayName = getBudgetDisplayName({ subsidyAccessPolicy, enterpriseOffer });
const helmetPageTitle = budgetDisplayName ? `${budgetDisplayName} - ${PAGE_TITLE}` : PAGE_TITLE;
const successfulAssignmentToast = useSuccessfulAssignmentToastContextValue();
@@ -49,9 +63,7 @@ const BudgetDetailPageWrapper = ({
}), [successfulAssignmentToast, successfulCancellationToast, successfulReminderToast]);
return (
-
+
{includeHero && }
@@ -90,13 +102,19 @@ const BudgetDetailPageWrapper = ({
BudgetDetailPageWrapper.propTypes = {
children: PropTypes.node.isRequired,
- subsidyAccessPolicy: PropTypes.shape(),
+ subsidyAccessPolicy: PropTypes.shape({
+ displayName: PropTypes.string,
+ }),
+ enterpriseOffer: PropTypes.shape({
+ displayName: PropTypes.string,
+ }),
includeHero: PropTypes.bool,
};
BudgetDetailPageWrapper.defaultProps = {
includeHero: true,
subsidyAccessPolicy: undefined,
+ enterpriseOffer: undefined,
};
export default BudgetDetailPageWrapper;
diff --git a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
index 7fe5d41fe8..73a165f07c 100644
--- a/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
+++ b/src/components/learner-credit-management/BudgetDetailRedemptions.jsx
@@ -39,14 +39,15 @@ const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
Spent
Spent activity is driven by completed enrollments.{' '}
-
- Learn more
-
- {(enterpriseOfferId || (subsidyAccessPolicyId && !enterpriseFeatures.topDownAssignmentRealTimeLcm)) && (
+ {(enterpriseOfferId || (subsidyAccessPolicyId && !enterpriseFeatures.topDownAssignmentRealTimeLcm)) ? (
<>
Enrollment data is automatically updated every 12 hours.
Come back later to view more recent enrollments.
>
+ ) : (
+
+ Learn more
+
)}
{
- if (!subsidySummary) { return null; }
+const transformSubsidySummaryToPolicy = (subsidySummary, enterpriseOfferMetadata) => {
+ // Check whether Enterprise Offer metadata is still fetching from the ecommerce API
+ if (!enterpriseOfferMetadata) {
+ return null;
+ }
- return {
- displayName: metadata.displayName,
- subsidyActiveDatetime: metadata.startDatetime,
- subsidyExpirationDatetime: metadata.endDatetime,
- aggregates: {
- spendAvailableUsd: subsidySummary.remainingBalance,
- amountAllocatedUsd: 0,
- amountRedeemedUsd: subsidySummary.amountOfOfferSpent,
- },
- spendLimit: subsidySummary.maxDiscount * 100,
+ const transformedData = {
+ displayName: enterpriseOfferMetadata.displayName,
+ subsidyActiveDatetime: enterpriseOfferMetadata.startDatetime,
+ subsidyExpirationDatetime: enterpriseOfferMetadata.endDatetime,
isAssignable: false,
};
+ if (subsidySummary) {
+ transformedData.spendLimit = subsidySummary.totalFunds * 100;
+ transformedData.aggregates = {
+ spendAvailableUsd: subsidySummary.remainingFunds,
+ amountAllocatedUsd: 0,
+ amountRedeemedUsd: subsidySummary.redeemedFunds,
+ };
+ }
+ return transformedData;
};
const assignBudgetStatus = (policy) => {
@@ -30,44 +36,54 @@ const assignBudgetStatus = (policy) => {
};
};
-const assignBudgetDetails = (policy) => {
+const assignBudgetDetails = (policy, isTopDownAssignmentEnabled) => {
+ if (!policy.aggregates) {
+ return {};
+ }
+
const { spendAvailableUsd, amountAllocatedUsd, amountRedeemedUsd } = policy.aggregates;
const available = spendAvailableUsd;
const limit = policy.spendLimit / 100;
- const utilized = policy.isAssignable
+ const utilized = policy.isAssignable && isTopDownAssignmentEnabled
? (amountAllocatedUsd + amountRedeemedUsd)
: amountRedeemedUsd;
- return { budgetTotalSummary: { available, limit, utilized } };
+ return {
+ budgetTotalSummary: {
+ available,
+ limit,
+ utilized,
+ },
+ };
};
const useBudgetDetailHeaderData = ({
- subsidyAccessPolicy, subsidySummary, budgetId, enterpriseOfferMetadata,
+ subsidyAccessPolicy,
+ subsidySummary,
+ budgetId,
+ enterpriseOfferMetadata,
+ isTopDownAssignmentEnabled,
}) => {
const policy = subsidyAccessPolicy || transformSubsidySummaryToPolicy(subsidySummary, enterpriseOfferMetadata);
- if (policy == null) {
- return {};
- }
-
- const defaultData = {
+ const transformedPolicyData = {
budgetId,
budgetTotalSummary: {
available: 0,
utilized: 0,
limit: 0,
},
- budgetAggregates: policy.aggregates || {},
- isAssignable: policy.isAssignable || false,
- budgetDisplayName: policy.displayName || 'Overview',
+ budgetAggregates: policy?.aggregates || {},
+ isAssignable: policy?.isAssignable || false,
+ budgetDisplayName: policy?.displayName || 'Overview',
};
- return {
- ...defaultData,
- ...assignBudgetStatus(policy),
- ...assignBudgetDetails(policy),
- };
+ if (policy) {
+ Object.assign(transformedPolicyData, assignBudgetStatus(policy));
+ Object.assign(transformedPolicyData, assignBudgetDetails(policy, isTopDownAssignmentEnabled));
+ }
+ return transformedPolicyData;
};
export default useBudgetDetailHeaderData;
diff --git a/src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js b/src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js
index bb3ed2fbf5..1377efbf8d 100644
--- a/src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js
+++ b/src/components/learner-credit-management/data/hooks/useBudgetRedemptions.js
@@ -105,7 +105,9 @@ const useBudgetRedemptions = (
setBudgetRedemptions({
itemCount: data.count,
- pageCount: data.numPages,
+ // If the data comes from the subsidy transactions endpoint, the number of pages is calculated
+ // TODO: https://2u-internal.atlassian.net/browse/ENT-8106
+ pageCount: data.numPages ?? Math.floor(data.count / options.pageSize),
results: transformedTableResults,
});
if (shouldTrackFetchEvents.current) {
diff --git a/src/components/learner-credit-management/data/tests/constants.js b/src/components/learner-credit-management/data/tests/constants.js
index 69503fbc46..f2e7b2dbe0 100644
--- a/src/components/learner-credit-management/data/tests/constants.js
+++ b/src/components/learner-credit-management/data/tests/constants.js
@@ -94,31 +94,18 @@ export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
};
export const mockSubsidySummary = {
- offerId: '84014',
- budgets: [],
- enterpriseCustomerUuid: '852eac48-b5a9-4849-8490-743f3f2deabf',
- enterpriseName: 'Executive Education (2U) Integration QA',
- sumAmountLearnerPaid: 0.0,
- sumCoursePrice: 0.0,
- status: 'Open',
+ budgetsSummary: [],
+ offerId: '1234',
offerType: 'Site',
- dateCreated: '2022-09-23T12:37:32Z',
- emailsForUsageAlert: '',
- discountType: 'percent_discount',
- discountValue: 100.0,
- maxDiscount: 50000.0,
- totalDiscountEcommerce: 42024.0,
- amountOfOfferSpent: 0.0,
- percentOfOfferSpent: 0.0,
- remainingBalance: 50000.0,
- amountOfferSpentOcm: 0.0,
- amountOfferSpentExecEd: 0.0,
- exportTimestamp: '2023-12-04T06:47:54Z',
+ percentUtilized: 0.5,
+ redeemedFunds: 500,
+ remainingFunds: 500,
+ totalFunds: 1000,
};
export const mockEnterpriseOfferMetadata = {
id: 99511,
startDatetime: '2022-09-01T00:00:00Z',
endDatetime: '2024-09-01T00:00:00Z',
- displayName: 'Test enterprise',
+ displayName: 'Test Display Name',
};
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 9eb2dda1a4..4d4475cd78 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -127,6 +127,7 @@ const mockLearnerContentAssignment = {
recentAction: { actionType: 'assigned', timestamp: '2023-10-27' },
actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction],
errorReason: null,
+ assignmentConfiguration: expect.any(Object),
};
const createMockLearnerContentAssignment = () => ({
...mockLearnerContentAssignment,
@@ -343,18 +344,23 @@ describe(' ', () => {
{
subsidyAccessPolicy: null,
subsidySummary: mockSubsidySummary,
+ enterpriseOfferMetadata: mockEnterpriseOfferMetadata,
expected: {
displayName: mockEnterpriseOfferMetadata.displayName,
- spend: formatPrice(mockSubsidySummary.remainingBalance),
- utilized: formatPrice(mockSubsidySummary.amountOfOfferSpent),
- limit: formatPrice(mockSubsidySummary.maxDiscount),
+ spend: formatPrice(mockSubsidySummary.remainingFunds),
+ utilized: formatPrice(mockSubsidySummary.redeemedFunds),
+ limit: formatPrice(mockSubsidySummary.totalFunds),
allocated: formatPrice(0),
- redeemed: formatPrice(mockSubsidySummary.amountOfOfferSpent),
+ redeemed: formatPrice(mockSubsidySummary.redeemedFunds),
},
isLoading: false,
},
])('render budget banner data (%s)', async ({
- subsidyAccessPolicy, subsidySummary, expected, isLoading,
+ subsidyAccessPolicy,
+ subsidySummary,
+ enterpriseOfferMetadata,
+ expected,
+ isLoading,
}) => {
useParams.mockReturnValue({
budgetId: 'a52e6548-649f-4576-b73f-c5c2bee25e9c',
@@ -378,7 +384,7 @@ describe(' ', () => {
});
useEnterpriseOffer.mockReturnValue({
isLoading: false,
- data: mockEnterpriseOfferMetadata,
+ data: enterpriseOfferMetadata,
});
useBudgetRedemptions.mockReturnValue({
isLoading: false,
@@ -415,7 +421,7 @@ describe(' ', () => {
}
}
- if ((subsidySummary || subsidySummary) && !isLoading) {
+ if ((subsidyAccessPolicy || subsidySummary) && !isLoading) {
expect(screen.getByText(expected.displayName, { selector: 'h2' }));
expect(screen.getByTestId('budget-detail-available')).toHaveTextContent(expected.spend);
From 6d8ca372a345768a13af4b3f667f05f58aa58344 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Mon, 11 Dec 2023 13:28:58 -0500
Subject: [PATCH 108/124] fix: update UI padding (#1133)
* fix: update UI padding
* chore: testing updates
* chore: PR feedback
---
.../SubBudgetCard.jsx | 17 +-
.../tests/BudgetCard.test.jsx | 297 +++++++++++++++++-
2 files changed, 300 insertions(+), 14 deletions(-)
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
index acd5a33560..ff81c89f0c 100644
--- a/src/components/learner-credit-management/SubBudgetCard.jsx
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
+import classNames from 'classnames';
import {
Card,
Button,
@@ -42,6 +43,7 @@ const SubBudgetCard = ({
data-testid="view-budget"
as={Link}
to={`/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}/${budgetId}`}
+ variant={budgetLabel.status === BUDGET_STATUSES.expired ? 'outline-primary' : 'primary'}
>
View budget
@@ -62,10 +64,13 @@ const SubBudgetCard = ({
title={
{budgetType} }
subtitle={
{subtitle} }
actions={
- budgetLabel.status !== BUDGET_STATUSES.scheduled
- ? renderActions(budgetId)
- : undefined
- }
+ budgetLabel.status !== BUDGET_STATUSES.scheduled
+ ? renderActions(budgetId)
+ : undefined
+ }
+ className={classNames('align-items-center', {
+ 'mb-4.5': budgetLabel.status !== BUDGET_STATUSES.active,
+ })}
/>
);
};
@@ -106,9 +111,9 @@ const SubBudgetCard = ({
isLoading={isLoading}
>
-
+
{renderCardHeader(displayName || 'Overview', id)}
- {budgetLabel.status !== BUDGET_STATUSES.scheduled && renderCardSection()}
+ {budgetLabel.status === BUDGET_STATUSES.active && renderCardSection()}
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index b030cb6d17..4594de1b00 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -76,7 +76,140 @@ describe('
', () => {
jest.clearAllMocks();
});
- it('displays correctly for Enterprise Offers (ecommerce)', () => {
+ it('displays correctly for a scheduled Enterprise Offers (ecommerce)', () => {
+ const mockBudget = {
+ id: mockEnterpriseOfferId,
+ name: mockBudgetDisplayName,
+ start: '3022-01-01',
+ end: '3023-01-01',
+ source: BUDGET_TYPES.ecommerce,
+ };
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {
+ totalFunds: mockBudgetAggregates.total,
+ redeemedFunds: mockBudgetAggregates.spent,
+ remainingFunds: mockBudgetAggregates.available,
+ percentUtilized: mockBudgetAggregates.spent / mockBudgetAggregates.total,
+ offerType: 'Site',
+ offerId: mockEnterpriseOfferId,
+ budgetsSummary: [],
+ },
+ });
+
+ render(
);
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Starts ${dayjs(mockBudget.start).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+ });
+
+ it('displays correctly for a scheduled Subsidy (enterprise-subsidy)', () => {
+ const mockBudget = {
+ id: mockEnterpriseOfferId,
+ name: mockBudgetDisplayName,
+ start: '3022-01-01',
+ end: '4023-01-01',
+ source: BUDGET_TYPES.subsidy,
+ };
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {
+ totalFunds: mockBudgetAggregates.total,
+ redeemedFunds: mockBudgetAggregates.spent,
+ remainingFunds: mockBudgetAggregates.available,
+ percentUtilized: mockBudgetAggregates.spent / mockBudgetAggregates.total,
+ offerType: 'Site',
+ offerId: mockEnterpriseOfferId,
+ budgetsSummary: [
+ {
+ id: 'test-subsidy-uuid',
+ start: '3022-01-01',
+ end: '4023-01-01',
+ remainingFunds: mockBudgetAggregates.available,
+ redeemedFunds: mockBudgetAggregates.spent,
+ enterpriseSlug,
+ subsidyAccessPolicyDisplayName: mockBudgetDisplayName,
+ subsidyAccessPolicyUuid: mockBudgetUuid,
+ },
+ ],
+ },
+ });
+
+ render(
);
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Starts ${dayjs(mockBudget.start).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+ });
+
+ it.each([
+ { isAssignableBudget: false },
+ { isAssignableBudget: true },
+ ])('displays correctly for a scheduled Policy (enterprise-access) (%s)', ({ isAssignableBudget }) => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ pending: 100,
+ available: isAssignableBudget ? 4700 : 4800,
+ };
+ const mockBudget = {
+ id: mockBudgetUuid,
+ name: mockBudgetDisplayName,
+ start: '3022-01-01',
+ end: '4023-01-01',
+ source: BUDGET_TYPES.policy,
+ aggregates: {
+ available: mockBudgetAggregates.available,
+ pending: isAssignableBudget ? mockBudgetAggregates.pending : undefined,
+ spent: mockBudgetAggregates.spent,
+ },
+ isAssignable: isAssignableBudget,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: undefined,
+ });
+
+ render(
);
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Starts ${dayjs(mockBudget.start).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+ });
+
+ it('displays correctly for an expired Enterprise Offers (ecommerce)', () => {
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
@@ -119,6 +252,154 @@ describe('
', () => {
const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
expect(viewBudgetCTA).toBeInTheDocument();
expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockEnterpriseOfferId}`);
+ });
+
+ it('displays correctly for an expired Subsidy (enterprise-subsidy)', () => {
+ const mockBudget = {
+ id: mockEnterpriseOfferId,
+ name: mockBudgetDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ source: BUDGET_TYPES.subsidy,
+ };
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {
+ totalFunds: mockBudgetAggregates.total,
+ redeemedFunds: mockBudgetAggregates.spent,
+ remainingFunds: mockBudgetAggregates.available,
+ percentUtilized: mockBudgetAggregates.spent / mockBudgetAggregates.total,
+ offerType: 'Site',
+ offerId: mockEnterpriseOfferId,
+ budgetsSummary: [
+ {
+ id: 'test-subsidy-uuid',
+ start: '2022-01-01',
+ end: '2022-01-01',
+ remainingFunds: mockBudgetAggregates.available,
+ redeemedFunds: mockBudgetAggregates.spent,
+ enterpriseSlug,
+ subsidyAccessPolicyDisplayName: mockBudgetDisplayName,
+ subsidyAccessPolicyUuid: mockBudgetUuid,
+ },
+ ],
+ },
+ });
+
+ render(
);
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+
+ // View budget CTA
+ const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
+ expect(viewBudgetCTA).toBeInTheDocument();
+ expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockBudgetUuid}`);
+ });
+
+ it.each([
+ { isAssignableBudget: false },
+ { isAssignableBudget: true },
+ ])('displays correctly for an expired Policy (enterprise-access) (%s)', ({ isAssignableBudget }) => {
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ pending: 100,
+ available: isAssignableBudget ? 4700 : 4800,
+ };
+ const mockBudget = {
+ id: mockBudgetUuid,
+ name: mockBudgetDisplayName,
+ start: '2022-01-01',
+ end: '2023-01-01',
+ source: BUDGET_TYPES.policy,
+ aggregates: {
+ available: mockBudgetAggregates.available,
+ pending: isAssignableBudget ? mockBudgetAggregates.pending : undefined,
+ spent: mockBudgetAggregates.spent,
+ },
+ isAssignable: isAssignableBudget,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: undefined,
+ });
+
+ render(
);
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+
+ // View budget CTA
+ const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
+ expect(viewBudgetCTA).toBeInTheDocument();
+ expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockBudgetUuid}`);
+ });
+
+ it('displays correctly for a current Enterprise Offers (ecommerce)', () => {
+ const mockBudget = {
+ id: mockEnterpriseOfferId,
+ name: mockBudgetDisplayName,
+ start: '2022-01-01',
+ end: '3022-01-01',
+ source: BUDGET_TYPES.ecommerce,
+ };
+ const mockBudgetAggregates = {
+ total: 5000,
+ spent: 200,
+ available: 4800,
+ };
+ useSubsidySummaryAnalyticsApi.mockReturnValue({
+ isLoading: false,
+ subsidySummary: {
+ totalFunds: mockBudgetAggregates.total,
+ redeemedFunds: mockBudgetAggregates.spent,
+ remainingFunds: mockBudgetAggregates.available,
+ percentUtilized: mockBudgetAggregates.spent / mockBudgetAggregates.total,
+ offerType: 'Site',
+ offerId: mockEnterpriseOfferId,
+ budgetsSummary: [],
+ },
+ });
+
+ render(
);
+
+ expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
+ const formattedString = `Expires ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const elementsWithTestId = screen.getAllByTestId('budget-date');
+ const firstElementWithTestId = elementsWithTestId[0];
+ expect(firstElementWithTestId).toHaveTextContent(formattedString);
+
+ // View budget CTA
+ const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
+ expect(viewBudgetCTA).toBeInTheDocument();
+ expect(viewBudgetCTA).toHaveAttribute('href', `/${enterpriseSlug}/admin/learner-credit/${mockEnterpriseOfferId}`);
// Aggregates
expect(screen.getByText('Balance')).toBeInTheDocument();
@@ -128,12 +409,12 @@ describe('
', () => {
expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
});
- it('displays correctly for Subsidy (enterprise-subsidy)', () => {
+ it('displays correctly for a current Subsidy (enterprise-subsidy)', () => {
const mockBudget = {
id: mockEnterpriseOfferId,
name: mockBudgetDisplayName,
start: '2022-01-01',
- end: '2023-01-01',
+ end: '3023-01-01',
source: BUDGET_TYPES.subsidy,
};
const mockBudgetAggregates = {
@@ -154,7 +435,7 @@ describe('
', () => {
{
id: 'test-subsidy-uuid',
start: '2022-01-01',
- end: '2022-01-01',
+ end: '3023-01-01',
remainingFunds: mockBudgetAggregates.available,
redeemedFunds: mockBudgetAggregates.spent,
enterpriseSlug,
@@ -173,7 +454,7 @@ describe('
', () => {
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
- const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const formattedString = `Expires ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
const elementsWithTestId = screen.getAllByTestId('budget-date');
const firstElementWithTestId = elementsWithTestId[0];
expect(firstElementWithTestId).toHaveTextContent(formattedString);
@@ -194,7 +475,7 @@ describe('
', () => {
it.each([
{ isAssignableBudget: false },
{ isAssignableBudget: true },
- ])('displays correctly for Policy (enterprise-access) (%s)', ({ isAssignableBudget }) => {
+ ])('displays correctly for a current Policy (enterprise-access) (%s)', ({ isAssignableBudget }) => {
const mockBudgetAggregates = {
total: 5000,
spent: 200,
@@ -205,7 +486,7 @@ describe('
', () => {
id: mockBudgetUuid,
name: mockBudgetDisplayName,
start: '2022-01-01',
- end: '2023-01-01',
+ end: '3023-01-01',
source: BUDGET_TYPES.policy,
aggregates: {
available: mockBudgetAggregates.available,
@@ -227,7 +508,7 @@ describe('
', () => {
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();
- const formattedString = `Expired ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
+ const formattedString = `Expires ${dayjs(mockBudget.end).format('MMMM D, YYYY')}`;
const elementsWithTestId = screen.getAllByTestId('budget-date');
const firstElementWithTestId = elementsWithTestId[0];
expect(firstElementWithTestId).toHaveTextContent(formattedString);
From 9d98bdd2db48ccdcf43f4f7ad8c62899550936c5 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Mon, 11 Dec 2023 13:53:44 -0500
Subject: [PATCH 109/124] fix: replace pending with assigned in budget cards
(#1134)
---
src/components/learner-credit-management/SubBudgetCard.jsx | 2 +-
.../learner-credit-management/tests/BudgetCard.test.jsx | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/learner-credit-management/SubBudgetCard.jsx b/src/components/learner-credit-management/SubBudgetCard.jsx
index ff81c89f0c..6261d3762a 100644
--- a/src/components/learner-credit-management/SubBudgetCard.jsx
+++ b/src/components/learner-credit-management/SubBudgetCard.jsx
@@ -89,7 +89,7 @@ const SubBudgetCard = ({
{isAssignable && (
- Pending
+ Assigned
{isFetchingBudgets ? : formatPrice(pending)}
diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
index 4594de1b00..f146dc3111 100644
--- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx
@@ -523,10 +523,10 @@ describe(' ', () => {
expect(screen.getByText('Available')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.available))).toBeInTheDocument();
if (isAssignableBudget) {
- expect(screen.getByText('Pending')).toBeInTheDocument();
+ expect(screen.getByText('Assigned')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.pending))).toBeInTheDocument();
} else {
- expect(screen.queryByText('Pending')).not.toBeInTheDocument();
+ expect(screen.queryByText('Assigned')).not.toBeInTheDocument();
}
expect(screen.getByText('Spent')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
From 247985ba761d2dccf00753f7cd2588c5f9a2d07c Mon Sep 17 00:00:00 2001
From: Troy Sankey
Date: Wed, 13 Dec 2023 11:49:42 -0800
Subject: [PATCH 110/124] feat: add assignment status chip for a failed
redemption
This is the frontend component corresponding to:
https://github.com/openedx/enterprise-access/pull/364
Before this change, failed redemptions would result in the frontend
displaying a "Failed: System" chip, which is a generic fallback. After
this change, an explicit "Failed: Redemption" chip is displayed.
ENT-8105
---
.../AssignmentStatusTableCell.jsx | 12 +++-
.../FailedRedemption.jsx | 59 +++++++++++++++++++
.../tests/BudgetDetailPage.test.jsx | 23 ++++++++
3 files changed, 92 insertions(+), 2 deletions(-)
create mode 100644 src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
index af24ed54fb..e6c104ab52 100644
--- a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -4,6 +4,7 @@ import {
import PropTypes from 'prop-types';
import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
import FailedCancellation from './assignments-status-chips/FailedCancellation';
+import FailedRedemption from './assignments-status-chips/FailedRedemption';
import FailedReminder from './assignments-status-chips/FailedReminder';
import FailedSystem from './assignments-status-chips/FailedSystem';
import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
@@ -36,17 +37,19 @@ const AssignmentStatusTableCell = ({ row }) => {
}
if (learnerState === 'failed') {
- // If learnerState is failed but no error reason is defined, return a failed system chip.
+ // If learnerState is failed but no top-level error reason is defined, return a failed system chip.
if (!errorReason) {
return ;
}
- // Determine which failure chip to display based on the error reason.
+ // Determine which failure chip to display based on the top level errorReason. In most cases, the actual errorReason
+ // code is ignored, in which case we key off the actionType.
if (errorReason.actionType === 'notified') {
if (errorReason.errorReason === 'email_error') {
return (
);
}
+ // non-email errors on failed notifications should NOT use the FailedBadEmail chip.
return ;
}
if (errorReason.actionType === 'cancelled') {
@@ -55,6 +58,11 @@ const AssignmentStatusTableCell = ({ row }) => {
if (errorReason.actionType === 'reminded') {
return ;
}
+ if (errorReason.actionType === 'redeemed') {
+ return ;
+ }
+ // In all other unexpected cases, return a failed system chip.
+ return ;
}
// Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx
new file mode 100644
index 0000000000..6e12f9303f
--- /dev/null
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx
@@ -0,0 +1,59 @@
+import React, { useState } from 'react';
+import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import { Error } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
+
+import BaseModalPopup from './BaseModalPopup';
+
+const FailedRedemption = () => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = useState(null);
+
+ return (
+ <>
+
+ Failed: Redemption
+
+
+
+ Failed: Redemption
+
+
+
+ Something went wrong behind the scenes when the learner attempted to redeem this course assignment.
+ Associated Learner credit funds have been released into your available balance.
+
+
+
Resolution steps
+
+
+ Try assigning this content to the learner again.
+
+
+ If the issue continues, contact customer support.
+
+
+ Get more troubleshooting help at{' '}
+
+ Help Center: Course Assignments
+ .
+
+
+
+
+
+ >
+ );
+};
+
+export default FailedRedemption;
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 4d4475cd78..afee771b75 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -167,6 +167,12 @@ const mockFailedReminderLearnerAction = {
errorReason: 'email_error',
};
+const mockFailedRedemptionLearnerAction = {
+ actionType: 'redeemed',
+ completedAt: null,
+ errorReason: 'enrollment_error',
+};
+
const defaultEnterpriseSubsidiesContextValue = {
isLoading: false,
};
@@ -1046,6 +1052,8 @@ describe(' ', () => {
actionType: 'notified',
},
},
+ // This test case is weird because we always serialize the latest failed action into error_reason in the assignment
+ // API response. Nevertheless, keep it in just to cover potential backend serializer bugs.
{
learnerState: 'failed',
hasLearnerEmail: true,
@@ -1091,6 +1099,21 @@ describe(' ', () => {
actionType: 'reminded',
},
},
+ {
+ learnerState: 'failed',
+ hasLearnerEmail: true,
+ expectedChipStatus: 'Failed: Redemption',
+ expectedModalPopupHeading: 'Failed: Redemption',
+ expectedModalPopupContent: (
+ 'Something went wrong behind the scenes when the learner attempted to redeem this course assignment. '
+ + 'Associated Learner credit funds have been released into your available balance.'
+ ),
+ actions: [mockSuccessfulLinkedLearnerAction, mockSuccessfulNotifiedAction, mockFailedRedemptionLearnerAction],
+ errorReason: {
+ actionType: mockFailedRedemptionLearnerAction.actionType,
+ errorReason: mockFailedRedemptionLearnerAction.errorReason,
+ },
+ },
])('renders correct status chips with assigned table data (%s)', ({
learnerState,
hasLearnerEmail,
From 4a03e0e7aa84e69453e64b5877743e51d3ee606f Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Wed, 13 Dec 2023 16:44:23 -0500
Subject: [PATCH 111/124] fix: update variant on new assignment button (#1137)
---
.../BudgetDetailPageOverviewAvailability.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
index 78d32ef5f4..8fca7681f6 100644
--- a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
@@ -66,7 +66,7 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
Get people learning using this budget
Date: Tue, 12 Dec 2023 16:58:05 -0500
Subject: [PATCH 112/124] fix: support remind/cancel-all
---
README.md | 25 ++++++++++++
package.json | 1 +
.../AssignmentTableCancel.jsx | 34 +++++++++++++++--
.../AssignmentTableRemind.jsx | 38 +++++++++++++++----
.../BudgetAssignmentsTable.jsx | 2 +-
.../data/hooks/useCancelContentAssignments.js | 9 ++++-
.../data/hooks/useRemindContentAssignments.js | 9 ++++-
.../tests/BudgetDetailPage.test.jsx | 36 ++++++++++++------
.../services/EnterpriseAccessApiService.js | 16 ++++++++
.../tests/EnterpriseAccessApiService.test.js | 16 +++++++-
10 files changed, 158 insertions(+), 28 deletions(-)
diff --git a/README.md b/README.md
index 10d490cbd5..4a49acd0ac 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,31 @@ module.exports = {
NB: In order for webpack to properly resolve scss imports locally, you must use a `~` before the import, like so: `@import "~@edx/brand/paragon/fonts";`
+### Running tests
+
+You can run all tests as follows:
+```
+nvm use
+npm install
+npm run test
+```
+
+Additionally, you can run a single test file with
+```
+npm run test -- ProvisioningFormSubmissionButton.test.jsx
+```
+
+or run a given test function by appending a `.only` to the test function (or appending `.skip` to do the inverse).
+For example: `test.only('your test function', () => {...})`.
+
+You can use watch mode with tests as follows:
+```
+npm run test:watch BudgetDetailPage.test.jsx
+# or to skip coverage reporting
+npm run test:watch-no-cov BudgetDetailpage.test.jsx
+```
+Note the watcher has its own set of commands to help run test functions that match a regex (`t my regex`), etc.
+Use the `w` command to get a list of valid watch commands.
## Getting Help
diff --git a/package.json b/package.json
index 3d60221c8c..5e92b523b4 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"debug-test": "node --inspect-brk node_modules/.bin/jest --runInBand --coverage",
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests --maxWorkers=50%",
"test:watch": "npm run test -- --watch",
+ "test:watch-no-cov": "npm run test -- --watch --collectCoverage=false",
"snapshot": "fedx-scripts jest --updateSnapshot"
},
"license": "AGPL-3.0",
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
index a160934ef4..86c5580115 100644
--- a/src/components/learner-credit-management/AssignmentTableCancel.jsx
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -5,7 +5,18 @@ import { DoNotDisturbOn } from '@edx/paragon/icons';
import CancelAssignmentModal from './CancelAssignmentModal';
import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
-const AssignmentTableCancelAction = ({ selectedFlatRows }) => {
+const calculateTotalToCancel = ({
+ assignmentUuids,
+ isEntireTableSelected,
+ tableItemCount,
+}) => {
+ if (isEntireTableSelected) {
+ return tableItemCount;
+ }
+ return assignmentUuids.length;
+};
+
+const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected, tableInstance }) => {
const assignmentUuids = selectedFlatRows.map(row => row.id);
const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
const {
@@ -14,19 +25,26 @@ const AssignmentTableCancelAction = ({ selectedFlatRows }) => {
close,
isOpen,
open,
- } = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ } = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids, isEntireTableSelected);
+
+ const tableItemCount = tableInstance.itemCount;
+ const totalToCancel = calculateTotalToCancel({
+ assignmentUuids,
+ isEntireTableSelected,
+ tableItemCount,
+ });
return (
<>
- {`Cancel (${assignmentUuids.length})`}
+ {`Cancel (${totalToCancel})`}
>
);
@@ -34,10 +52,18 @@ const AssignmentTableCancelAction = ({ selectedFlatRows }) => {
AssignmentTableCancelAction.propTypes = {
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
+ isEntireTableSelected: PropTypes.bool,
+ tableInstance: PropTypes.shape({
+ itemCount: PropTypes.number.isRequired,
+ }),
};
AssignmentTableCancelAction.defaultProps = {
selectedFlatRows: [],
+ isEntireTableSelected: false,
+ tableInstance: {
+ itemCount: 0,
+ },
};
export default AssignmentTableCancelAction;
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
index e63fbfac72..56f5d7378b 100644
--- a/src/components/learner-credit-management/AssignmentTableRemind.jsx
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -5,33 +5,51 @@ import { Mail } from '@edx/paragon/icons';
import useRemindContentAssignments from './data/hooks/useRemindContentAssignments';
import RemindAssignmentModal from './RemindAssignmentModal';
-const AssignmentTableRemindAction = ({ selectedFlatRows }) => {
+const calculateTotalToRemind = ({
+ assignmentUuids,
+ isEntireTableSelected,
+ learnerStateCounts,
+}) => {
+ if (isEntireTableSelected) {
+ const waitingAssignmentCounts = learnerStateCounts.filter(({ learnerState }) => (learnerState === 'waiting'));
+ return waitingAssignmentCounts.length ? waitingAssignmentCounts[0].count : 0;
+ }
+ return assignmentUuids.length;
+};
+
+const AssignmentTableRemindAction = ({ selectedFlatRows, isEntireTableSelected, learnerStateCounts }) => {
const assignmentUuids = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').map(({ id }) => id);
const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
- const selectedRemindableRows = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').length;
const {
remindButtonState,
remindContentAssignments,
close,
isOpen,
open,
- } = useRemindContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ } = useRemindContentAssignments(assignmentConfigurationUuid, assignmentUuids, isEntireTableSelected);
+
+ const selectedRemindableRowCount = calculateTotalToRemind({
+ assignmentUuids,
+ isEntireTableSelected,
+ learnerStateCounts,
+ });
+
return (
<>
- {`Remind (${selectedRemindableRows})`}
+ {`Remind (${selectedRemindableRowCount})`}
>
);
@@ -39,10 +57,16 @@ const AssignmentTableRemindAction = ({ selectedFlatRows }) => {
AssignmentTableRemindAction.propTypes = {
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
+ isEntireTableSelected: PropTypes.bool,
+ learnerStateCounts: PropTypes.arrayOf(PropTypes.shape({
+ learnerState: PropTypes.string.isRequired,
+ count: PropTypes.number.isRequired,
+ })).isRequired,
};
AssignmentTableRemindAction.defaultProps = {
selectedFlatRows: [],
+ isEntireTableSelected: false,
};
export default AssignmentTableRemindAction;
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 9488d2f754..0ece5dc2a2 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -117,7 +117,7 @@ const BudgetAssignmentsTable = ({
pageCount={tableData.numPages || 1}
EmptyTableComponent={CustomDataTableEmptyState}
bulkActions={[
- ,
+ ,
,
]}
/>
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
index 189462a776..a21dc68c64 100644
--- a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
@@ -10,6 +10,7 @@ import useBudgetId from './useBudgetId';
const useCancelContentAssignments = (
assignmentConfigurationUuid,
assignmentUuids,
+ cancelAll = false,
) => {
const [isOpen, open, close] = useToggle(false);
const [cancelButtonState, setCancelButtonState] = useState('default');
@@ -19,7 +20,11 @@ const useCancelContentAssignments = (
const cancelContentAssignments = useCallback(async () => {
setCancelButtonState('pending');
try {
- await EnterpriseAccessApiService.cancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ if (cancelAll) {
+ await EnterpriseAccessApiService.cancelAllContentAssignments(assignmentConfigurationUuid);
+ } else {
+ await EnterpriseAccessApiService.cancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ }
setCancelButtonState('complete');
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
@@ -28,7 +33,7 @@ const useCancelContentAssignments = (
logError(err);
setCancelButtonState('error');
}
- }, [assignmentConfigurationUuid, assignmentUuids, queryClient, subsidyAccessPolicyId]);
+ }, [assignmentConfigurationUuid, assignmentUuids, cancelAll, queryClient, subsidyAccessPolicyId]);
return {
cancelButtonState,
diff --git a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
index bc55743a30..aaeac80c36 100644
--- a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
@@ -10,6 +10,7 @@ import useBudgetId from './useBudgetId';
const useRemindContentAssignments = (
assignmentConfigurationUuid,
assignmentUuids,
+ remindAll = false,
) => {
const [isOpen, open, close] = useToggle(false);
const [remindButtonState, setRemindButtonState] = useState('default');
@@ -19,7 +20,11 @@ const useRemindContentAssignments = (
const remindContentAssignments = useCallback(async () => {
setRemindButtonState('pending');
try {
- await EnterpriseAccessApiService.remindContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ if (remindAll) {
+ await EnterpriseAccessApiService.remindAllContentAssignments(assignmentConfigurationUuid);
+ } else {
+ await EnterpriseAccessApiService.remindContentAssignments(assignmentConfigurationUuid, assignmentUuids);
+ }
setRemindButtonState('complete');
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(subsidyAccessPolicyId),
@@ -28,7 +33,7 @@ const useRemindContentAssignments = (
logError(err);
setRemindButtonState('error');
}
- }, [assignmentConfigurationUuid, assignmentUuids, queryClient, subsidyAccessPolicyId]);
+ }, [assignmentConfigurationUuid, assignmentUuids, remindAll, queryClient, subsidyAccessPolicyId]);
return {
remindButtonState,
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index afee771b75..9052c8d93c 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -1453,7 +1453,7 @@ describe(' ', () => {
});
it('cancels assignments in bulk', async () => {
- EnterpriseAccessApiService.cancelContentAssignments.mockResolvedValueOnce({ status: 200 });
+ EnterpriseAccessApiService.cancelAllContentAssignments.mockResolvedValueOnce({ status: 200 });
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
@@ -1520,16 +1520,18 @@ describe(' ', () => {
expect(modalDialog).toBeInTheDocument();
const cancelDialogButton = getButtonElement('Cancel assignments (2)');
userEvent.click(cancelDialogButton);
- expect(
- EnterpriseAccessApiService.cancelContentAssignments,
- ).toHaveBeenCalled();
+ await waitFor(
+ () => expect(
+ EnterpriseAccessApiService.cancelAllContentAssignments,
+ ).toHaveBeenCalled(),
+ );
await waitFor(
() => expect(screen.getByText('Assignments canceled (2)')).toBeInTheDocument(),
);
});
it('reminds assignments in bulk', async () => {
- EnterpriseAccessApiService.remindContentAssignments.mockResolvedValueOnce({ status: 200 });
+ EnterpriseAccessApiService.remindAllContentAssignments.mockResolvedValueOnce({ status: 202 });
useParams.mockReturnValue({
budgetId: mockSubsidyAccessPolicyUUID,
activeTabKey: 'activity',
@@ -1553,7 +1555,7 @@ describe(' ', () => {
useBudgetContentAssignments.mockReturnValue({
isLoading: false,
contentAssignments: {
- count: 2,
+ count: 3,
results: [
{
uuid: 'test-uuid1',
@@ -1575,10 +1577,20 @@ describe(' ', () => {
errorReason: null,
state: 'allocated',
},
+ {
+ uuid: 'test-uuid3',
+ contentKey: mockCourseKey,
+ contentQuantity: -29900,
+ learnerState: 'notifying',
+ recentAction: { actionType: 'assigned', timestamp: '2023-11-27' },
+ actions: [mockSuccessfulNotifiedAction],
+ errorReason: null,
+ state: 'allocated',
+ },
],
learnerStateCounts: [
- { learnerState: 'waiting', count: 1 },
- { learnerState: 'waiting', count: 1 },
+ { learnerState: 'waiting', count: 2 },
+ { learnerState: 'notifying', count: 1 },
],
numPages: 1,
currentPage: 1,
@@ -1596,9 +1608,11 @@ describe(' ', () => {
expect(modalDialog).toBeInTheDocument();
const remindDialogButton = getButtonElement('Send reminders (2)');
userEvent.click(remindDialogButton);
- expect(
- EnterpriseAccessApiService.remindContentAssignments,
- ).toHaveBeenCalled();
+ await waitFor(
+ () => expect(
+ EnterpriseAccessApiService.remindAllContentAssignments,
+ ).toHaveBeenCalled(),
+ );
await waitFor(
() => expect(screen.getByText('Reminders sent (2)')).toBeInTheDocument(),
);
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index 8a38cb2b14..7313698db4 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -184,6 +184,14 @@ class EnterpriseAccessApiService {
return EnterpriseAccessApiService.apiClient().post(url, options);
}
+ /**
+ * Cancel ALL content assignments for a specific AssignmentConfiguration.
+ */
+ static cancelAllContentAssignments(assignmentConfigurationUUID) {
+ const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/cancel-all/`;
+ return EnterpriseAccessApiService.apiClient().post(url);
+ }
+
/**
* Remind content assignments for a specific AssignmentConfiguration.
*/
@@ -195,6 +203,14 @@ class EnterpriseAccessApiService {
return EnterpriseAccessApiService.apiClient().post(url, options);
}
+ /**
+ * Remind ALL content assignments for a specific AssignmentConfiguration.
+ */
+ static remindAllContentAssignments(assignmentConfigurationUUID) {
+ const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/remind-all/`;
+ return EnterpriseAccessApiService.apiClient().post(url);
+ }
+
/**
* Retrieve a specific subsidy access policy.
* @param {string} subsidyAccessPolicyUUID The UUID of the subsidy access policy to retrieve.
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index f2007a56f3..864ef63277 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -201,7 +201,7 @@ describe('EnterpriseAccessApiService', () => {
);
});
- test('remindContentAssignments calls enterprise-access cancel POST API to remind learners', () => {
+ test('remindContentAssignments calls enterprise-access remind POST API to remind learners', () => {
const options = {
assignment_uuids: mockAssignmentUUIDs,
};
@@ -211,4 +211,18 @@ describe('EnterpriseAccessApiService', () => {
options,
);
});
+
+ test('cancelAllContentAssignments calls enterprise-access cancel-all POST API to cancel all assignments', () => {
+ EnterpriseAccessApiService.cancelAllContentAssignments(mockAssignmentConfigurationUUID);
+ expect(axios.post).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/cancel-all/`,
+ );
+ });
+
+ test('remindAllContentAssignments calls enterprise-access remind-all POST API to remind all learners', () => {
+ EnterpriseAccessApiService.remindAllContentAssignments(mockAssignmentConfigurationUUID);
+ expect(axios.post).toBeCalledWith(
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/remind-all/`,
+ );
+ });
});
From 2b52504ff25270cfbd8103b66b5202695b367abc Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Thu, 14 Dec 2023 10:43:32 -0500
Subject: [PATCH 113/124] fix: sets button variant to primary (#1139)
---
.../BudgetDetailPageOverviewAvailability.jsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
index 8fca7681f6..39fa31dbe8 100644
--- a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
@@ -66,7 +66,6 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
Get people learning using this budget
Date: Thu, 14 Dec 2023 19:54:24 -0500
Subject: [PATCH 114/124] fix: dynamic theme fixes for outline buttons and
fullscreen steppers (#1140)
---
.../ContentHighlightStepper.jsx | 2 +-
.../AssignmentStatusTableCell.jsx | 4 +--
.../BudgetAssignmentsTable.jsx | 1 +
.../FailedCancellation.jsx | 4 +--
.../FailedReminder.jsx | 4 +--
.../cards/NewAssignmentModalButton.jsx | 2 +-
src/components/settings/data/hooks.js | 32 +++++++++++--------
src/components/settings/settings.scss | 4 ---
src/index.scss | 8 ++++-
9 files changed, 31 insertions(+), 30 deletions(-)
diff --git a/src/components/ContentHighlights/HighlightStepper/ContentHighlightStepper.jsx b/src/components/ContentHighlights/HighlightStepper/ContentHighlightStepper.jsx
index f4f65cb77c..79083b316d 100644
--- a/src/components/ContentHighlights/HighlightStepper/ContentHighlightStepper.jsx
+++ b/src/components/ContentHighlights/HighlightStepper/ContentHighlightStepper.jsx
@@ -258,7 +258,7 @@ const ContentHighlightStepper = ({ enterpriseId }) => {
}
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
index e6c104ab52..d68cf57d9f 100644
--- a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -1,6 +1,4 @@
-import {
- Chip,
-} from '@edx/paragon';
+import { Chip } from '@edx/paragon';
import PropTypes from 'prop-types';
import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
import FailedCancellation from './assignments-status-chips/FailedCancellation';
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 0ece5dc2a2..3b3c7e76d1 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -47,6 +47,7 @@ const BudgetAssignmentsTable = ({
number: count,
value: learnerState,
}));
+
return (
{
return (
<>
Failed: Cancellation
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
index d3b9533bb9..033ae7bca4 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
@@ -10,13 +10,11 @@ const FailedReminder = () => {
return (
<>
Failed: Reminder
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index eb2a089129..790fc68d3f 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -177,7 +177,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
<>
{children}
{
diff --git a/src/components/settings/data/hooks.js b/src/components/settings/data/hooks.js
index e907c36a34..cb800dc143 100644
--- a/src/components/settings/data/hooks.js
+++ b/src/components/settings/data/hooks.js
@@ -132,6 +132,17 @@ export const useStylesForCustomBrandColors = (branding) => {
.btn-brand:focus:before {
border-color: ${brandColors.secondary.regular.hex()} !important;
}
+ .btn-outline-brand {
+ color: ${brandColors.secondary.textColor.hex()} !important;
+ }
+ .btn-outline-brand:hover {
+ background-color: ${brandColors.secondary.regular.hex()} !important;
+ color: ${brandColors.secondary.textColor.hex()} !important;
+ border-color: ${brandColors.secondary.regular.hex()} !important;
+ }
+ .btn-outline-brand:focus:before {
+ border-color: ${brandColors.secondary.regular.hex()} !important;
+ }
.btn-primary {
background-color: ${brandColors.primary.regular.hex()} !important;
border-color: ${brandColors.primary.regular.hex()} !important;
@@ -141,26 +152,19 @@ export const useStylesForCustomBrandColors = (branding) => {
background-color: ${brandColors.primary.dark.hex()} !important;
border-color: ${brandColors.primary.dark.hex()} !important;
}
- .btn-brand-primary {
- background-color: ${brandColors.primary.regular.hex()} !important;
+ .btn-primary:focus:before {
border-color: ${brandColors.primary.regular.hex()} !important;
- color: ${brandColors.primary.textColor.hex()} !important;
}
- .btn-brand-primary:hover {
- background-color: ${brandColors.primary.dark.hex()} !important;
- border-color: ${brandColors.primary.dark.hex()} !important;
- }
- .btn-brand-primary:focus:before {
- border-color: ${brandColors.primary.regular.hex()} !important;
+ .btn-outline-primary {
+ color: ${brandColors.primary.textColor.hex()} !important;
}
- .bg-brand-primary {
+ .btn-outline-primary:hover {
background-color: ${brandColors.primary.regular.hex()} !important;
- }
- .border-brand-primary {
+ color: ${brandColors.primary.textColor.hex()} !important;
border-color: ${brandColors.primary.regular.hex()} !important;
}
- .color-brand-tertiary {
- color: ${brandColors.tertiary.regular.hex()} !important;
+ .btn-outline-primary:focus:before {
+ border-color: ${brandColors.primary.regular.hex()} !important;
}
.secondary-background {
background: ${brandColors.secondary.regular.hex()} !important;
diff --git a/src/components/settings/settings.scss b/src/components/settings/settings.scss
index 9730540a88..683398b253 100644
--- a/src/components/settings/settings.scss
+++ b/src/components/settings/settings.scss
@@ -167,10 +167,6 @@
font-weight: 500;
}
-.stepper-modal .pgn__modal-header {
- border-bottom: solid 7px;
-}
-
.warning-icon {
color: #F0CC00;
}
diff --git a/src/index.scss b/src/index.scss
index c2bfdfb974..c3a4e794b0 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -1,13 +1,14 @@
// ... Any custom SCSS variables should be defined here
$modal-max-width: 650px;
+// Paragon/Brand
@import "~@edx/brand/paragon/fonts";
@import "~@edx/brand/paragon/variables";
@import "~@edx/paragon/scss/core/core";
@import "~@edx/frontend-enterprise-catalog-search";
@import "~@edx/brand/paragon/overrides";
-
+// Components
@import "./components/EnterpriseApp/EnterpriseApp";
@import "./components/CodeSearchResults/CodeSearchResults";
@import "./components/Coupon/Coupon";
@@ -26,6 +27,7 @@ $modal-max-width: 650px;
@import "./components/settings/settings";
@import "./components/learner-credit-management/styles";
+// Global
body {
overflow-x: hidden;
}
@@ -73,3 +75,7 @@ form {
max-width: $modal-max-width;
}
}
+
+.stepper-modal .pgn__modal-header {
+ border-bottom: solid 7px;
+}
From f2aefb60aa6dfd51a8e5e77ed78b997d7a73cfc2 Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Thu, 14 Dec 2023 20:50:30 -0500
Subject: [PATCH 115/124] fix: remove color style on outline button variants
for dynamic themes (#1141)
---
src/components/settings/data/hooks.js | 6 ------
1 file changed, 6 deletions(-)
diff --git a/src/components/settings/data/hooks.js b/src/components/settings/data/hooks.js
index cb800dc143..5dc6ac6c99 100644
--- a/src/components/settings/data/hooks.js
+++ b/src/components/settings/data/hooks.js
@@ -132,9 +132,6 @@ export const useStylesForCustomBrandColors = (branding) => {
.btn-brand:focus:before {
border-color: ${brandColors.secondary.regular.hex()} !important;
}
- .btn-outline-brand {
- color: ${brandColors.secondary.textColor.hex()} !important;
- }
.btn-outline-brand:hover {
background-color: ${brandColors.secondary.regular.hex()} !important;
color: ${brandColors.secondary.textColor.hex()} !important;
@@ -155,9 +152,6 @@ export const useStylesForCustomBrandColors = (branding) => {
.btn-primary:focus:before {
border-color: ${brandColors.primary.regular.hex()} !important;
}
- .btn-outline-primary {
- color: ${brandColors.primary.textColor.hex()} !important;
- }
.btn-outline-primary:hover {
background-color: ${brandColors.primary.regular.hex()} !important;
color: ${brandColors.primary.textColor.hex()} !important;
From 13ad64ca1d5939643201396e180ef67f17bfd44e Mon Sep 17 00:00:00 2001
From: Adam Stankiewicz
Date: Fri, 15 Dec 2023 09:53:40 -0500
Subject: [PATCH 116/124] fix: account for filters in select all bulk actions
assigned table (#1142)
---
.../AssignmentTableCancel.jsx | 26 +++++++++---------
.../AssignmentTableRemind.jsx | 27 ++++++++++++-------
src/components/subscriptions/data/utils.js | 12 ---------
.../bulk-actions/RemindBulkAction.jsx | 5 ++--
.../bulk-actions/RevokeBulkAction.jsx | 5 ++--
src/utils.js | 15 +++++++++++
6 files changed, 51 insertions(+), 39 deletions(-)
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
index 86c5580115..db155be926 100644
--- a/src/components/learner-credit-management/AssignmentTableCancel.jsx
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -4,6 +4,7 @@ import { Button } from '@edx/paragon';
import { DoNotDisturbOn } from '@edx/paragon/icons';
import CancelAssignmentModal from './CancelAssignmentModal';
import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
+import { getActiveTableColumnFilters } from '../../utils';
const calculateTotalToCancel = ({
assignmentUuids,
@@ -19,18 +20,24 @@ const calculateTotalToCancel = ({
const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected, tableInstance }) => {
const assignmentUuids = selectedFlatRows.map(row => row.id);
const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
+
+ const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
+
+ // If entire table is selected and there are NO filters, hit cancel-all endpoint. Otherwise, hit usual bulk cancel.
+ const shouldCancelAll = isEntireTableSelected && activeFilters.length === 0;
+
const {
cancelButtonState,
cancelContentAssignments,
close,
isOpen,
open,
- } = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids, isEntireTableSelected);
+ } = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids, shouldCancelAll);
const tableItemCount = tableInstance.itemCount;
const totalToCancel = calculateTotalToCancel({
assignmentUuids,
- isEntireTableSelected,
+ isEntireTableSelected: shouldCancelAll,
tableItemCount,
});
@@ -51,19 +58,12 @@ const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected,
};
AssignmentTableCancelAction.propTypes = {
- selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
- isEntireTableSelected: PropTypes.bool,
+ selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ isEntireTableSelected: PropTypes.bool.isRequired,
tableInstance: PropTypes.shape({
itemCount: PropTypes.number.isRequired,
- }),
-};
-
-AssignmentTableCancelAction.defaultProps = {
- selectedFlatRows: [],
- isEntireTableSelected: false,
- tableInstance: {
- itemCount: 0,
- },
+ columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ }).isRequired,
};
export default AssignmentTableCancelAction;
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
index 56f5d7378b..d501178a11 100644
--- a/src/components/learner-credit-management/AssignmentTableRemind.jsx
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -4,6 +4,7 @@ import { Button } from '@edx/paragon';
import { Mail } from '@edx/paragon/icons';
import useRemindContentAssignments from './data/hooks/useRemindContentAssignments';
import RemindAssignmentModal from './RemindAssignmentModal';
+import { getActiveTableColumnFilters } from '../../utils';
const calculateTotalToRemind = ({
assignmentUuids,
@@ -17,20 +18,28 @@ const calculateTotalToRemind = ({
return assignmentUuids.length;
};
-const AssignmentTableRemindAction = ({ selectedFlatRows, isEntireTableSelected, learnerStateCounts }) => {
+const AssignmentTableRemindAction = ({
+ selectedFlatRows, isEntireTableSelected, learnerStateCounts, tableInstance,
+}) => {
const assignmentUuids = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').map(({ id }) => id);
const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
+
+ const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
+
+ // If entire table is selected and there are NO filters, hit remind-all endpoint. Otherwise, hit usual bulk remind.
+ const shouldRemindAll = isEntireTableSelected && activeFilters.length === 0;
+
const {
remindButtonState,
remindContentAssignments,
close,
isOpen,
open,
- } = useRemindContentAssignments(assignmentConfigurationUuid, assignmentUuids, isEntireTableSelected);
+ } = useRemindContentAssignments(assignmentConfigurationUuid, assignmentUuids, shouldRemindAll);
const selectedRemindableRowCount = calculateTotalToRemind({
assignmentUuids,
- isEntireTableSelected,
+ isEntireTableSelected: shouldRemindAll,
learnerStateCounts,
});
@@ -56,17 +65,15 @@ const AssignmentTableRemindAction = ({ selectedFlatRows, isEntireTableSelected,
};
AssignmentTableRemindAction.propTypes = {
- selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()),
- isEntireTableSelected: PropTypes.bool,
+ selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ isEntireTableSelected: PropTypes.bool.isRequired,
learnerStateCounts: PropTypes.arrayOf(PropTypes.shape({
learnerState: PropTypes.string.isRequired,
count: PropTypes.number.isRequired,
})).isRequired,
-};
-
-AssignmentTableRemindAction.defaultProps = {
- selectedFlatRows: [],
- isEntireTableSelected: false,
+ tableInstance: PropTypes.shape({
+ columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ }).isRequired,
};
export default AssignmentTableRemindAction;
diff --git a/src/components/subscriptions/data/utils.js b/src/components/subscriptions/data/utils.js
index 2829929b24..77343f53c0 100644
--- a/src/components/subscriptions/data/utils.js
+++ b/src/components/subscriptions/data/utils.js
@@ -65,15 +65,3 @@ export const transformFiltersForRequest = (filters) => {
}),
);
};
-
-/**
- * Helper to determine which table columns have an active filter applied.
- *
- * @param {object} columns Array of column objects (e.g., { id, filter, filterValue })
- * @returns Array of column objects with an active filter applied.
- */
-export const getActiveFilters = columns => columns.map(column => ({
- name: column.id,
- filter: column.filter,
- filterValue: column.filterValue,
-})).filter(filter => !!filter.filterValue);
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.jsx
index b06d85e98b..2d5615c341 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RemindBulkAction.jsx
@@ -9,9 +9,10 @@ import {
licenseManagementModalZeroState as modalZeroState,
} from '../../LicenseManagementModals/LicenseManagementModalHook';
import LicenseManagementRemindModal from '../../LicenseManagementModals/LicenseManagementRemindModal';
-import { canRemindLicense, getActiveFilters } from '../../../data/utils';
+import { canRemindLicense } from '../../../data/utils';
import { ACTIVATED, REVOKED } from '../../../data/constants';
import { SUBSCRIPTION_TABLE_EVENTS } from '../../../../../eventTracking';
+import { getActiveTableColumnFilters } from '../../../../../utils';
const calculateTotalToRemind = ({
selectedRemindableRows,
@@ -54,7 +55,7 @@ const RemindBulkAction = ({
const [remindModal, setRemindModal] = useLicenseManagementModalState();
const selectedRows = selectedFlatRows.map(selectedRow => selectedRow.original);
const selectedRemindableRows = selectedRows.filter(row => canRemindLicense(row.status));
- const activeFilters = getActiveFilters(tableInstance.columns);
+ const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
const tableItemCount = tableInstance.itemCount;
const totalToRemind = calculateTotalToRemind({
selectedRemindableRows,
diff --git a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.jsx b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.jsx
index 37243dc07f..11726b8344 100644
--- a/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.jsx
+++ b/src/components/subscriptions/licenses/LicenseManagementTable/bulk-actions/RevokeBulkAction.jsx
@@ -9,9 +9,10 @@ import {
licenseManagementModalZeroState as modalZeroState,
} from '../../LicenseManagementModals/LicenseManagementModalHook';
import LicenseManagementRevokeModal from '../../LicenseManagementModals/LicenseManagementRevokeModal';
-import { canRevokeLicense, getActiveFilters } from '../../../data/utils';
+import { canRevokeLicense } from '../../../data/utils';
import { REVOKED } from '../../../data/constants';
import { SUBSCRIPTION_TABLE_EVENTS } from '../../../../../eventTracking';
+import { getActiveTableColumnFilters } from '../../../../../utils';
const calculateTotalToRevoke = ({
selectedRevocableRows,
@@ -51,7 +52,7 @@ const RevokeBulkAction = ({
const [revokeModal, setRevokeModal] = useLicenseManagementModalState();
const selectedRows = selectedFlatRows.map(selectedRow => selectedRow.original);
const selectedRevocableRows = selectedRows.filter(row => canRevokeLicense(row.status));
- const activeFilters = getActiveFilters(tableInstance.columns);
+ const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
const tableItemCount = tableInstance.itemCount;
const totalToRevoke = calculateTotalToRevoke({
selectedRevocableRows,
diff --git a/src/utils.js b/src/utils.js
index f3fa4e103e..b1e2399cb4 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -423,6 +423,20 @@ function isAssignableSubsidyAccessPolicyType(policy) {
return isAssignable && assignableSubsidyAccessPolicyTypes.includes(policyType);
}
+/**
+ * Helper to determine which table columns have an active filter applied.
+ *
+ * @param {object} columns Array of column objects (e.g., { id, filter, filterValue })
+ * @returns Array of column objects with an active filter applied.
+ */
+function getActiveTableColumnFilters(columns) {
+ return columns.map(column => ({
+ name: column.id,
+ filter: column.filter,
+ filterValue: column.filterValue,
+ })).filter(filter => !!filter.filterValue);
+}
+
export {
camelCaseDict,
camelCaseDictArray,
@@ -458,4 +472,5 @@ export {
isNotValidNumberString,
defaultQueryClientRetryHandler,
isAssignableSubsidyAccessPolicyType,
+ getActiveTableColumnFilters,
};
From 27a7d6f9118c0b5507f8cbc3dd8079c05017236a Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Fri, 15 Dec 2023 12:52:04 -0500
Subject: [PATCH 117/124] fix: updates package reference for useLocation
(#1143)
* fix: updates package reference for useLocation
* fix: update dependency
* chore: PR fixes
* chore: Pr fixes 2
---
.../ContentHighlights/tests/ContentHighlights.test.jsx | 2 +-
.../BudgetDetailCatalogTabContents.jsx | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/ContentHighlights/tests/ContentHighlights.test.jsx b/src/components/ContentHighlights/tests/ContentHighlights.test.jsx
index 6fc9ca0b38..1561513cb2 100644
--- a/src/components/ContentHighlights/tests/ContentHighlights.test.jsx
+++ b/src/components/ContentHighlights/tests/ContentHighlights.test.jsx
@@ -5,8 +5,8 @@ import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { renderWithRouter } from '@edx/frontend-enterprise-utils';
-import { useHistory } from 'react-router';
import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { useHistory } from 'react-router-dom';
import ContentHighlights from '../ContentHighlights';
import { EnterpriseAppContext } from '../../EnterpriseApp/EnterpriseAppContextProvider';
diff --git a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
index af044e0a0f..297dd18eef 100644
--- a/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailCatalogTabContents.jsx
@@ -4,7 +4,7 @@ import algoliasearch from 'algoliasearch/lite';
import { Row, Col } from '@edx/paragon';
import { SearchData, SEARCH_FACET_FILTERS } from '@edx/frontend-enterprise-catalog-search';
-import { useHistory } from 'react-router';
+import { useLocation, useHistory } from 'react-router-dom';
import CatalogSearch from './search/CatalogSearch';
import {
LANGUAGE_REFINEMENT,
@@ -14,7 +14,7 @@ import { configuration } from '../../config';
const BudgetDetailCatalogTabContents = () => {
const history = useHistory();
- const { location } = history;
+ const location = useLocation();
const { state: locationState } = location;
const catalogContainerRef = useRef();
From 8f7d90e2831d30bf2d87689644e0d9c566ed36ad Mon Sep 17 00:00:00 2001
From: Alexander J Sheehan
Date: Tue, 19 Dec 2023 21:12:41 +0000
Subject: [PATCH 118/124] fix: adding country attribute mapping to sso self
serve stepper
---
.../SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
index 31070334ca..cd2a6e1efe 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
@@ -103,6 +103,14 @@ const SSOConfigConfigureStep = () => {
fieldInstructions="URN of SAML attribute containing the user's email address[es]."
/>
+
+
+
>
);
const renderSAPFields = () => (
From 2d7e691905069d068295f6743f7aa736c671f40e Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Tue, 2 Jan 2024 09:33:45 -0500
Subject: [PATCH 119/124] feat: add additional segment events (#1132)
* feat: add addtional segment events
* feat: close and open chip modal events
* feat: naming for track events added
* feat: more segment eventing work
* feat: breadcrumbs and budget overview eventing added
* feat: individual cancel and remind modal events
* feat: cancel and remind submission events
* feat: bulk cancel/remind eventing
* feat: Add failed redemption, refator select all events
* fix: updates how assignment configuration is retrieved
* chore: tests
* chore: more tests
* chore: PR feedback
* feat: add additional metadata across all events
* chore: Update tests
* chore: PR feedback with abstractions
* chore: Update comments
* chore: PR fixes
* chore: remove extraneous code from test
---
.../AssignmentStatusTableCell.jsx | 77 ++++++++++---
.../AssignmentTableCancel.jsx | 101 ++++++++++++++++--
.../AssignmentTableRemind.jsx | 97 +++++++++++++++--
.../BudgetAssignmentsTable.jsx | 2 +-
.../BudgetDetailActivityTabContents.jsx | 4 +-
.../BudgetDetailPageBreadcrumbs.jsx | 59 +++++++---
.../BudgetDetailPageOverviewAvailability.jsx | 48 ++++++++-
.../BudgetDetailPageOverviewUtilization.jsx | 25 ++++-
.../CancelAssignmentModal.jsx | 3 +
.../PendingAssignmentCancelButton.jsx | 91 +++++++++++++++-
.../PendingAssignmentRemindButton.jsx | 90 +++++++++++++++-
.../RemindAssignmentModal.jsx | 4 +-
.../FailedBadEmail.jsx | 37 +++++--
.../FailedCancellation.jsx | 41 +++++--
.../FailedRedemption.jsx | 43 ++++++--
.../FailedReminder.jsx | 42 ++++++--
.../assignments-status-chips/FailedSystem.jsx | 42 ++++++--
.../NotifyingLearner.jsx | 26 +++--
.../WaitingForLearner.jsx | 38 +++++--
.../cards/NewAssignmentModalButton.jsx | 29 +++--
.../cards/tests/CourseCard.test.jsx | 1 -
.../data/hooks/index.js | 1 +
.../data/hooks/useAssignmentStatusChip.jsx | 39 +++++++
.../learner-credit-management/data/utils.js | 57 ++++++++++
.../tests/BudgetDetailPage.test.jsx | 25 +++++
.../tests/BudgetDetailPageWrapper.test.jsx | 39 +++++--
src/eventTracking.js | 40 +++++++
27 files changed, 972 insertions(+), 129 deletions(-)
create mode 100644 src/components/learner-credit-management/data/hooks/useAssignmentStatusChip.jsx
diff --git a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
index d68cf57d9f..8dc0c0da52 100644
--- a/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
+++ b/src/components/learner-credit-management/AssignmentStatusTableCell.jsx
@@ -1,5 +1,7 @@
import { Chip } from '@edx/paragon';
import PropTypes from 'prop-types';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
import FailedBadEmail from './assignments-status-chips/FailedBadEmail';
import FailedCancellation from './assignments-status-chips/FailedCancellation';
import FailedRedemption from './assignments-status-chips/FailedRedemption';
@@ -8,14 +10,61 @@ import FailedSystem from './assignments-status-chips/FailedSystem';
import NotifyingLearner from './assignments-status-chips/NotifyingLearner';
import WaitingForLearner from './assignments-status-chips/WaitingForLearner';
import { capitalizeFirstLetter } from '../../utils';
+import { useBudgetId, useSubsidyAccessPolicy } from './data';
-const AssignmentStatusTableCell = ({ row }) => {
+const AssignmentStatusTableCell = ({ enterpriseId, row }) => {
const { original } = row;
const {
learnerEmail,
learnerState,
errorReason,
} = original;
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
+ } = subsidyAccessPolicy;
+
+ const sharedTrackEventMetadata = {
+ learnerState,
+ subsidyUuid,
+ assignmentConfiguration,
+ isSubsidyActive,
+ isAssignable,
+ catalogUuid,
+ aggregates,
+ };
+
+ const sendGenericTrackEvent = (eventName, eventMetadata = {}) => {
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventName,
+ {
+ ...sharedTrackEventMetadata,
+ ...eventMetadata,
+ },
+ );
+ };
+
+ const sendErrorStateTrackEvent = (eventName, eventMetadata = {}) => {
+ const errorReasonMetadata = {
+ erroredAction: {
+ errorReason: errorReason?.errorReason || null,
+ actionType: errorReason?.actionType || null,
+ },
+ };
+ const errorStateMetadata = {
+ ...sharedTrackEventMetadata,
+ ...errorReasonMetadata,
+ ...eventMetadata,
+ };
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventName,
+ errorStateMetadata,
+ );
+ };
+
// Learner state is not available for this assignment, so don't display anything.
if (!learnerState) {
return null;
@@ -24,43 +73,42 @@ const AssignmentStatusTableCell = ({ row }) => {
// Display the appropriate status chip based on the learner state.
if (learnerState === 'notifying') {
return (
-
+
);
}
if (learnerState === 'waiting') {
return (
-
+
);
}
if (learnerState === 'failed') {
// If learnerState is failed but no top-level error reason is defined, return a failed system chip.
if (!errorReason) {
- return ;
+ return ;
}
// Determine which failure chip to display based on the top level errorReason. In most cases, the actual errorReason
// code is ignored, in which case we key off the actionType.
if (errorReason.actionType === 'notified') {
if (errorReason.errorReason === 'email_error') {
return (
-
+
);
}
- // non-email errors on failed notifications should NOT use the FailedBadEmail chip.
- return ;
+ return ;
}
if (errorReason.actionType === 'cancelled') {
- return ;
+ return ;
}
if (errorReason.actionType === 'reminded') {
- return ;
+ return ;
}
if (errorReason.actionType === 'redeemed') {
- return ;
+ return ;
}
// In all other unexpected cases, return a failed system chip.
- return ;
+ return ;
}
// Note: The given `learnerState` not officially supported with a `ModalPopup`, but display it anyway.
@@ -68,6 +116,7 @@ const AssignmentStatusTableCell = ({ row }) => {
};
AssignmentStatusTableCell.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
row: PropTypes.shape({
original: PropTypes.shape({
learnerEmail: PropTypes.string,
@@ -84,4 +133,8 @@ AssignmentStatusTableCell.propTypes = {
}).isRequired,
};
-export default AssignmentStatusTableCell;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(AssignmentStatusTableCell);
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
index db155be926..eac61c8f31 100644
--- a/src/components/learner-credit-management/AssignmentTableCancel.jsx
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -2,8 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { DoNotDisturbOn } from '@edx/paragon/icons';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
import CancelAssignmentModal from './CancelAssignmentModal';
import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
+import { transformSelectedRows, useBudgetId, useSubsidyAccessPolicy } from './data';
+import EVENT_NAMES from '../../eventTracking';
import { getActiveTableColumnFilters } from '../../utils';
const calculateTotalToCancel = ({
@@ -17,9 +21,23 @@ const calculateTotalToCancel = ({
return assignmentUuids.length;
};
-const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected, tableInstance }) => {
- const assignmentUuids = selectedFlatRows.map(row => row.id);
- const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
+const AssignmentTableCancelAction = ({
+ selectedFlatRows, isEntireTableSelected, learnerStateCounts, tableInstance, enterpriseId,
+}) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
+ } = subsidyAccessPolicy;
+
+ const {
+ uniqueLearnerState,
+ uniqueAssignmentState,
+ uniqueContentKeys,
+ totalContentQuantity,
+ assignmentUuids,
+ totalSelectedRows,
+ } = transformSelectedRows(selectedFlatRows);
const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
@@ -32,7 +50,66 @@ const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected,
close,
isOpen,
open,
- } = useCancelContentAssignments(assignmentConfigurationUuid, assignmentUuids, shouldCancelAll);
+ } = useCancelContentAssignments(assignmentConfiguration.uuid, assignmentUuids, shouldCancelAll);
+
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_CANCEL_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_CANCEL_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_CANCEL,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const trackEvent = (eventName) => {
+ // constructs a learner state object for the select all state to match format of select all on page metadata
+ const learnerStateObject = {};
+ learnerStateCounts.forEach((learnerState) => {
+ learnerStateObject[learnerState.learnerState] = learnerState.count;
+ });
+
+ const selectedRowsMetadata = isEntireTableSelected
+ ? { uniqueLearnerState: learnerStateObject, totalSelectedRows: tableInstance.itemCount }
+ : {
+ uniqueLearnerState, uniqueAssignmentState, uniqueContentKeys, totalContentQuantity, totalSelectedRows,
+ };
+
+ const trackEventMetadata = {
+ ...selectedRowsMetadata,
+ isAssignable,
+ isSubsidyActive,
+ subsidyUuid,
+ catalogUuid,
+ isEntireTableSelected,
+ assignmentUuids,
+ aggregates,
+ assignmentConfiguration,
+ isOpen: !isOpen,
+ };
+
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventName,
+ trackEventMetadata,
+ );
+ };
+
+ const openModal = () => {
+ open();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_CANCEL_MODAL,
+ );
+ };
+
+ const closeModal = () => {
+ close();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_CANCEL_MODAL,
+ );
+ };
+
+ const cancellationTrackEvent = () => {
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_CANCEL,
+ );
+ };
const tableItemCount = tableInstance.itemCount;
const totalToCancel = calculateTotalToCancel({
@@ -43,14 +120,15 @@ const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected,
return (
<>
-
+
{`Cancel (${totalToCancel})`}
>
@@ -58,12 +136,21 @@ const AssignmentTableCancelAction = ({ selectedFlatRows, isEntireTableSelected,
};
AssignmentTableCancelAction.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()).isRequired,
isEntireTableSelected: PropTypes.bool.isRequired,
+ learnerStateCounts: PropTypes.arrayOf(PropTypes.shape({
+ learnerState: PropTypes.string.isRequired,
+ count: PropTypes.number.isRequired,
+ })).isRequired,
tableInstance: PropTypes.shape({
itemCount: PropTypes.number.isRequired,
columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
}).isRequired,
};
-export default AssignmentTableCancelAction;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(AssignmentTableCancelAction);
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
index d501178a11..d4eb2f00f1 100644
--- a/src/components/learner-credit-management/AssignmentTableRemind.jsx
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -2,8 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { Mail } from '@edx/paragon/icons';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
import useRemindContentAssignments from './data/hooks/useRemindContentAssignments';
import RemindAssignmentModal from './RemindAssignmentModal';
+import { transformSelectedRows, useBudgetId, useSubsidyAccessPolicy } from './data';
+import EVENT_NAMES from '../../eventTracking';
import { getActiveTableColumnFilters } from '../../utils';
const calculateTotalToRemind = ({
@@ -19,10 +23,23 @@ const calculateTotalToRemind = ({
};
const AssignmentTableRemindAction = ({
- selectedFlatRows, isEntireTableSelected, learnerStateCounts, tableInstance,
+ selectedFlatRows, isEntireTableSelected, learnerStateCounts, tableInstance, enterpriseId,
}) => {
- const assignmentUuids = selectedFlatRows.filter(row => row.original.learnerState === 'waiting').map(({ id }) => id);
- const assignmentConfigurationUuid = selectedFlatRows[0].original.assignmentConfiguration;
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
+ } = subsidyAccessPolicy;
+
+ const remindableRows = selectedFlatRows.filter(row => row.original.learnerState === 'waiting');
+ const {
+ uniqueLearnerState,
+ uniqueAssignmentState,
+ uniqueContentKeys,
+ totalContentQuantity,
+ assignmentUuids,
+ totalSelectedRows,
+ } = transformSelectedRows(remindableRows);
const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
@@ -35,7 +52,7 @@ const AssignmentTableRemindAction = ({
close,
isOpen,
open,
- } = useRemindContentAssignments(assignmentConfigurationUuid, assignmentUuids, shouldRemindAll);
+ } = useRemindContentAssignments(assignmentConfiguration.uuid, assignmentUuids, shouldRemindAll);
const selectedRemindableRowCount = calculateTotalToRemind({
assignmentUuids,
@@ -43,21 +60,81 @@ const AssignmentTableRemindAction = ({
learnerStateCounts,
});
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_REMIND_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_REMIND_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_REMIND,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const trackEvent = (eventName) => {
+ // constructs a learner state object for the select all state to match format of select all on page metadata
+ const learnerStateObject = {};
+ learnerStateCounts.forEach((learnerState) => {
+ learnerStateObject[learnerState.learnerState] = learnerState.count;
+ });
+
+ const selectedRowsMetadata = isEntireTableSelected
+ ? { uniqueLearnerState: learnerStateObject, totalSelectedRows: selectedRemindableRowCount }
+ : {
+ uniqueLearnerState, uniqueAssignmentState, uniqueContentKeys, totalContentQuantity, totalSelectedRows,
+ };
+
+ const trackEventMetadata = {
+ ...selectedRowsMetadata,
+ isAssignable,
+ isSubsidyActive,
+ subsidyUuid,
+ catalogUuid,
+ isEntireTableSelected,
+ assignmentUuids,
+ aggregates,
+ assignmentConfiguration,
+ isOpen: !isOpen,
+ };
+
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventName,
+ trackEventMetadata,
+ );
+ };
+
+ const openModal = () => {
+ open();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_REMIND_MODAL,
+ );
+ };
+
+ const closeModal = () => {
+ close();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_REMIND_MODAL,
+ );
+ };
+
+ const reminderTrackEvent = () => {
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_REMIND,
+ );
+ };
+
return (
<>
{`Remind (${selectedRemindableRowCount})`}
>
@@ -66,6 +143,7 @@ const AssignmentTableRemindAction = ({
AssignmentTableRemindAction.propTypes = {
selectedFlatRows: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ enterpriseId: PropTypes.string.isRequired,
isEntireTableSelected: PropTypes.bool.isRequired,
learnerStateCounts: PropTypes.arrayOf(PropTypes.shape({
learnerState: PropTypes.string.isRequired,
@@ -73,7 +151,12 @@ AssignmentTableRemindAction.propTypes = {
})).isRequired,
tableInstance: PropTypes.shape({
columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ itemCount: PropTypes.number.isRequired,
}).isRequired,
};
-export default AssignmentTableRemindAction;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(AssignmentTableRemindAction);
diff --git a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
index 3b3c7e76d1..b9d6aa7dc3 100644
--- a/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
+++ b/src/components/learner-credit-management/BudgetAssignmentsTable.jsx
@@ -119,7 +119,7 @@ const BudgetAssignmentsTable = ({
EmptyTableComponent={CustomDataTableEmptyState}
bulkActions={[
,
- ,
+ ,
]}
/>
);
diff --git a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
index 8c41d33eef..21e65d2de1 100644
--- a/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
+++ b/src/components/learner-credit-management/BudgetDetailActivityTabContents.jsx
@@ -21,8 +21,8 @@ const BudgetDetailActivityTabContents = ({ enterpriseUUID, enterpriseFeatures })
isTopDownAssignmentEnabled,
});
- // // If the budget activity overview data is loading (either the initial request OR any
- // // background re-fetching), show a skeleton.
+ // If the budget activity overview data is loading (either the initial request OR any
+ // background re-fetching), show a skeleton.
if (isBudgetActivityOverviewLoading || isBudgetActivityOverviewFetching || !budgetActivityOverview) {
return (
<>
diff --git a/src/components/learner-credit-management/BudgetDetailPageBreadcrumbs.jsx b/src/components/learner-credit-management/BudgetDetailPageBreadcrumbs.jsx
index fd8ae54555..5d4c9f4294 100644
--- a/src/components/learner-credit-management/BudgetDetailPageBreadcrumbs.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageBreadcrumbs.jsx
@@ -3,27 +3,60 @@ import { connect } from 'react-redux';
import { Breadcrumb } from '@edx/paragon';
import { Link } from 'react-router-dom';
import React from 'react';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { ROUTE_NAMES } from '../EnterpriseApp/data/constants';
+import EVENT_NAMES from '../../eventTracking';
+import { useBudgetId, useSubsidyAccessPolicy } from './data';
-const BudgetDetailPageBreadcrumbs = ({ enterpriseSlug, budgetDisplayName }) => (
-
-
-
-);
+const BudgetDetailPageBreadcrumbs = ({ enterpriseId, enterpriseSlug, budgetDisplayName }) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
+ const trackEventMetadata = {};
+ if (subsidyAccessPolicy) {
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, catalogUuid, aggregates, isAssignable,
+ } = subsidyAccessPolicy;
+ Object.assign(
+ trackEventMetadata,
+ {
+ subsidyUuid,
+ assignmentConfiguration,
+ isSubsidyActive,
+ isAssignable,
+ catalogUuid,
+ aggregates,
+ },
+ );
+ }
+
+ return (
+
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BREADCRUMB_FROM_BUDGET_DETAIL_TO_BUDGETS,
+ trackEventMetadata,
+ )}
+ />
+
+ );
+};
const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
});
BudgetDetailPageBreadcrumbs.propTypes = {
+ enterpriseId: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
budgetDisplayName: PropTypes.string.isRequired,
};
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
index 39fa31dbe8..9be186816a 100644
--- a/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewAvailability.jsx
@@ -7,8 +7,10 @@ import {
} from '@edx/paragon';
import { Add } from '@edx/paragon/icons';
import { generatePath, useRouteMatch, Link } from 'react-router-dom';
-import { formatPrice } from './data';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { formatPrice, useBudgetId, useSubsidyAccessPolicy } from './data';
import { configuration } from '../../config';
+import EVENT_NAMES from '../../eventTracking';
const BudgetDetail = ({ available, utilized, limit }) => {
const currentProgressBarLimit = (available / limit) * 100;
@@ -38,9 +40,29 @@ BudgetDetail.propTypes = {
limit: PropTypes.number.isRequired,
};
-const BudgetActions = ({ budgetId, isAssignable }) => {
+const BudgetActions = ({ budgetId, isAssignable, enterpriseId }) => {
const routeMatch = useRouteMatch();
const supportUrl = configuration.ENTERPRISE_SUPPORT_URL;
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+
+ const trackEventMetadata = {};
+ if (subsidyAccessPolicy) {
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, catalogUuid, aggregates,
+ } = subsidyAccessPolicy;
+ Object.assign(
+ trackEventMetadata,
+ {
+ subsidyUuid,
+ assignmentConfiguration,
+ isSubsidyActive,
+ isAssignable,
+ catalogUuid,
+ aggregates,
+ },
+ );
+ }
const isLargeScreenOrGreater = useMediaQuery({ query: `(min-width: ${breakpoints.small.minWidth}px)` });
@@ -53,7 +75,17 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
Funds from this budget are set to auto-allocate to registered learners based on
settings configured with your support team.
-
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_OVERVIEW_CONTACT_US,
+ trackEventMetadata,
+ )}
+ target="_blank"
+ >
Contact support
@@ -73,6 +105,11 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
pathname: generatePath(routeMatch.path, { budgetId, activeTabKey: 'catalog' }),
state: { budgetActivityScrollToKey: 'catalog' },
}}
+ onClick={() => sendEnterpriseTrackEvent(
+ enterpriseId,
+ EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.BUDGET_OVERVIEW_NEW_ASSIGNMENT,
+ trackEventMetadata,
+ )}
>
New course assignment
@@ -84,6 +121,7 @@ const BudgetActions = ({ budgetId, isAssignable }) => {
BudgetActions.propTypes = {
budgetId: PropTypes.string.isRequired,
isAssignable: PropTypes.bool.isRequired,
+ enterpriseId: PropTypes.string.isRequired,
};
const BudgetDetailPageOverviewAvailability = ({
@@ -91,6 +129,7 @@ const BudgetDetailPageOverviewAvailability = ({
isAssignable,
budgetTotalSummary: { available, utilized, limit },
enterpriseFeatures,
+ enterpriseId,
}) => (
@@ -101,6 +140,7 @@ const BudgetDetailPageOverviewAvailability = ({
@@ -120,9 +160,11 @@ BudgetDetailPageOverviewAvailability.propTypes = {
enterpriseFeatures: PropTypes.shape({
topDownAssignmentRealTimeLcm: PropTypes.bool,
}).isRequired,
+ enterpriseId: PropTypes.string.isRequired,
};
const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
});
diff --git a/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx b/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
index 2276670b81..f4865abd7d 100644
--- a/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
+++ b/src/components/learner-credit-management/BudgetDetailPageOverviewUtilization.jsx
@@ -5,11 +5,13 @@ import {
Stack, Collapsible, Row, Col, Button,
} from '@edx/paragon';
import { ArrowDownward } from '@edx/paragon/icons';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import {
generatePath, useRouteMatch, Link,
} from 'react-router-dom';
import { formatPrice } from './data';
+import EVENT_NAMES from '../../eventTracking';
const BudgetDetailPageOverviewUtilization = ({
budgetId,
@@ -17,10 +19,15 @@ const BudgetDetailPageOverviewUtilization = ({
budgetAggregates,
isAssignable,
enterpriseFeatures,
+ enterpriseId,
}) => {
const routeMatch = useRouteMatch();
-
const { amountAllocatedUsd, amountRedeemedUsd } = budgetAggregates;
+ const {
+ BUDGET_OVERVIEW_UTILIZATION_VIEW_ASSIGNED_TABLE,
+ BUDGET_OVERVIEW_UTILIZATION_VIEW_SPENT_TABLE,
+ BUDGET_OVERVIEW_UTILIZATION_DROPDOWN_TOGGLE,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
if (!budgetId || !enterpriseFeatures.topDownAssignmentRealTimeLcm || utilized <= 0 || !isAssignable) {
return null;
@@ -32,6 +39,9 @@ const BudgetDetailPageOverviewUtilization = ({
}
const linkText = (type === 'assigned') ? 'View assigned activity' : 'View spent activity';
+ const eventNameType = (type === 'assigned')
+ ? BUDGET_OVERVIEW_UTILIZATION_VIEW_ASSIGNED_TABLE
+ : BUDGET_OVERVIEW_UTILIZATION_VIEW_SPENT_TABLE;
return (
sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventNameType,
+ )}
>
{linkText}
@@ -55,6 +69,13 @@ const BudgetDetailPageOverviewUtilization = ({
className="mt-4 budget-utilization-container"
styling="basic"
title={Utilization details }
+ onToggle={(open) => sendEnterpriseTrackEvent(
+ enterpriseId,
+ BUDGET_OVERVIEW_UTILIZATION_DROPDOWN_TOGGLE,
+ {
+ isOpen: open,
+ },
+ )}
>
@@ -121,10 +142,12 @@ BudgetDetailPageOverviewUtilization.propTypes = {
enterpriseFeatures: PropTypes.shape({
topDownAssignmentRealTimeLcm: PropTypes.bool,
}).isRequired,
+ enterpriseId: PropTypes.string.isRequired,
};
const mapStateToProps = state => ({
enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
+ enterpriseId: state.portalConfiguration.enterpriseId,
});
export default connect(mapStateToProps)(BudgetDetailPageOverviewUtilization);
diff --git a/src/components/learner-credit-management/CancelAssignmentModal.jsx b/src/components/learner-credit-management/CancelAssignmentModal.jsx
index cce7b896b1..13657a184b 100644
--- a/src/components/learner-credit-management/CancelAssignmentModal.jsx
+++ b/src/components/learner-credit-management/CancelAssignmentModal.jsx
@@ -12,6 +12,7 @@ const CancelAssignmentModal = ({
close,
isOpen,
uuidCount,
+ trackEvent,
}) => {
const {
successfulCancellationToast: { displayToastForAssignmentCancellation },
@@ -19,6 +20,7 @@ const CancelAssignmentModal = ({
const handleOnClick = async () => {
await cancelContentAssignments();
+ trackEvent();
displayToastForAssignmentCancellation(uuidCount);
};
@@ -69,6 +71,7 @@ CancelAssignmentModal.propTypes = {
close: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
uuidCount: PropTypes.number,
+ trackEvent: PropTypes.func.isRequired,
};
export default CancelAssignmentModal;
diff --git a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
index 5b2144b149..74ebab3da9 100644
--- a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
+++ b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
@@ -4,25 +4,96 @@ import {
Icon, IconButtonWithTooltip,
} from '@edx/paragon';
import { DoNotDisturbOn } from '@edx/paragon/icons';
+import { connect } from 'react-redux';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
import CancelAssignmentModal from './CancelAssignmentModal';
+import EVENT_NAMES from '../../eventTracking';
+import { useBudgetId, useSubsidyAccessPolicy } from './data';
+
+const PendingAssignmentCancelButton = ({ row, enterpriseId }) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
+ } = subsidyAccessPolicy;
-const PendingAssignmentCancelButton = ({ row }) => {
const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
+ const {
+ contentKey,
+ contentQuantity,
+ learnerState,
+ state,
+ uuid,
+ } = row.original;
+
const {
cancelButtonState,
cancelContentAssignments,
close,
isOpen,
open,
- } = useCancelContentAssignments(row.original.assignmentConfiguration, [row.original.uuid]);
+ } = useCancelContentAssignments(assignmentConfiguration, [uuid]);
+
+ const sharedTrackEventMetadata = {
+ subsidyUuid,
+ isSubsidyActive,
+ isAssignable,
+ catalogUuid,
+ assignmentConfiguration,
+ contentKey,
+ contentQuantity,
+ learnerState,
+ aggregates,
+ assignmentState: state,
+ isOpen: !isOpen,
+ };
+
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_CANCEL_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_CANCEL_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CANCEL,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const trackEvent = (eventName, eventMetadata = {}) => {
+ const trackEventMetadata = {
+ ...sharedTrackEventMetadata,
+ ...eventMetadata,
+ };
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventName,
+ trackEventMetadata,
+ );
+ };
+
+ const openModal = () => {
+ open();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_CANCEL_MODAL,
+ );
+ };
+
+ const closeModal = () => {
+ close();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_CANCEL_MODAL,
+ );
+ };
+
+ const cancellationTrackEvent = () => {
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CANCEL,
+ );
+ };
+
return (
<>
{
/>
>
);
@@ -41,11 +113,20 @@ const PendingAssignmentCancelButton = ({ row }) => {
PendingAssignmentCancelButton.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
+ contentKey: PropTypes.string.isRequired,
+ contentQuantity: PropTypes.number.isRequired,
+ learnerState: PropTypes.string.isRequired,
+ state: PropTypes.string.isRequired,
assignmentConfiguration: PropTypes.string.isRequired,
learnerEmail: PropTypes.string,
uuid: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
+ enterpriseId: PropTypes.string.isRequired,
};
-export default PendingAssignmentCancelButton;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(PendingAssignmentCancelButton);
diff --git a/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx b/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
index 07f38883cf..a88843a2fd 100644
--- a/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
+++ b/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
@@ -3,18 +3,88 @@ import PropTypes from 'prop-types';
import { Icon, IconButtonWithTooltip } from '@edx/paragon';
import { Mail } from '@edx/paragon/icons';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { connect } from 'react-redux';
import RemindAssignmentModal from './RemindAssignmentModal';
import useRemindContentAssignments from './data/hooks/useRemindContentAssignments';
+import EVENT_NAMES from '../../eventTracking';
+import { useBudgetId, useSubsidyAccessPolicy } from './data';
+
+const PendingAssignmentRemindButton = ({ row, enterpriseId }) => {
+ const { subsidyAccessPolicyId } = useBudgetId();
+ const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
+ const {
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
+ } = subsidyAccessPolicy;
-const PendingAssignmentRemindButton = ({ row }) => {
const emailAltText = row.original.learnerEmail ? `for ${row.original.learnerEmail}` : '';
+ const {
+ contentKey,
+ contentQuantity,
+ learnerState,
+ state,
+ uuid,
+ } = row.original;
+
const {
remindButtonState,
remindContentAssignments,
close,
isOpen,
open,
- } = useRemindContentAssignments(row.original.assignmentConfiguration, [row.original.uuid]);
+ } = useRemindContentAssignments(assignmentConfiguration, [uuid]);
+
+ const sharedTrackEventMetadata = {
+ subsidyUuid,
+ isSubsidyActive,
+ isAssignable,
+ catalogUuid,
+ assignmentConfiguration,
+ contentKey,
+ contentQuantity,
+ learnerState,
+ aggregates,
+ assignmentState: state,
+ isOpen: !isOpen,
+ };
+
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_REMIND_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_REMIND_MODAL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_REMIND,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const trackEvent = (eventName, eventMetadata = {}) => {
+ const trackEventMetadata = {
+ ...sharedTrackEventMetadata,
+ ...eventMetadata,
+ };
+ sendEnterpriseTrackEvent(
+ enterpriseId,
+ eventName,
+ trackEventMetadata,
+ );
+ };
+
+ const openModal = () => {
+ open();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_REMIND_MODAL,
+ );
+ };
+
+ const closeModal = () => {
+ close();
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_REMIND_MODAL,
+ );
+ };
+
+ const reminderTrackEvent = () => {
+ trackEvent(
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_REMIND,
+ );
+ };
return (
<>
@@ -22,16 +92,17 @@ const PendingAssignmentRemindButton = ({ row }) => {
alt={`Remind learner ${emailAltText}`}
data-testid={`remind-assignment-${row.original.uuid}`}
iconAs={Icon}
- onClick={open}
+ onClick={openModal}
src={Mail}
tooltipContent="Remind learner"
tooltipPlacement="top"
/>
>
);
@@ -40,11 +111,20 @@ const PendingAssignmentRemindButton = ({ row }) => {
PendingAssignmentRemindButton.propTypes = {
row: PropTypes.shape({
original: PropTypes.shape({
+ contentKey: PropTypes.string.isRequired,
+ contentQuantity: PropTypes.number.isRequired,
+ learnerState: PropTypes.string.isRequired,
+ state: PropTypes.string.isRequired,
assignmentConfiguration: PropTypes.string.isRequired,
learnerEmail: PropTypes.string,
uuid: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
+ enterpriseId: PropTypes.string.isRequired,
};
-export default PendingAssignmentRemindButton;
+const mapStateToProps = state => ({
+ enterpriseId: state.portalConfiguration.enterpriseId,
+});
+
+export default connect(mapStateToProps)(PendingAssignmentRemindButton);
diff --git a/src/components/learner-credit-management/RemindAssignmentModal.jsx b/src/components/learner-credit-management/RemindAssignmentModal.jsx
index 013535e2b9..546bee7093 100644
--- a/src/components/learner-credit-management/RemindAssignmentModal.jsx
+++ b/src/components/learner-credit-management/RemindAssignmentModal.jsx
@@ -7,7 +7,7 @@ import { Mail } from '@edx/paragon/icons';
import { BudgetDetailPageContext } from './BudgetDetailPageWrapper';
const RemindAssignmentModal = ({
- remindButtonState, remindContentAssignments, close, isOpen, uuidCount,
+ remindButtonState, remindContentAssignments, close, isOpen, uuidCount, trackEvent,
}) => {
const {
successfulReminderToast: { displayToastForAssignmentReminder },
@@ -15,6 +15,7 @@ const RemindAssignmentModal = ({
const handleOnClick = async () => {
await remindContentAssignments();
+ trackEvent();
displayToastForAssignmentReminder(uuidCount);
};
@@ -66,6 +67,7 @@ RemindAssignmentModal.propTypes = {
close: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
uuidCount: PropTypes.number,
+ trackEvent: PropTypes.func.isRequired,
};
export default RemindAssignmentModal;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
index b6664a8768..df5fb496c3 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedBadEmail.jsx
@@ -1,30 +1,46 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
-import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import { Chip, Hyperlink } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
+import EVENT_NAMES from '../../../eventTracking';
+import { useAssignmentStatusChip } from '../data';
-const FailedBadEmail = ({ learnerEmail }) => {
- const [isOpen, open, close] = useToggle(false);
+const FailedBadEmail = ({ learnerEmail, trackEvent }) => {
const [target, setTarget] = useState(null);
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_EMAIL,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_EMAIL_HELP_CENTER,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ helpCenterTrackEvent,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_EMAIL,
+ chipHelpCenterEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_EMAIL_HELP_CENTER,
+ trackEvent,
+ });
return (
<>
Failed: Bad email
Failed: Bad email
@@ -41,7 +57,11 @@ const FailedBadEmail = ({ learnerEmail }) => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
.
@@ -55,6 +75,7 @@ const FailedBadEmail = ({ learnerEmail }) => {
FailedBadEmail.propTypes = {
learnerEmail: PropTypes.string,
+ trackEvent: PropTypes.func.isRequired,
};
FailedBadEmail.defaultProps = {
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
index f102592f4f..18bc683c56 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedCancellation.jsx
@@ -1,29 +1,46 @@
import React, { useState } from 'react';
-import { Chip, useToggle, Hyperlink } from '@edx/paragon';
+import PropTypes from 'prop-types';
+import { Chip, Hyperlink } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
+import EVENT_NAMES from '../../../eventTracking';
+import { useAssignmentStatusChip } from '../data';
-const FailedCancellation = () => {
- const [isOpen, open, close] = useToggle(false);
+const FailedCancellation = ({ trackEvent }) => {
const [target, setTarget] = useState(null);
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_CANCELLATION,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_CANCELLATION_HELP_CENTER,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ helpCenterTrackEvent,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_CANCELLATION,
+ chipHelpCenterEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_CANCELLATION_HELP_CENTER,
+ trackEvent,
+ });
return (
<>
Failed: Cancellation
Failed: Cancellation
@@ -41,7 +58,11 @@ const FailedCancellation = () => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
@@ -53,4 +74,8 @@ const FailedCancellation = () => {
);
};
+FailedCancellation.propTypes = {
+ trackEvent: PropTypes.func.isRequired,
+};
+
export default FailedCancellation;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx
index 6e12f9303f..ef263065c6 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedRedemption.jsx
@@ -1,29 +1,48 @@
import React, { useState } from 'react';
-import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import PropTypes from 'prop-types';
+
+import { Chip, Hyperlink } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
+import EVENT_NAMES from '../../../eventTracking';
+import { useAssignmentStatusChip } from '../data';
-const FailedRedemption = () => {
- const [isOpen, open, close] = useToggle(false);
+const FailedRedemption = ({ trackEvent }) => {
const [target, setTarget] = useState(null);
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REDEMPTION,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REDEMPTION_HELP_CENTER,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ helpCenterTrackEvent,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REDEMPTION,
+ chipHelpCenterEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REDEMPTION_HELP_CENTER,
+ trackEvent,
+ });
+
return (
<>
Failed: Redemption
Failed: Redemption
@@ -44,7 +63,11 @@ const FailedRedemption = () => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
.
@@ -56,4 +79,8 @@ const FailedRedemption = () => {
);
};
+FailedRedemption.propTypes = {
+ trackEvent: PropTypes.func.isRequired,
+};
+
export default FailedRedemption;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
index 033ae7bca4..03519222c9 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedReminder.jsx
@@ -1,27 +1,45 @@
import React, { useState } from 'react';
-import { Chip, useToggle, Hyperlink } from '@edx/paragon';
+import PropTypes from 'prop-types';
+import { Chip, Hyperlink } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
+import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
+import EVENT_NAMES from '../../../eventTracking';
+import { useAssignmentStatusChip } from '../data';
-const FailedReminder = () => {
- const [isOpen, open, close] = useToggle(false);
+const FailedReminder = ({ trackEvent }) => {
const [target, setTarget] = useState(null);
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REMINDER,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REMINDER_HELP_CENTER,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ helpCenterTrackEvent,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REMINDER,
+ chipHelpCenterEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REMINDER_HELP_CENTER,
+ trackEvent,
+ });
return (
<>
Failed: Reminder
Failed: Reminder
@@ -39,7 +57,11 @@ const FailedReminder = () => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
.
@@ -51,4 +73,8 @@ const FailedReminder = () => {
);
};
+FailedReminder.propTypes = {
+ trackEvent: PropTypes.func.isRequired,
+};
+
export default FailedReminder;
diff --git a/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx b/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
index 43ae45e4b0..c5a1acb026 100644
--- a/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/FailedSystem.jsx
@@ -1,29 +1,47 @@
import React, { useState } from 'react';
-import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import PropTypes from 'prop-types';
+
+import { Chip, Hyperlink } from '@edx/paragon';
import { Error } from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
+import EVENT_NAMES from '../../../eventTracking';
+import { useAssignmentStatusChip } from '../data';
-const FailedSystem = () => {
- const [isOpen, open, close] = useToggle(false);
+const FailedSystem = ({ trackEvent }) => {
const [target, setTarget] = useState(null);
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_SYSTEM,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_SYSTEM_HELP_CENTER,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ helpCenterTrackEvent,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_SYSTEM,
+ chipHelpCenterEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_SYSTEM_HELP_CENTER,
+ trackEvent,
+ });
return (
<>
Failed: System
Failed: System
@@ -38,7 +56,11 @@ const FailedSystem = () => {
Get more troubleshooting help at{' '}
-
+
Help Center: Course Assignments
.
@@ -50,4 +72,8 @@ const FailedSystem = () => {
);
};
+FailedSystem.propTypes = {
+ trackEvent: PropTypes.func.isRequired,
+};
+
export default FailedSystem;
diff --git a/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx b/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx
index 2380fda362..94ac11d39f 100644
--- a/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/NotifyingLearner.jsx
@@ -1,28 +1,39 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
-import { Chip, useToggle } from '@edx/paragon';
+import { Chip } from '@edx/paragon';
import { Send } from '@edx/paragon/icons';
import BaseModalPopup from './BaseModalPopup';
+import EVENT_NAMES from '../../../eventTracking';
+import { useAssignmentStatusChip } from '../data';
-const NotifyingLearner = ({ learnerEmail }) => {
- const [isOpen, open, close] = useToggle(false);
+const NotifyingLearner = ({ learnerEmail, trackEvent }) => {
const [target, setTarget] = useState(null);
+ const { BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_NOTIFY_LEARNER } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_NOTIFY_LEARNER,
+ trackEvent,
+ });
return (
<>
Notifying learner
Notifying {learnerEmail ?? 'learner'}
@@ -40,6 +51,7 @@ const NotifyingLearner = ({ learnerEmail }) => {
NotifyingLearner.propTypes = {
learnerEmail: PropTypes.string,
+ trackEvent: PropTypes.func.isRequired,
};
export default NotifyingLearner;
diff --git a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
index f1176b3317..819f918c53 100644
--- a/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
+++ b/src/components/learner-credit-management/assignments-status-chips/WaitingForLearner.jsx
@@ -1,31 +1,46 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
-import { Chip, Hyperlink, useToggle } from '@edx/paragon';
+import { Chip, Hyperlink } from '@edx/paragon';
import { Timelapse } from '@edx/paragon/icons';
import { getConfig } from '@edx/frontend-platform/config';
import BaseModalPopup from './BaseModalPopup';
-import { ASSIGNMENT_ENROLLMENT_DEADLINE } from '../data';
+import { ASSIGNMENT_ENROLLMENT_DEADLINE, useAssignmentStatusChip } from '../data';
+import EVENT_NAMES from '../../../eventTracking';
-const WaitingForLearner = ({ learnerEmail }) => {
- const [isOpen, open, close] = useToggle(false);
+const WaitingForLearner = ({ learnerEmail, trackEvent }) => {
const [target, setTarget] = useState(null);
+ const {
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_WAITING_FOR_LEARNER,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_WAITING_FOR_LEARNER_HELP_CENTER,
+ } = EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT;
+
+ const {
+ openChipModal,
+ closeChipModal,
+ isChipModalOpen,
+ helpCenterTrackEvent,
+ } = useAssignmentStatusChip({
+ chipInteractionEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_WAITING_FOR_LEARNER,
+ chipHelpCenterEventName: BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_WAITING_FOR_LEARNER_HELP_CENTER,
+ trackEvent,
+ });
return (
<>
Waiting for learner
Waiting for {learnerEmail ?? 'learner'}
@@ -40,7 +55,11 @@ const WaitingForLearner = ({ learnerEmail }) => {
Need help?
Learn more about learner enrollment in assigned courses at{' '}
-
+
Help Center: Course Assignments
.
@@ -53,6 +72,7 @@ const WaitingForLearner = ({ learnerEmail }) => {
WaitingForLearner.propTypes = {
learnerEmail: PropTypes.string,
+ trackEvent: PropTypes.func.isRequired,
};
export default WaitingForLearner;
diff --git a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
index 790fc68d3f..4024990c9f 100644
--- a/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
+++ b/src/components/learner-credit-management/cards/NewAssignmentModalButton.jsx
@@ -47,7 +47,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
} = useContext(BudgetDetailPageContext);
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
- subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid,
+ subsidyUuid, assignmentConfiguration, isSubsidyActive, isAssignable, catalogUuid, aggregates,
} = subsidyAccessPolicy;
const sharedEnterpriseTrackEventMetadata = {
subsidyAccessPolicyId,
@@ -55,10 +55,11 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
subsidyUuid,
isSubsidyActive,
isAssignable,
+ aggregates,
contentPriceCents: course.normalizedMetadata.contentPrice * 100,
contentKey: course.key,
courseUuid: course.uuid,
- assignmentConfigurationUuid: assignmentConfiguration.uuid,
+ assignmentConfiguration,
};
const { mutate } = useAllocateContentAssignments();
@@ -94,11 +95,13 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
const onSuccessEnterpriseTrackEvents = ({
totalLearnersAllocated,
totalLearnersAlreadyAllocated,
+ response,
}) => {
const trackEventMetadata = {
...sharedEnterpriseTrackEventMetadata,
totalLearnersAllocated,
totalLearnersAlreadyAllocated,
+ response,
};
sendEnterpriseTrackEvent(
enterpriseId,
@@ -120,7 +123,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
setAssignButtonState('pending');
setCreateAssignmentsErrorReason(null);
mutate(mutationArgs, {
- onSuccess: ({ created, noChange, updated }) => {
+ onSuccess: (res) => {
setAssignButtonState('complete');
// Ensure the budget and budgets queries are invalidated so that the relevant
// queries become stale and refetches new updated data from the API.
@@ -131,11 +134,12 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
queryKey: learnerCreditManagementQueryKeys.budgets(enterpriseId),
});
handleCloseAssignmentModal();
- const totalLearnersAllocated = created.length + updated.length;
- const totalLearnersAlreadyAllocated = noChange.length;
+ const totalLearnersAllocated = res.created.length + res.updated.length;
+ const totalLearnersAlreadyAllocated = res.noChange.length;
onSuccessEnterpriseTrackEvents({
totalLearnersAllocated,
totalLearnersAlreadyAllocated,
+ res,
});
displayToastForAssignmentAllocation({
totalLearnersAllocated,
@@ -167,6 +171,7 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
totalAllocatedLearners: learnerEmails.length,
errorStatus: httpErrorStatus,
errorReason,
+ response: err,
},
);
},
@@ -185,7 +190,10 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
sendEnterpriseTrackEvent(
enterpriseId,
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_EXIT,
- { assignButtonState },
+ {
+ ...sharedEnterpriseTrackEventMetadata,
+ assignButtonState,
+ },
);
}}
footerNode={(
@@ -196,6 +204,10 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
onClick={() => sendEnterpriseTrackEvent(
enterpriseId,
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_HELP_CENTER,
+ {
+ ...sharedEnterpriseTrackEventMetadata,
+ assignButtonState,
+ },
)}
destination={getConfig().ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL}
showLaunchIcon
@@ -211,7 +223,10 @@ const NewAssignmentModalButton = ({ enterpriseId, course, children }) => {
sendEnterpriseTrackEvent(
enterpriseId,
EVENT_NAMES.LEARNER_CREDIT_MANAGEMENT.ASSIGNMENT_MODAL_CANCEL,
- { assignButtonState },
+ {
+ ...sharedEnterpriseTrackEventMetadata,
+ assignButtonState,
+ },
);
}}
>
diff --git a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
index 517f14fbd5..f5fed6a990 100644
--- a/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
+++ b/src/components/learner-credit-management/cards/tests/CourseCard.test.jsx
@@ -9,7 +9,6 @@ import { QueryClientProvider, useQueryClient } from '@tanstack/react-query';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { renderWithRouter, sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
-
import CourseCard from '../CourseCard';
import {
formatPrice,
diff --git a/src/components/learner-credit-management/data/hooks/index.js b/src/components/learner-credit-management/data/hooks/index.js
index 63681a4030..396153dad6 100644
--- a/src/components/learner-credit-management/data/hooks/index.js
+++ b/src/components/learner-credit-management/data/hooks/index.js
@@ -12,3 +12,4 @@ export { default as useSuccessfulAssignmentToastContextValue } from './useSucces
export { default as useSuccessfulCancellationToastContextValue } from './useSuccessfulCancellationToastContextValue';
export { default as useSuccessfulReminderToastContextValue } from './useSuccessfulReminderToastContextValue';
export { default as useEnterpriseOffer } from './useEnterpriseOffer';
+export { default as useAssignmentStatusChip } from './useAssignmentStatusChip';
diff --git a/src/components/learner-credit-management/data/hooks/useAssignmentStatusChip.jsx b/src/components/learner-credit-management/data/hooks/useAssignmentStatusChip.jsx
new file mode 100644
index 0000000000..f74dc90a2a
--- /dev/null
+++ b/src/components/learner-credit-management/data/hooks/useAssignmentStatusChip.jsx
@@ -0,0 +1,39 @@
+import { useToggle } from '@edx/paragon';
+
+/**
+ *
+ * @param chipInteractionEventName {String} - The event name that will be read in Segment of a chip opening and closing
+ * @param chipHelpCenterEventName {String} - The event name that will be read in Segment for a help center link
+ * interaction
+ * @param trackEvent {Function} - The track event functioning that will be sending a track event
+ * @returns {{
+ * closeChipModal: closeChipModal,
+ * openChipModal: openChipModal,
+ * helpCenterTrackEvent: helpCenterTrackEvent,
+ * isChipModalOpen: *
+ * }}
+ */
+export default function useAssignmentStatusChip({ chipInteractionEventName, chipHelpCenterEventName, trackEvent }) {
+ const [isChipModalOpen, open, close] = useToggle(false);
+ const openChipModal = () => {
+ open();
+ // Note: could hardcode true given this function *always* opens
+ trackEvent(chipInteractionEventName, { isOpen: true });
+ };
+ const closeChipModal = () => {
+ close();
+ // Note: could hardcode false given this function *always* closes
+ trackEvent(chipInteractionEventName, { isOpen: false });
+ };
+
+ const helpCenterTrackEvent = () => {
+ trackEvent(chipHelpCenterEventName);
+ };
+
+ return {
+ isChipModalOpen,
+ openChipModal,
+ closeChipModal,
+ helpCenterTrackEvent,
+ };
+}
diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js
index 51746f3eca..2216a01580 100644
--- a/src/components/learner-credit-management/data/utils.js
+++ b/src/components/learner-credit-management/data/utils.js
@@ -337,3 +337,60 @@ export async function retrieveBudgetDetailActivityOverview({
}
return result;
}
+
+/**
+ * Takes the raw selected flat rows data from the 'Assigned' datatable and returns metadata that is used for tracking
+ * bulk enrollment of reminders and bulk enrollment of cancellations.
+ * @param {Array} selectedFlatRows An array of selectedFlatRows from the activity 'Assigned' table
+ * @returns {{
+ * uniqueLearnerState: [String],
+ * totalContentQuantity: Number,
+ * assignmentConfigurationUuid: String,
+ * assignmentUuids: [String]
+ * uniqueContentKeys: [String],
+ * uniqueAssignmentState: [String],
+ * totalSelectedRows: Number,
+ * }}
+ */
+export const transformSelectedRows = (selectedFlatRows) => {
+ const assignmentUuids = selectedFlatRows.map(item => item.id);
+ const totalSelectedRows = selectedFlatRows.length;
+
+ // Count of unique content keys, where the key is the course,
+ // and value is count of the course.
+ const flatMappedContentKeys = selectedFlatRows.map(item => item?.original?.contentKey);
+ const uniqueContentKeys = {};
+ flatMappedContentKeys.forEach((courseKey) => {
+ uniqueContentKeys[courseKey] = (uniqueContentKeys[courseKey] || 0) + 1;
+ });
+
+ // Count of unique learner states, where the key is the learnerState,
+ // and value is count of the learnerState.
+ const flatMappedLearnerState = selectedFlatRows.map(item => item?.original?.learnerState);
+ const uniqueLearnerState = {};
+ flatMappedLearnerState.forEach((learnerState) => {
+ uniqueLearnerState[learnerState] = (uniqueLearnerState[learnerState] || 0) + 1;
+ });
+
+ // Count of unique assignment states, where the key is the assignment state,
+ // and value is count of the assignment state.
+ const flatMappedAssignmentState = selectedFlatRows.map(item => item?.original?.state);
+ const uniqueAssignmentState = {};
+ flatMappedAssignmentState.forEach((state) => {
+ uniqueAssignmentState[state] = (uniqueAssignmentState[state] || 0) + 1;
+ });
+
+ // Total value of all the selected rows accumulated from the contentQuantity
+ const totalContentQuantity = selectedFlatRows.map(
+ item => item.original.contentQuantity,
+ ).reduce((prev, next) => prev + next, 0);
+
+ return {
+ uniqueAssignmentState,
+ uniqueLearnerState,
+ uniqueContentKeys,
+ totalContentQuantity,
+ assignmentUuids,
+ totalSelectedRows,
+ };
+};
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index 9052c8d93c..b3ade09d7b 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -1175,10 +1175,25 @@ describe(' ', () => {
expect(statusChip).toBeInTheDocument();
userEvent.click(statusChip);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+
// Modal popup is visible with expected text
const modalPopupContents = within(screen.getByTestId('assignment-status-modalpopup-contents'));
expect(modalPopupContents.getByText(expectedModalPopupHeading)).toBeInTheDocument();
expect(modalPopupContents.getByText(expectedModalPopupContent, { exact: false })).toBeInTheDocument();
+
+ // Help Center link clicked and modal closed
+ if (screen.queryByText('Help Center: Course Assignments')) {
+ const helpCenterLink = screen.getByText('Help Center: Course Assignments');
+ userEvent.click(helpCenterLink);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
+ // Click chip to close modal
+ userEvent.click(statusChip);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalled();
+ } else {
+ userEvent.click(statusChip);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
+ }
});
it.each([
@@ -1332,6 +1347,8 @@ describe(' ', () => {
userEvent.click(catalogTab);
});
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+
await waitFor(() => {
expect(screen.getByTestId('budget-detail-catalog-tab-contents')).toBeInTheDocument();
});
@@ -1516,6 +1533,7 @@ describe(' ', () => {
const cancelBulkActionButton = screen.getByText('Cancel (2)');
expect(cancelBulkActionButton).toBeInTheDocument();
userEvent.click(cancelBulkActionButton);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
const modalDialog = screen.getByRole('dialog');
expect(modalDialog).toBeInTheDocument();
const cancelDialogButton = getButtonElement('Cancel assignments (2)');
@@ -1528,6 +1546,7 @@ describe(' ', () => {
await waitFor(
() => expect(screen.getByText('Assignments canceled (2)')).toBeInTheDocument(),
);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
});
it('reminds assignments in bulk', async () => {
@@ -1608,6 +1627,7 @@ describe(' ', () => {
expect(modalDialog).toBeInTheDocument();
const remindDialogButton = getButtonElement('Send reminders (2)');
userEvent.click(remindDialogButton);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
await waitFor(
() => expect(
EnterpriseAccessApiService.remindAllContentAssignments,
@@ -1616,6 +1636,7 @@ describe(' ', () => {
await waitFor(
() => expect(screen.getByText('Reminders sent (2)')).toBeInTheDocument(),
);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
});
it('cancels a single assignment', async () => {
@@ -1666,6 +1687,7 @@ describe(' ', () => {
const cancelIconButton = screen.getByTestId('cancel-assignment-test-uuid');
expect(cancelIconButton).toBeInTheDocument();
userEvent.click(cancelIconButton);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
const modalDialog = screen.getByRole('dialog');
expect(modalDialog).toBeInTheDocument();
const cancelDialogButton = getButtonElement('Cancel assignment');
@@ -1673,6 +1695,7 @@ describe(' ', () => {
await waitFor(
() => expect(screen.getByText('Assignment canceled')).toBeInTheDocument(),
);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
});
it('reminds a single assignment', async () => {
EnterpriseAccessApiService.remindContentAssignments.mockResolvedValueOnce({ status: 200 });
@@ -1722,6 +1745,7 @@ describe(' ', () => {
const remindIconButton = screen.getByTestId('remind-assignment-test-uuid');
expect(remindIconButton).toBeInTheDocument();
userEvent.click(remindIconButton);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
const modalDialog = screen.getByRole('dialog');
expect(modalDialog).toBeInTheDocument();
const remindDialogButton = getButtonElement('Send reminder');
@@ -1729,5 +1753,6 @@ describe(' ', () => {
await waitFor(
() => expect(screen.getByText('Reminder sent')).toBeInTheDocument(),
);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
});
});
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
index 96c7ed5fc2..7ce956ab94 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPageWrapper.test.jsx
@@ -7,9 +7,12 @@ import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import '@testing-library/jest-dom/extend-expect';
+import { QueryClientProvider } from '@tanstack/react-query';
+import { renderWithRouter, sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import BudgetDetailPageWrapper, { BudgetDetailPageContext } from '../BudgetDetailPageWrapper';
-import { getButtonElement } from '../../test/testUtils';
+import { getButtonElement, queryClient } from '../../test/testUtils';
+import BudgetDetailPageBreadcrumbs from '../BudgetDetailPageBreadcrumbs';
const mockStore = configureMockStore([thunk]);
const getMockStore = store => mockStore(store);
@@ -26,19 +29,27 @@ const defaultStoreState = {
},
};
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-utils'),
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
+
const MockBudgetDetailPageWrapper = ({
initialStoreState = defaultStoreState,
children,
}) => {
const store = getMockStore(initialStoreState);
return (
-
-
-
- {children}
-
-
-
+
+
+
+
+ {children}
+
+
+
+
+
);
};
@@ -263,4 +274,16 @@ describe(' ', () => {
expect(screen.queryByText(expectedToastMessage)).not.toBeInTheDocument();
});
});
+ it('calls segment event on breadcrumb click', () => {
+ const mockBudgetDisplayName = 'Test Budget';
+ renderWithRouter(
+
+
+ ,
+ );
+ const previousBreadcrumb = screen.getByText('Budgets');
+
+ userEvent.click(previousBreadcrumb);
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ });
});
diff --git a/src/eventTracking.js b/src/eventTracking.js
index ed4244786f..d77196db3e 100644
--- a/src/eventTracking.js
+++ b/src/eventTracking.js
@@ -103,14 +103,54 @@ export const SUBSCRIPTION_EVENTS = {
};
export const LEARNER_CREDIT_MANAGEMENT_EVENTS = {
+ BREADCRUMB_FROM_BUDGET_DETAIL_TO_BUDGETS: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.breadcrumb_budget_detail_to_budgets.clicked`,
TAB_CHANGED: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.tab.changed`,
+ // Budget Overview
+ BUDGET_OVERVIEW_CONTACT_US: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.contact_us.clicked`,
+ BUDGET_OVERVIEW_NEW_ASSIGNMENT: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.new_assignment.clicked`,
+ BUDGET_OVERVIEW_UTILIZATION_DROPDOWN_TOGGLE: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.utilization_dropdown.toggled`,
+ BUDGET_OVERVIEW_UTILIZATION_VIEW_SPENT_TABLE: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.view_spent_activity.clicked`,
+ BUDGET_OVERVIEW_UTILIZATION_VIEW_ASSIGNED_TABLE: `${LEARNER_CREDIT_MANAGEMENT_PREFIX}.budget_detail.view_assigned_activity.clicked`,
// Activity tab
+ // Activity tab assigned datatable
BUDGET_DETAILS_ASSIGNED_DATATABLE_SORT_BY_OR_FILTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table.changed`,
BUDGET_DETAILS_ASSIGNED_DATATABLE_VIEW_COURSE: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_view_course.clicked`,
BUDGET_DETAILS_ASSIGNED_DATATABLE_ACTIONS_REFRESH: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_refresh.clicked`,
+ // Activity tab assigned table remind
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_REMIND_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_remind_modal_open.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_REMIND_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_remind_modal_close.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_REMIND: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_remind.clicked`,
+ // Activity tab assigned table bulk remind
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_REMIND_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_bulk_remind_modal_open.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_REMIND_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_bulk_remind_modal_close.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_REMIND: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_bulk_remind.clicked`,
+ // Activity tab assigned table cancel
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_CANCEL_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_cancel_modal_open.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_CANCEL_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_cancel_modal_close.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CANCEL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_cancel.clicked`,
+ // Activity tab assigned table bulk cancel
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_CANCEL_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_bulk_cancel_modal_open.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CLOSE_BULK_CANCEL_MODAL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_bulk_cancel_modal_close.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_BULK_CANCEL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_bulk_cancel.clicked`,
+ // Activity tab spent datatable
BUDGET_DETAILS_SPENT_DATATABLE_SORT_BY_OR_FILTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.spent_table.changed`,
BUDGET_DETAILS_SPENT_DATATABLE_VIEW_COURSE: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.spent_table_view_course.clicked`,
EMPTY_STATE_CTA: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.empty_state_cta_to_catalog.clicked`,
+ // Activity tab chips
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_NOTIFY_LEARNER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_notify_learner.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_WAITING_FOR_LEARNER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_waiting_for_learner.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_CANCELLATION: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_cancellation.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_SYSTEM: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_system.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_EMAIL: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_bad_email.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REMINDER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_reminder.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REDEMPTION: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_redemption.clicked`,
+ // Activity tab chips help center links
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_WAITING_FOR_LEARNER_HELP_CENTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_waiting_for_learner_help_center.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_CANCELLATION_HELP_CENTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_cancellation_help_center.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_SYSTEM_HELP_CENTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_system_help_center.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_EMAIL_HELP_CENTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_bad_email_help_center.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REMINDER_HELP_CENTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_reminder_help_center.clicked`,
+ BUDGET_DETAILS_ASSIGNED_DATATABLE_CHIP_FAILED_REDEMPTION_HELP_CENTER: `${BUDGET_DETAIL_ACTIVITY_TAB_PREFIX}.assigned_table_chip_failed_redemption_help_center.clicked`,
// Catalog tab
// Catalog tab search
VIEW_COURSE: `${BUDGET_DETAIL_SEARCH_PREFIX}.view_course.clicked`,
From dbdbe10d18c1517f1a4767ed0de9ebb9c1b6dff7 Mon Sep 17 00:00:00 2001
From: Alex Dusenbery
Date: Tue, 2 Jan 2024 11:18:46 -0500
Subject: [PATCH 120/124] feat: apply filters to cancel/remind all
ENT-8156 | Pass the appropriate content assignment filter state
to the `cancel-all` and `remind-all` endpoints.
---
src/components/forms/FormWorkflow.tsx | 4 +-
.../AssignmentTableCancel.jsx | 18 ++++----
.../AssignmentTableRemind.jsx | 18 ++++----
.../PendingAssignmentCancelButton.jsx | 6 ++-
.../PendingAssignmentRemindButton.jsx | 6 ++-
.../data/hooks/useCancelContentAssignments.js | 10 +++--
.../useCancelContentAssignments.test.jsx | 41 +++++++++++++++++++
.../data/hooks/useRemindContentAssignments.js | 10 +++--
.../useRemindContentAssignments.test.jsx | 41 +++++++++++++++++++
.../steps/NewSSOConfigConfigureStep.tsx | 2 +-
.../steps/NewSSOConfigConnectStep.tsx | 4 +-
.../services/EnterpriseAccessApiService.js | 30 ++++++++++++--
.../tests/EnterpriseAccessApiService.test.js | 14 +++++--
13 files changed, 168 insertions(+), 36 deletions(-)
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index a60b017e64..88688afec1 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -138,8 +138,8 @@ const FormWorkflow = ({
const newFormFields: FormConfigData = await nextButtonConfig.onClick({
formFields,
errHandler: (error) => {
- setFormError(error);
- if (!!error) {
+ setFormError(error);
+ if (error) {
advance = false;
}
},
diff --git a/src/components/learner-credit-management/AssignmentTableCancel.jsx b/src/components/learner-credit-management/AssignmentTableCancel.jsx
index eac61c8f31..ad376a31d8 100644
--- a/src/components/learner-credit-management/AssignmentTableCancel.jsx
+++ b/src/components/learner-credit-management/AssignmentTableCancel.jsx
@@ -8,7 +8,6 @@ import CancelAssignmentModal from './CancelAssignmentModal';
import useCancelContentAssignments from './data/hooks/useCancelContentAssignments';
import { transformSelectedRows, useBudgetId, useSubsidyAccessPolicy } from './data';
import EVENT_NAMES from '../../eventTracking';
-import { getActiveTableColumnFilters } from '../../utils';
const calculateTotalToCancel = ({
assignmentUuids,
@@ -39,10 +38,7 @@ const AssignmentTableCancelAction = ({
totalSelectedRows,
} = transformSelectedRows(selectedFlatRows);
- const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
-
- // If entire table is selected and there are NO filters, hit cancel-all endpoint. Otherwise, hit usual bulk cancel.
- const shouldCancelAll = isEntireTableSelected && activeFilters.length === 0;
+ const { state: dataTableState } = tableInstance;
const {
cancelButtonState,
@@ -50,7 +46,12 @@ const AssignmentTableCancelAction = ({
close,
isOpen,
open,
- } = useCancelContentAssignments(assignmentConfiguration.uuid, assignmentUuids, shouldCancelAll);
+ } = useCancelContentAssignments(
+ assignmentConfiguration.uuid,
+ assignmentUuids,
+ isEntireTableSelected,
+ dataTableState.filters,
+ );
const {
BUDGET_DETAILS_ASSIGNED_DATATABLE_OPEN_BULK_CANCEL_MODAL,
@@ -114,7 +115,7 @@ const AssignmentTableCancelAction = ({
const tableItemCount = tableInstance.itemCount;
const totalToCancel = calculateTotalToCancel({
assignmentUuids,
- isEntireTableSelected: shouldCancelAll,
+ isEntireTableSelected,
tableItemCount,
});
@@ -146,6 +147,9 @@ AssignmentTableCancelAction.propTypes = {
tableInstance: PropTypes.shape({
itemCount: PropTypes.number.isRequired,
columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ state: PropTypes.shape({
+ filters: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ }).isRequired,
}).isRequired,
};
diff --git a/src/components/learner-credit-management/AssignmentTableRemind.jsx b/src/components/learner-credit-management/AssignmentTableRemind.jsx
index d4eb2f00f1..5c2de29a36 100644
--- a/src/components/learner-credit-management/AssignmentTableRemind.jsx
+++ b/src/components/learner-credit-management/AssignmentTableRemind.jsx
@@ -8,7 +8,6 @@ import useRemindContentAssignments from './data/hooks/useRemindContentAssignment
import RemindAssignmentModal from './RemindAssignmentModal';
import { transformSelectedRows, useBudgetId, useSubsidyAccessPolicy } from './data';
import EVENT_NAMES from '../../eventTracking';
-import { getActiveTableColumnFilters } from '../../utils';
const calculateTotalToRemind = ({
assignmentUuids,
@@ -41,10 +40,7 @@ const AssignmentTableRemindAction = ({
totalSelectedRows,
} = transformSelectedRows(remindableRows);
- const activeFilters = getActiveTableColumnFilters(tableInstance.columns);
-
- // If entire table is selected and there are NO filters, hit remind-all endpoint. Otherwise, hit usual bulk remind.
- const shouldRemindAll = isEntireTableSelected && activeFilters.length === 0;
+ const { state: dataTableState } = tableInstance;
const {
remindButtonState,
@@ -52,11 +48,16 @@ const AssignmentTableRemindAction = ({
close,
isOpen,
open,
- } = useRemindContentAssignments(assignmentConfiguration.uuid, assignmentUuids, shouldRemindAll);
+ } = useRemindContentAssignments(
+ assignmentConfiguration.uuid,
+ assignmentUuids,
+ isEntireTableSelected,
+ dataTableState.filters,
+ );
const selectedRemindableRowCount = calculateTotalToRemind({
assignmentUuids,
- isEntireTableSelected: shouldRemindAll,
+ isEntireTableSelected,
learnerStateCounts,
});
@@ -152,6 +153,9 @@ AssignmentTableRemindAction.propTypes = {
tableInstance: PropTypes.shape({
columns: PropTypes.arrayOf(PropTypes.shape()).isRequired,
itemCount: PropTypes.number.isRequired,
+ state: PropTypes.shape({
+ filters: PropTypes.arrayOf(PropTypes.shape()).isRequired,
+ }).isRequired,
}).isRequired,
};
diff --git a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
index 74ebab3da9..ffd9e0d508 100644
--- a/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
+++ b/src/components/learner-credit-management/PendingAssignmentCancelButton.jsx
@@ -33,7 +33,7 @@ const PendingAssignmentCancelButton = ({ row, enterpriseId }) => {
close,
isOpen,
open,
- } = useCancelContentAssignments(assignmentConfiguration, [uuid]);
+ } = useCancelContentAssignments(assignmentConfiguration.uuid, [uuid]);
const sharedTrackEventMetadata = {
subsidyUuid,
@@ -117,7 +117,9 @@ PendingAssignmentCancelButton.propTypes = {
contentQuantity: PropTypes.number.isRequired,
learnerState: PropTypes.string.isRequired,
state: PropTypes.string.isRequired,
- assignmentConfiguration: PropTypes.string.isRequired,
+ assignmentConfiguration: PropTypes.shape({
+ uuid: PropTypes.string.isRequired,
+ }).isRequired,
learnerEmail: PropTypes.string,
uuid: PropTypes.string.isRequired,
}).isRequired,
diff --git a/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx b/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
index a88843a2fd..7cc7403b45 100644
--- a/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
+++ b/src/components/learner-credit-management/PendingAssignmentRemindButton.jsx
@@ -32,7 +32,7 @@ const PendingAssignmentRemindButton = ({ row, enterpriseId }) => {
close,
isOpen,
open,
- } = useRemindContentAssignments(assignmentConfiguration, [uuid]);
+ } = useRemindContentAssignments(assignmentConfiguration.uuid, [uuid]);
const sharedTrackEventMetadata = {
subsidyUuid,
@@ -115,7 +115,9 @@ PendingAssignmentRemindButton.propTypes = {
contentQuantity: PropTypes.number.isRequired,
learnerState: PropTypes.string.isRequired,
state: PropTypes.string.isRequired,
- assignmentConfiguration: PropTypes.string.isRequired,
+ assignmentConfiguration: PropTypes.shape({
+ uuid: PropTypes.string.isRequired,
+ }).isRequired,
learnerEmail: PropTypes.string,
uuid: PropTypes.string.isRequired,
}).isRequired,
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
index a21dc68c64..b7edc97823 100644
--- a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.js
@@ -6,11 +6,13 @@ import { useToggle } from '@edx/paragon';
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys } from '../constants';
import useBudgetId from './useBudgetId';
+import { applyFiltersToOptions } from './useBudgetContentAssignments';
const useCancelContentAssignments = (
assignmentConfigurationUuid,
assignmentUuids,
- cancelAll = false,
+ cancelAll,
+ tableFilters,
) => {
const [isOpen, open, close] = useToggle(false);
const [cancelButtonState, setCancelButtonState] = useState('default');
@@ -21,7 +23,9 @@ const useCancelContentAssignments = (
setCancelButtonState('pending');
try {
if (cancelAll) {
- await EnterpriseAccessApiService.cancelAllContentAssignments(assignmentConfigurationUuid);
+ const options = {};
+ applyFiltersToOptions(tableFilters, options);
+ await EnterpriseAccessApiService.cancelAllContentAssignments(assignmentConfigurationUuid, options);
} else {
await EnterpriseAccessApiService.cancelContentAssignments(assignmentConfigurationUuid, assignmentUuids);
}
@@ -33,7 +37,7 @@ const useCancelContentAssignments = (
logError(err);
setCancelButtonState('error');
}
- }, [assignmentConfigurationUuid, assignmentUuids, cancelAll, queryClient, subsidyAccessPolicyId]);
+ }, [assignmentConfigurationUuid, assignmentUuids, cancelAll, tableFilters, queryClient, subsidyAccessPolicyId]);
return {
cancelButtonState,
diff --git a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
index e8bcad9513..c78e8e4d1c 100644
--- a/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useCancelContentAssignments.test.jsx
@@ -104,6 +104,47 @@ describe('useCancelContentAssignments', () => {
});
});
+ it('should send a post request to cancel all assignments with filters', async () => {
+ EnterpriseAccessApiService.cancelAllContentAssignments.mockResolvedValueOnce({ status: 200 });
+ const tableFilters = [{
+ id: 'learnerState',
+ value: ['waiting'],
+ }];
+ const cancelAll = true;
+ const { result } = renderHook(
+ () => useCancelContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ [TEST_PENDING_ASSIGNMENT_UUID_1, TEST_PENDING_ASSIGNMENT_UUID_2],
+ cancelAll,
+ tableFilters,
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ cancelButtonState: 'default',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => act(() => result.current.cancelContentAssignments()));
+ const expectedFilterParams = { learnerState: 'waiting' };
+ expect(
+ EnterpriseAccessApiService.cancelAllContentAssignments,
+ ).toHaveBeenCalledWith(TEST_ASSIGNMENT_CONFIGURATION_UUID, expectedFilterParams);
+ expect(logError).toBeCalledTimes(0);
+
+ expect(result.current).toEqual({
+ cancelButtonState: 'complete',
+ cancelContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+
it('should handle assignment cancellation error', async () => {
const error = new Error('An error occurred');
EnterpriseAccessApiService.cancelContentAssignments.mockRejectedValueOnce(error);
diff --git a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
index aaeac80c36..8c70e6ef3c 100644
--- a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
+++ b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.js
@@ -6,11 +6,13 @@ import { useToggle } from '@edx/paragon';
import EnterpriseAccessApiService from '../../../../data/services/EnterpriseAccessApiService';
import { learnerCreditManagementQueryKeys } from '../constants';
import useBudgetId from './useBudgetId';
+import { applyFiltersToOptions } from './useBudgetContentAssignments';
const useRemindContentAssignments = (
assignmentConfigurationUuid,
assignmentUuids,
- remindAll = false,
+ remindAll,
+ tableFilters,
) => {
const [isOpen, open, close] = useToggle(false);
const [remindButtonState, setRemindButtonState] = useState('default');
@@ -21,7 +23,9 @@ const useRemindContentAssignments = (
setRemindButtonState('pending');
try {
if (remindAll) {
- await EnterpriseAccessApiService.remindAllContentAssignments(assignmentConfigurationUuid);
+ const options = {};
+ applyFiltersToOptions(tableFilters, options);
+ await EnterpriseAccessApiService.remindAllContentAssignments(assignmentConfigurationUuid, options);
} else {
await EnterpriseAccessApiService.remindContentAssignments(assignmentConfigurationUuid, assignmentUuids);
}
@@ -33,7 +37,7 @@ const useRemindContentAssignments = (
logError(err);
setRemindButtonState('error');
}
- }, [assignmentConfigurationUuid, assignmentUuids, remindAll, queryClient, subsidyAccessPolicyId]);
+ }, [assignmentConfigurationUuid, assignmentUuids, remindAll, tableFilters, queryClient, subsidyAccessPolicyId]);
return {
remindButtonState,
diff --git a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx
index 88133f99f1..daadc1ea7b 100644
--- a/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx
+++ b/src/components/learner-credit-management/data/hooks/useRemindContentAssignments.test.jsx
@@ -104,6 +104,47 @@ describe('useRemindContentAssignments', () => {
});
});
+ it('should send a post request to remind all assignments with filters', async () => {
+ EnterpriseAccessApiService.remindAllContentAssignments.mockResolvedValueOnce({ status: 200 });
+ const tableFilters = [{
+ id: 'learnerState',
+ value: ['waiting'],
+ }];
+ const remindAll = true;
+ const { result } = renderHook(
+ () => useRemindContentAssignments(
+ TEST_ASSIGNMENT_CONFIGURATION_UUID,
+ [TEST_PENDING_ASSIGNMENT_UUID_1, TEST_PENDING_ASSIGNMENT_UUID_2],
+ remindAll,
+ tableFilters,
+ ),
+ { wrapper },
+ );
+
+ expect(result.current).toEqual({
+ remindButtonState: 'default',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+
+ await waitFor(() => result.current.remindContentAssignments());
+ const expectedFilterParams = { learnerState: 'waiting' };
+ expect(
+ EnterpriseAccessApiService.remindAllContentAssignments,
+ ).toHaveBeenCalledWith(TEST_ASSIGNMENT_CONFIGURATION_UUID, expectedFilterParams);
+ expect(logError).toBeCalledTimes(0);
+
+ expect(result.current).toEqual({
+ remindButtonState: 'complete',
+ remindContentAssignments: expect.any(Function),
+ close: expect.any(Function),
+ isOpen: false,
+ open: expect.any(Function),
+ });
+ });
+
it('should handle assignment reminder error', async () => {
const error = new Error('An error occurred');
EnterpriseAccessApiService.remindContentAssignments.mockRejectedValueOnce(error);
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
index cd2a6e1efe..5ba011a48e 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConfigureStep.tsx
@@ -166,7 +166,7 @@ const SSOConfigConfigureStep = () => {
const returnToConnectStep = () => {
const connectStep = allSteps?.[0] as FormWorkflowStep;
dispatch?.(
- setStepAction({ step: connectStep })
+ setStepAction({ step: connectStep }),
);
};
diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
index bd0139db9a..d37d6d152d 100644
--- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
+++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigConnectStep.tsx
@@ -1,5 +1,7 @@
import React, { useState } from 'react';
-import { Container, Dropzone, Form, Stack } from '@edx/paragon';
+import {
+ Container, Dropzone, Form, Stack,
+} from '@edx/paragon';
import ValidatedFormRadio from '../../../forms/ValidatedFormRadio';
import ValidatedFormControl from '../../../forms/ValidatedFormControl';
diff --git a/src/data/services/EnterpriseAccessApiService.js b/src/data/services/EnterpriseAccessApiService.js
index 7313698db4..84f4bab6ce 100644
--- a/src/data/services/EnterpriseAccessApiService.js
+++ b/src/data/services/EnterpriseAccessApiService.js
@@ -187,8 +187,19 @@ class EnterpriseAccessApiService {
/**
* Cancel ALL content assignments for a specific AssignmentConfiguration.
*/
- static cancelAllContentAssignments(assignmentConfigurationUUID) {
- const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/cancel-all/`;
+ static cancelAllContentAssignments(assignmentConfigurationUUID, options = {}) {
+ const { learnerState, ...optionsRest } = options;
+ const params = {
+ ...snakeCaseObject(optionsRest),
+ };
+ if (learnerState) {
+ params.learner_state__in = learnerState;
+ }
+ const urlParams = new URLSearchParams(params);
+ let url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/cancel-all/`;
+ if (Object.keys(params).length > 0) {
+ url += `?${urlParams.toString()}`;
+ }
return EnterpriseAccessApiService.apiClient().post(url);
}
@@ -206,8 +217,19 @@ class EnterpriseAccessApiService {
/**
* Remind ALL content assignments for a specific AssignmentConfiguration.
*/
- static remindAllContentAssignments(assignmentConfigurationUUID) {
- const url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/remind-all/`;
+ static remindAllContentAssignments(assignmentConfigurationUUID, options = {}) {
+ const { learnerState, ...optionsRest } = options;
+ const params = {
+ ...snakeCaseObject(optionsRest),
+ };
+ if (learnerState) {
+ params.learner_state__in = learnerState;
+ }
+ const urlParams = new URLSearchParams(params);
+ let url = `${EnterpriseAccessApiService.baseUrl}/assignment-configurations/${assignmentConfigurationUUID}/admin/assignments/remind-all/`;
+ if (Object.keys(params).length > 0) {
+ url += `?${urlParams.toString()}`;
+ }
return EnterpriseAccessApiService.apiClient().post(url);
}
diff --git a/src/data/services/tests/EnterpriseAccessApiService.test.js b/src/data/services/tests/EnterpriseAccessApiService.test.js
index 864ef63277..c32fbeeaa9 100644
--- a/src/data/services/tests/EnterpriseAccessApiService.test.js
+++ b/src/data/services/tests/EnterpriseAccessApiService.test.js
@@ -213,16 +213,22 @@ describe('EnterpriseAccessApiService', () => {
});
test('cancelAllContentAssignments calls enterprise-access cancel-all POST API to cancel all assignments', () => {
- EnterpriseAccessApiService.cancelAllContentAssignments(mockAssignmentConfigurationUUID);
+ const options = {
+ learnerState: 'pending,waiting',
+ };
+ EnterpriseAccessApiService.cancelAllContentAssignments(mockAssignmentConfigurationUUID, options);
expect(axios.post).toBeCalledWith(
- `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/cancel-all/`,
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/cancel-all/?learner_state__in=pending%2Cwaiting`,
);
});
test('remindAllContentAssignments calls enterprise-access remind-all POST API to remind all learners', () => {
- EnterpriseAccessApiService.remindAllContentAssignments(mockAssignmentConfigurationUUID);
+ const options = {
+ learnerState: 'pending,waiting',
+ };
+ EnterpriseAccessApiService.remindAllContentAssignments(mockAssignmentConfigurationUUID, options);
expect(axios.post).toBeCalledWith(
- `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/remind-all/`,
+ `${enterpriseAccessBaseUrl}/api/v1/assignment-configurations/${mockAssignmentConfigurationUUID}/admin/assignments/remind-all/?learner_state__in=pending%2Cwaiting`,
);
});
});
From 17972097baa1e0b1592a9efb9de9dff1b82ac7d6 Mon Sep 17 00:00:00 2001
From: Mashal Malik <107556986+Mashal-m@users.noreply.github.com>
Date: Fri, 5 Jan 2024 19:03:31 +0500
Subject: [PATCH 121/124] feat: update react & react-dom to v17 (#1009)
* feat: update react & react-dom to v17
* chore: replace react truncate w react-lines-ellipsis
* fix: update snapshots
* build: set helmet w named component
* build: update jest pkgs
* build: update pkgs
* fix: fix test and lint
* build: update test pkg
* fix: fix test
* refactor: update package.json file
* build: update lock file
* fix: fix test
* refactor: update test file
* fix: fix test in numbercard
* fix: fix test in content highlight card
* fix: fix failing test that is coming from paragon upgrade
* fix: fix test
* refactor: add comment in test file
* fix: fix conflicts
* fix: update snapshots
* build: pin packages
* build: pin packages
* refactor: replace LinesEllipsis with paragon truncate
* refactor: remove extra expect
* build: uninstall react-lines-ellipsis
* refactor: edit authorization test file
* refactor: removed setTimeout from AuthorizatiionsConfigs
* refactor: reverted AuthorizationsConfigs test
* refactor: updated util getBudgetStatus and respective test to resolve test failure
* fix: fix test and remove caret from pkg
* fix: fix lint
* fix: fix indentation
* fix: fix lint error
* fix: update snapshots
* fix: upgrade paragon and fix test accordingly
* fix: update snapshots
* build: update pkg json file
* fix: update test file
* fix: update test file
* build: update edx brand
* build: remove unused pkg
* fix: fix failed test and remove caret
* refactor: update test script
---------
Co-authored-by: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
Co-authored-by: Muhammad Abdullah Waheed <42172960+abdullahwaheed@users.noreply.github.com>
---
package-lock.json | 6929 ++++++-----------
package.json | 25 +-
src/components/Admin/index.jsx | 2 +-
src/components/BrandStyles/index.jsx | 2 +-
src/components/CodeManagement/index.jsx | 2 +-
.../ContentHighlightCardItem.jsx | 12 +-
src/components/EnterpriseList/index.jsx | 2 +-
src/components/ErrorPage/index.jsx | 2 +-
src/components/ForbiddenPage/index.jsx | 2 +-
src/components/NotFoundPage/index.jsx | 2 +-
src/components/NumberCard/NumberCard.test.jsx | 4 +-
src/components/RequestCodesPage/index.jsx | 2 +-
.../tests/BudgetDetailPage.test.jsx | 2 +-
.../tests/EmailAddressTableCell.test.jsx | 4 +-
.../tests/AuthorizationsConfigs.test.tsx | 13 +-
src/setupTest.js | 2 +-
16 files changed, 2318 insertions(+), 4689 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index eba758d745..5d75ae3359 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,11 +12,11 @@
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
- "@edx/frontend-enterprise-catalog-search": "4.2.0",
- "@edx/frontend-enterprise-hotjar": "1.3.0",
- "@edx/frontend-enterprise-logistration": "3.2.0",
- "@edx/frontend-enterprise-utils": "3.2.0",
- "@edx/frontend-platform": "4.0.1",
+ "@edx/frontend-enterprise-catalog-search": "4.5.0",
+ "@edx/frontend-enterprise-hotjar": "1.4.0",
+ "@edx/frontend-enterprise-logistration": "3.4.0",
+ "@edx/frontend-enterprise-utils": "3.4.0",
+ "@edx/frontend-platform": "4.4.0",
"@edx/paragon": "20.46.3",
"@tanstack/react-query": "4.36.1",
"@tanstack/react-query-devtools": "4.36.1",
@@ -37,15 +37,14 @@
"lodash": "4.17.21",
"lodash.debounce": "4.0.8",
"prop-types": "15.7.2",
- "react": "16.14.0",
- "react-dom": "16.13.1",
- "react-helmet": "5.2.1",
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-helmet": "6.1.0",
"react-instantsearch-dom": "6.8.3",
"react-markdown": "6.0.0",
- "react-redux": "7.1.1",
+ "react-redux": "7.2.9",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
- "react-truncate": "^2.4.0",
"redux": "4.0.4",
"redux-devtools-extension": "2.13.8",
"redux-form": "8.3.8",
@@ -68,12 +67,12 @@
"@faker-js/faker": "^7.6.0",
"@testing-library/dom": "9.3.1",
"@testing-library/jest-dom": "5.16.5",
- "@testing-library/react": "11.2.7",
+ "@testing-library/react": "^11.2.7",
"@testing-library/react-hooks": "5.0.3",
"@testing-library/user-event": "12.8.3",
+ "@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"css-loader": "5.2.6",
"enzyme": "3.11.0",
- "enzyme-adapter-react-16": "1.15.6",
"husky": "0.14.3",
"identity-obj-proxy": "3.0.0",
"jest-canvas-mock": "^2.4.0",
@@ -81,7 +80,7 @@
"patch-package": "8.0.0",
"postcss": "8.4.24",
"react-dev-utils": "11.0.4",
- "react-test-renderer": "16.13.1",
+ "react-test-renderer": "^17.0.2",
"resize-observer-polyfill": "1.5.1",
"ts-jest": "^26.5.0"
},
@@ -97,35 +96,30 @@
},
"node_modules/@adobe/css-tools": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz",
- "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@algolia/cache-browser-local-storage": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.8.3.tgz",
- "integrity": "sha512-Cwc03hikHSUI+xvgUdN+H+f6jFyoDsC9fegzXzJ2nPn1YSN9EXzDMBnbrgl0sbl9iLGXe0EIGMYqR2giCv1wMQ==",
+ "license": "MIT",
"dependencies": {
"@algolia/cache-common": "4.8.3"
}
},
"node_modules/@algolia/cache-common": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.8.3.tgz",
- "integrity": "sha512-Cf7zZ2i6H+tLSBTkFePHhYvlgc9fnMPKsF9qTmiU38kFIGORy/TN2Fx5n1GBuRLIzaSXvcf+oHv1HvU0u1gE1g=="
+ "license": "MIT"
},
"node_modules/@algolia/cache-in-memory": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.8.3.tgz",
- "integrity": "sha512-+N7tkvmijXiDy2E7u1mM73AGEgGPWFmEmPeJS96oT46I98KXAwVPNYbcAqBE79YlixdXpkYJk41cFcORzNh+Iw==",
+ "license": "MIT",
"dependencies": {
"@algolia/cache-common": "4.8.3"
}
},
"node_modules/@algolia/client-account": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.8.3.tgz",
- "integrity": "sha512-Uku8LqnXBwfDCtsTCDYTUOz2/2oqcAQCKgaO0uGdIR8DTQENBXFQvzziambHdn9KuFuY+6Et9k1+cjpTPBDTBg==",
+ "license": "MIT",
"dependencies": {
"@algolia/client-common": "4.8.3",
"@algolia/client-search": "4.8.3",
@@ -134,8 +128,7 @@
},
"node_modules/@algolia/client-analytics": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.8.3.tgz",
- "integrity": "sha512-9ensIWmjYJprZ+YjAVSZdWUG05xEnbytENXp508X59tf34IMIX8BR2xl0RjAQODtxBdAteGxuKt5THX6U9tQLA==",
+ "license": "MIT",
"dependencies": {
"@algolia/client-common": "4.8.3",
"@algolia/client-search": "4.8.3",
@@ -145,8 +138,7 @@
},
"node_modules/@algolia/client-common": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.8.3.tgz",
- "integrity": "sha512-TU3623AEFAWUQlDTznkgAMSYo8lfS9pNs5QYDQzkvzWdqK0GBDWthwdRfo9iIsfxiR9qdCMHqwEu+AlZMVhNSA==",
+ "license": "MIT",
"dependencies": {
"@algolia/requester-common": "4.8.3",
"@algolia/transporter": "4.8.3"
@@ -154,8 +146,7 @@
},
"node_modules/@algolia/client-recommendation": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.8.3.tgz",
- "integrity": "sha512-qysGbmkcc6Agt29E38KWJq9JuxjGsyEYoKuX9K+P5HyQh08yR/BlRYrA8mB7vT/OIUHRGFToGO6Vq/rcg0NIOQ==",
+ "license": "MIT",
"dependencies": {
"@algolia/client-common": "4.8.3",
"@algolia/requester-common": "4.8.3",
@@ -164,8 +155,7 @@
},
"node_modules/@algolia/client-search": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.8.3.tgz",
- "integrity": "sha512-rAnvoy3GAhbzOQVniFcKVn1eM2NX77LearzYNCbtFrFYavG+hJI187bNVmajToiuGZ10FfJvK99X2OB1AzzezQ==",
+ "license": "MIT",
"dependencies": {
"@algolia/client-common": "4.8.3",
"@algolia/requester-common": "4.8.3",
@@ -174,47 +164,40 @@
},
"node_modules/@algolia/events": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz",
- "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ=="
+ "license": "MIT"
},
"node_modules/@algolia/logger-common": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.8.3.tgz",
- "integrity": "sha512-03wksHRbhl2DouEKnqWuUb64s1lV6kDAAabMCQ2Du1fb8X/WhDmxHC4UXMzypeOGlH5BZBsgVwSB7vsZLP3MZg=="
+ "license": "MIT"
},
"node_modules/@algolia/logger-console": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.8.3.tgz",
- "integrity": "sha512-Npt+hI4UF8t3TLMluL5utr9Gc11BjL5kDnGZOhDOAz5jYiSO2nrHMFmnpLT4Cy/u7a5t7EB5dlypuC4/AGStkA==",
+ "license": "MIT",
"dependencies": {
"@algolia/logger-common": "4.8.3"
}
},
"node_modules/@algolia/requester-browser-xhr": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.8.3.tgz",
- "integrity": "sha512-/LTTIpgEmEwkyhn8yXxDdBWqXqzlgw5w2PtTpIwkSlP2/jDwdR/9w1TkFzhNbJ81ki6LAEQM5mSwoTTnbIIecg==",
+ "license": "MIT",
"dependencies": {
"@algolia/requester-common": "4.8.3"
}
},
"node_modules/@algolia/requester-common": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.8.3.tgz",
- "integrity": "sha512-+Yo9vBkofoKR1SCqqtMnmnfq9yt/BiaDewY/6bYSMNxSYCnu2Fw1JKSIaf/4zos09PMSsxGpLohZwGas3+0GDQ=="
+ "license": "MIT"
},
"node_modules/@algolia/requester-node-http": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.8.3.tgz",
- "integrity": "sha512-k2fiKIeMIFqgC01FnzII6kqC2GQBAfbNaUX4k7QCPa6P8t4sp2xE6fImOUiztLnnL3C9X9ZX6Fw3L+cudi7jvQ==",
+ "license": "MIT",
"dependencies": {
"@algolia/requester-common": "4.8.3"
}
},
"node_modules/@algolia/transporter": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.8.3.tgz",
- "integrity": "sha512-nU7fy2iU8snxATlsks0MjMyv97QJWQmOVwTjDc+KZ4+nue8CLcgm4LA4dsTBqvxeCQIoEtt3n72GwXcaqiJSjQ==",
+ "license": "MIT",
"dependencies": {
"@algolia/cache-common": "4.8.3",
"@algolia/logger-common": "4.8.3",
@@ -223,8 +206,7 @@
},
"node_modules/@ampproject/remapping": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "license": "Apache-2.0",
"dependencies": {
"@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9"
@@ -235,8 +217,7 @@
},
"node_modules/@babel/cli": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.21.0.tgz",
- "integrity": "sha512-xi7CxyS8XjSyiwUGCfwf+brtJxjW1/ZTcBUkP10xawIEXLX5HzLn+3aXkgxozcP2UhRhtKTmQurw9Uaes7jZrA==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
"commander": "^4.0.1",
@@ -263,8 +244,7 @@
},
"node_modules/@babel/code-frame": {
"version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
- "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
+ "license": "MIT",
"dependencies": {
"@babel/highlight": "^7.18.6"
},
@@ -274,16 +254,14 @@
},
"node_modules/@babel/compat-data": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz",
- "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
- "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
+ "license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.21.4",
@@ -311,8 +289,7 @@
},
"node_modules/@babel/generator": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz",
- "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.22.3",
"@jridgewell/gen-mapping": "^0.3.2",
@@ -325,8 +302,7 @@
},
"node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -338,8 +314,7 @@
},
"node_modules/@babel/helper-annotate-as-pure": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
- "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.18.6"
},
@@ -349,8 +324,7 @@
},
"node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz",
- "integrity": "sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.22.3"
},
@@ -360,8 +334,7 @@
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz",
- "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==",
+ "license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.22.0",
"@babel/helper-validator-option": "^7.21.0",
@@ -378,21 +351,18 @@
},
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+ "license": "ISC"
},
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz",
- "integrity": "sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-environment-visitor": "^7.22.1",
@@ -413,8 +383,7 @@
},
"node_modules/@babel/helper-create-regexp-features-plugin": {
"version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz",
- "integrity": "sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"regexpu-core": "^5.3.1",
@@ -429,8 +398,7 @@
},
"node_modules/@babel/helper-define-polyfill-provider": {
"version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
- "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.17.7",
"@babel/helper-plugin-utils": "^7.16.7",
@@ -445,16 +413,14 @@
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz",
- "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
- "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+ "license": "MIT",
"dependencies": {
"@babel/template": "^7.20.7",
"@babel/types": "^7.21.0"
@@ -465,8 +431,7 @@
},
"node_modules/@babel/helper-hoist-variables": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.18.6"
},
@@ -476,8 +441,7 @@
},
"node_modules/@babel/helper-member-expression-to-functions": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz",
- "integrity": "sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.22.3"
},
@@ -487,8 +451,7 @@
},
"node_modules/@babel/helper-module-imports": {
"version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
- "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.21.4"
},
@@ -498,8 +461,7 @@
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz",
- "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.1",
"@babel/helper-module-imports": "^7.21.4",
@@ -516,8 +478,7 @@
},
"node_modules/@babel/helper-optimise-call-expression": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
- "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.18.6"
},
@@ -527,16 +488,14 @@
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz",
- "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-remap-async-to-generator": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
- "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-environment-visitor": "^7.18.9",
@@ -552,8 +511,7 @@
},
"node_modules/@babel/helper-replace-supers": {
"version": "7.22.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz",
- "integrity": "sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.1",
"@babel/helper-member-expression-to-functions": "^7.22.0",
@@ -568,8 +526,7 @@
},
"node_modules/@babel/helper-simple-access": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz",
- "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.21.5"
},
@@ -579,8 +536,7 @@
},
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
"version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
- "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.20.0"
},
@@ -590,8 +546,7 @@
},
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.18.6"
},
@@ -601,32 +556,28 @@
},
"node_modules/@babel/helper-string-parser": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
- "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.19.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
- "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
- "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-wrap-function": {
"version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz",
- "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-function-name": "^7.19.0",
"@babel/template": "^7.18.10",
@@ -639,8 +590,7 @@
},
"node_modules/@babel/helpers": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz",
- "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==",
+ "license": "MIT",
"dependencies": {
"@babel/template": "^7.21.9",
"@babel/traverse": "^7.22.1",
@@ -652,8 +602,7 @@
},
"node_modules/@babel/highlight": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
- "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.18.6",
"chalk": "^2.0.0",
@@ -665,8 +614,7 @@
},
"node_modules/@babel/parser": {
"version": "7.22.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz",
- "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==",
+ "license": "MIT",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -676,8 +624,7 @@
},
"node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
- "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -690,8 +637,7 @@
},
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz",
- "integrity": "sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
@@ -706,8 +652,7 @@
},
"node_modules/@babel/plugin-proposal-async-generator-functions": {
"version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz",
- "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-plugin-utils": "^7.20.2",
@@ -723,8 +668,7 @@
},
"node_modules/@babel/plugin-proposal-class-properties": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
- "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -738,8 +682,7 @@
},
"node_modules/@babel/plugin-proposal-class-static-block": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz",
- "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.21.0",
"@babel/helper-plugin-utils": "^7.20.2",
@@ -754,8 +697,7 @@
},
"node_modules/@babel/plugin-proposal-dynamic-import": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
- "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3"
@@ -769,8 +711,7 @@
},
"node_modules/@babel/plugin-proposal-export-namespace-from": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
- "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.9",
"@babel/plugin-syntax-export-namespace-from": "^7.8.3"
@@ -784,8 +725,7 @@
},
"node_modules/@babel/plugin-proposal-json-strings": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
- "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/plugin-syntax-json-strings": "^7.8.3"
@@ -799,8 +739,7 @@
},
"node_modules/@babel/plugin-proposal-logical-assignment-operators": {
"version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz",
- "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
@@ -814,8 +753,7 @@
},
"node_modules/@babel/plugin-proposal-nullish-coalescing-operator": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
- "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
@@ -829,8 +767,7 @@
},
"node_modules/@babel/plugin-proposal-numeric-separator": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
- "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/plugin-syntax-numeric-separator": "^7.10.4"
@@ -844,8 +781,7 @@
},
"node_modules/@babel/plugin-proposal-object-rest-spread": {
"version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
- "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
+ "license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.20.5",
"@babel/helper-compilation-targets": "^7.20.7",
@@ -862,8 +798,7 @@
},
"node_modules/@babel/plugin-proposal-optional-catch-binding": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
- "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
@@ -877,8 +812,7 @@
},
"node_modules/@babel/plugin-proposal-optional-chaining": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
- "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
@@ -893,8 +827,7 @@
},
"node_modules/@babel/plugin-proposal-private-methods": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
- "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -908,8 +841,7 @@
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
- "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-create-class-features-plugin": "^7.21.0",
@@ -925,8 +857,7 @@
},
"node_modules/@babel/plugin-proposal-unicode-property-regex": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
- "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -940,8 +871,7 @@
},
"node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -951,8 +881,7 @@
},
"node_modules/@babel/plugin-syntax-bigint": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
- "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -962,8 +891,7 @@
},
"node_modules/@babel/plugin-syntax-class-properties": {
"version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
- "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.12.13"
},
@@ -973,8 +901,7 @@
},
"node_modules/@babel/plugin-syntax-class-static-block": {
"version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
- "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
},
@@ -987,8 +914,7 @@
},
"node_modules/@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -998,8 +924,7 @@
},
"node_modules/@babel/plugin-syntax-export-namespace-from": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
- "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.3"
},
@@ -1009,8 +934,7 @@
},
"node_modules/@babel/plugin-syntax-import-assertions": {
"version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz",
- "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.19.0"
},
@@ -1023,8 +947,7 @@
},
"node_modules/@babel/plugin-syntax-import-meta": {
"version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
- "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
},
@@ -1034,8 +957,7 @@
},
"node_modules/@babel/plugin-syntax-json-strings": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -1045,8 +967,7 @@
},
"node_modules/@babel/plugin-syntax-jsx": {
"version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz",
- "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2"
},
@@ -1059,8 +980,7 @@
},
"node_modules/@babel/plugin-syntax-logical-assignment-operators": {
"version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
- "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
},
@@ -1070,8 +990,7 @@
},
"node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -1081,8 +1000,7 @@
},
"node_modules/@babel/plugin-syntax-numeric-separator": {
"version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
- "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.10.4"
},
@@ -1092,8 +1010,7 @@
},
"node_modules/@babel/plugin-syntax-object-rest-spread": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -1103,8 +1020,7 @@
},
"node_modules/@babel/plugin-syntax-optional-catch-binding": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -1114,8 +1030,7 @@
},
"node_modules/@babel/plugin-syntax-optional-chaining": {
"version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.8.0"
},
@@ -1125,8 +1040,7 @@
},
"node_modules/@babel/plugin-syntax-private-property-in-object": {
"version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
- "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
},
@@ -1139,8 +1053,7 @@
},
"node_modules/@babel/plugin-syntax-top-level-await": {
"version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.14.5"
},
@@ -1153,8 +1066,7 @@
},
"node_modules/@babel/plugin-syntax-typescript": {
"version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz",
- "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2"
},
@@ -1167,8 +1079,7 @@
},
"node_modules/@babel/plugin-transform-arrow-functions": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz",
- "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5"
},
@@ -1181,8 +1092,7 @@
},
"node_modules/@babel/plugin-transform-async-to-generator": {
"version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz",
- "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.18.6",
"@babel/helper-plugin-utils": "^7.20.2",
@@ -1197,8 +1107,7 @@
},
"node_modules/@babel/plugin-transform-block-scoped-functions": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
- "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1211,8 +1120,7 @@
},
"node_modules/@babel/plugin-transform-block-scoping": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz",
- "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2"
},
@@ -1225,8 +1133,7 @@
},
"node_modules/@babel/plugin-transform-classes": {
"version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz",
- "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-compilation-targets": "^7.20.7",
@@ -1247,8 +1154,7 @@
},
"node_modules/@babel/plugin-transform-computed-properties": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz",
- "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5",
"@babel/template": "^7.20.7"
@@ -1262,8 +1168,7 @@
},
"node_modules/@babel/plugin-transform-destructuring": {
"version": "7.21.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz",
- "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2"
},
@@ -1276,8 +1181,7 @@
},
"node_modules/@babel/plugin-transform-dotall-regex": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
- "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1291,8 +1195,7 @@
},
"node_modules/@babel/plugin-transform-duplicate-keys": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz",
- "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.9"
},
@@ -1305,8 +1208,7 @@
},
"node_modules/@babel/plugin-transform-exponentiation-operator": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
- "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1320,8 +1222,7 @@
},
"node_modules/@babel/plugin-transform-for-of": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz",
- "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5"
},
@@ -1334,8 +1235,7 @@
},
"node_modules/@babel/plugin-transform-function-name": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz",
- "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.18.9",
"@babel/helper-function-name": "^7.18.9",
@@ -1350,8 +1250,7 @@
},
"node_modules/@babel/plugin-transform-literals": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz",
- "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.9"
},
@@ -1364,8 +1263,7 @@
},
"node_modules/@babel/plugin-transform-member-expression-literals": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
- "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1378,8 +1276,7 @@
},
"node_modules/@babel/plugin-transform-modules-amd": {
"version": "7.20.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz",
- "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.20.11",
"@babel/helper-plugin-utils": "^7.20.2"
@@ -1393,8 +1290,7 @@
},
"node_modules/@babel/plugin-transform-modules-commonjs": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz",
- "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.21.5",
"@babel/helper-plugin-utils": "^7.21.5",
@@ -1409,8 +1305,7 @@
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz",
- "integrity": "sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-hoist-variables": "^7.18.6",
"@babel/helper-module-transforms": "^7.22.1",
@@ -1426,8 +1321,7 @@
},
"node_modules/@babel/plugin-transform-modules-umd": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
- "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1441,8 +1335,7 @@
},
"node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz",
- "integrity": "sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.22.1",
"@babel/helper-plugin-utils": "^7.21.5"
@@ -1456,8 +1349,7 @@
},
"node_modules/@babel/plugin-transform-new-target": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz",
- "integrity": "sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5"
},
@@ -1470,8 +1362,7 @@
},
"node_modules/@babel/plugin-transform-object-super": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
- "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/helper-replace-supers": "^7.18.6"
@@ -1485,8 +1376,7 @@
},
"node_modules/@babel/plugin-transform-optional-chaining": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz",
- "integrity": "sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
@@ -1501,8 +1391,7 @@
},
"node_modules/@babel/plugin-transform-parameters": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz",
- "integrity": "sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5"
},
@@ -1515,8 +1404,7 @@
},
"node_modules/@babel/plugin-transform-property-literals": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
- "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1529,8 +1417,7 @@
},
"node_modules/@babel/plugin-transform-react-constant-elements": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.3.tgz",
- "integrity": "sha512-b5J6muxQYp4H7loAQv/c7GO5cPuRA6H5hx4gO+/Hn+Cu9MRQU0PNiUoWq1L//8sq6kFSNxGXFb2XTaUfa9y+Pg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5"
},
@@ -1543,8 +1430,7 @@
},
"node_modules/@babel/plugin-transform-react-display-name": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz",
- "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1557,8 +1443,7 @@
},
"node_modules/@babel/plugin-transform-react-jsx": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz",
- "integrity": "sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-module-imports": "^7.21.4",
@@ -1575,8 +1460,7 @@
},
"node_modules/@babel/plugin-transform-react-jsx-development": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz",
- "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==",
+ "license": "MIT",
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.18.6"
},
@@ -1589,8 +1473,7 @@
},
"node_modules/@babel/plugin-transform-react-pure-annotations": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz",
- "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1604,8 +1487,7 @@
},
"node_modules/@babel/plugin-transform-regenerator": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz",
- "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5",
"regenerator-transform": "^0.15.1"
@@ -1619,8 +1501,7 @@
},
"node_modules/@babel/plugin-transform-reserved-words": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
- "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1633,8 +1514,7 @@
},
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.12.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz",
- "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.12.1",
"@babel/helper-plugin-utils": "^7.10.4",
@@ -1647,16 +1527,14 @@
},
"node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
"version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/@babel/plugin-transform-shorthand-properties": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
- "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1669,8 +1547,7 @@
},
"node_modules/@babel/plugin-transform-spread": {
"version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz",
- "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0"
@@ -1684,8 +1561,7 @@
},
"node_modules/@babel/plugin-transform-sticky-regex": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
- "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@@ -1698,8 +1574,7 @@
},
"node_modules/@babel/plugin-transform-template-literals": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz",
- "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.9"
},
@@ -1712,8 +1587,7 @@
},
"node_modules/@babel/plugin-transform-typeof-symbol": {
"version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz",
- "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.9"
},
@@ -1726,8 +1600,7 @@
},
"node_modules/@babel/plugin-transform-typescript": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.3.tgz",
- "integrity": "sha512-pyjnCIniO5PNaEuGxT28h0HbMru3qCVrMqVgVOz/krComdIrY9W6FCLBq9NWHY8HDGaUlan+UhmZElDENIfCcw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-create-class-features-plugin": "^7.22.1",
@@ -1743,8 +1616,7 @@
},
"node_modules/@babel/plugin-transform-unicode-escapes": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz",
- "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5"
},
@@ -1757,8 +1629,7 @@
},
"node_modules/@babel/plugin-transform-unicode-regex": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
- "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1772,8 +1643,7 @@
},
"node_modules/@babel/preset-env": {
"version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.4.tgz",
- "integrity": "sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==",
+ "license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.21.4",
"@babel/helper-compilation-targets": "^7.21.4",
@@ -1860,8 +1730,7 @@
},
"node_modules/@babel/preset-modules": {
"version": "0.1.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
- "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
@@ -1875,8 +1744,7 @@
},
"node_modules/@babel/preset-react": {
"version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz",
- "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/helper-validator-option": "^7.18.6",
@@ -1894,8 +1762,7 @@
},
"node_modules/@babel/preset-typescript": {
"version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz",
- "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.21.5",
"@babel/helper-validator-option": "^7.21.0",
@@ -1912,13 +1779,11 @@
},
"node_modules/@babel/regjsgen": {
"version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
- "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
+ "license": "MIT"
},
"node_modules/@babel/runtime": {
"version": "7.22.3",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz",
- "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==",
+ "license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.13.11"
},
@@ -1927,9 +1792,9 @@
}
},
"node_modules/@babel/runtime-corejs3": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.22.5.tgz",
- "integrity": "sha512-TNPDN6aBFaUox2Lu+H/Y1dKKQgr4ucz/FGyCz67RVYLsBpVpUFf1dDngzg+Od8aqbrqwyztkaZjtWCZEUOT8zA==",
+ "version": "7.22.6",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
"core-js-pure": "^3.30.2",
"regenerator-runtime": "^0.13.11"
@@ -1940,18 +1805,16 @@
},
"node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": {
"version": "0.13.11",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
- "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
"version": "0.13.11",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
- "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ "license": "MIT"
},
"node_modules/@babel/template": {
"version": "7.21.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz",
- "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.21.4",
"@babel/parser": "^7.21.9",
@@ -1963,8 +1826,7 @@
},
"node_modules/@babel/traverse": {
"version": "7.22.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz",
- "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.21.4",
"@babel/generator": "^7.22.3",
@@ -1983,8 +1845,7 @@
},
"node_modules/@babel/types": {
"version": "7.22.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz",
- "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.21.5",
"@babel/helper-validator-identifier": "^7.19.1",
@@ -1996,13 +1857,11 @@
},
"node_modules/@bcoe/v8-coverage": {
"version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
+ "license": "MIT"
},
"node_modules/@cnakazawa/watch": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
- "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==",
+ "license": "Apache-2.0",
"dependencies": {
"exec-sh": "^0.3.2",
"minimist": "^1.2.0"
@@ -2016,16 +1875,14 @@
},
"node_modules/@cospired/i18n-iso-languages": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@cospired/i18n-iso-languages/-/i18n-iso-languages-2.2.0.tgz",
- "integrity": "sha512-hywY9u9apWGeLxQuRcXw7IW0XkMdXum/hr3TpmHY2fAbXMTFlhhkPCdsQeHzjxMQwTnMgXaZ4j4WOCwKtlDRCQ==",
+ "license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/@csstools/cascade-layer-name-parser": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.2.tgz",
- "integrity": "sha512-xm7Mgwej/wBfLoK0K5LfntmPJzoULayl1XZY9JYgQgT29JiqNw++sLnx95u5y9zCihblzkyaRYJrsRMhIBzRdg==",
+ "license": "MIT",
"engines": {
"node": "^14 || ^16 || >=18"
},
@@ -2040,8 +1897,6 @@
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz",
- "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==",
"funding": [
{
"type": "github",
@@ -2052,6 +1907,7 @@
"url": "https://opencollective.com/csstools"
}
],
+ "license": "MIT",
"engines": {
"node": "^14 || ^16 || >=18"
},
@@ -2061,8 +1917,7 @@
},
"node_modules/@csstools/css-tokenizer": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz",
- "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==",
+ "license": "MIT",
"engines": {
"node": "^14 || ^16 || >=18"
},
@@ -2073,8 +1928,6 @@
},
"node_modules/@csstools/media-query-list-parser": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.0.tgz",
- "integrity": "sha512-MXkR+TeaS2q9IkpyO6jVCdtA/bfpABJxIrfkLswThFN8EZZgI2RfAHhm6sDNDuYV25d5+b8Lj1fpTccIcSLPsQ==",
"funding": [
{
"type": "github",
@@ -2085,6 +1938,7 @@
"url": "https://opencollective.com/csstools"
}
],
+ "license": "MIT",
"engines": {
"node": "^14 || ^16 || >=18"
},
@@ -2095,28 +1949,25 @@
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
- "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
- "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/@edx/brand": {
"name": "@openedx/brand-openedx",
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@openedx/brand-openedx/-/brand-openedx-1.2.2.tgz",
- "integrity": "sha512-mBvxR7aB9290j9+h3d/9G8VkG1b8ecLSmlxc0vskfm7DL/fKUzFmHAj3PI7Z4kkwCQOL4QT5mJHJKC0ZFf7qvQ=="
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@openedx/brand-openedx/-/brand-openedx-1.2.3.tgz",
+ "integrity": "sha512-Dn9CtpC8fovh++Xi4NF5NJoeR9yU2yXZnV9IujxIyGd/dn0Phq5t6dzJVfupwq09mpDnzJv7egA8Znz/3ljO+w=="
},
"node_modules/@edx/browserslist-config": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.0.0.tgz",
- "integrity": "sha512-gLAlpz9Y5VruxqiUBTROG7PvouIxoMc6dvhvNpXUDHRN0KEke+zBj+zJ4frL9kGbkeex273nzSazbG42hNDLrg==",
- "dev": true
+ "dev": true,
+ "license": "AGPL-3.0"
},
"node_modules/@edx/eslint-config": {
"version": "4.0.0-alpha.1",
- "resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.0.0-alpha.1.tgz",
- "integrity": "sha512-+Hx8r6z+DdwryqluA0MF7S/RCurakzQs4AfNufWu2BLcIaf/Jot19NfcxS4Dzo8WUWiBL+lo4/42fclHHrvk9Q==",
+ "license": "MIT",
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
@@ -2131,8 +1982,7 @@
},
"node_modules/@edx/frontend-build": {
"version": "12.9.0-alpha.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.9.0-alpha.1.tgz",
- "integrity": "sha512-boHfp7yzUn5JptWGi2jK/EMaIlJ4nzjZBp7PW19vkNokbuQ3ansJsRvMscFg83tw0lXRTKCPN1y0uKg92AMizg==",
+ "license": "AGPL-3.0",
"dependencies": {
"@babel/cli": "7.21.0",
"@babel/core": "7.21.4",
@@ -2204,13 +2054,11 @@
},
"node_modules/@edx/frontend-build/node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
+ "license": "MIT"
},
"node_modules/@edx/frontend-build/node_modules/@types/jest": {
"version": "26.0.24",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz",
- "integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==",
+ "license": "MIT",
"dependencies": {
"jest-diff": "^26.0.0",
"pretty-format": "^26.0.0"
@@ -2218,8 +2066,7 @@
},
"node_modules/@edx/frontend-build/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -2232,16 +2079,14 @@
},
"node_modules/@edx/frontend-build/node_modules/array-union": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@edx/frontend-build/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -2255,8 +2100,7 @@
},
"node_modules/@edx/frontend-build/node_modules/clean-css": {
"version": "5.3.1",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
- "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==",
+ "license": "MIT",
"dependencies": {
"source-map": "~0.6.0"
},
@@ -2266,16 +2110,14 @@
},
"node_modules/@edx/frontend-build/node_modules/clean-css/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@edx/frontend-build/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -2285,21 +2127,18 @@
},
"node_modules/@edx/frontend-build/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@edx/frontend-build/node_modules/commander": {
"version": "8.3.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
- "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/@edx/frontend-build/node_modules/cosmiconfig": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
- "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+ "license": "MIT",
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.1.0",
@@ -2313,8 +2152,7 @@
},
"node_modules/@edx/frontend-build/node_modules/css-loader": {
"version": "5.2.7",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz",
- "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==",
+ "license": "MIT",
"dependencies": {
"icss-utils": "^5.1.0",
"loader-utils": "^2.0.0",
@@ -2340,8 +2178,7 @@
},
"node_modules/@edx/frontend-build/node_modules/css-loader/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -2354,8 +2191,7 @@
},
"node_modules/@edx/frontend-build/node_modules/css-select": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.0.1",
@@ -2369,8 +2205,7 @@
},
"node_modules/@edx/frontend-build/node_modules/cssnano": {
"version": "5.1.15",
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz",
- "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
+ "license": "MIT",
"dependencies": {
"cssnano-preset-default": "^5.2.14",
"lilconfig": "^2.0.3",
@@ -2389,8 +2224,7 @@
},
"node_modules/@edx/frontend-build/node_modules/cssnano-preset-default": {
"version": "5.2.14",
- "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
- "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
+ "license": "MIT",
"dependencies": {
"css-declaration-sorter": "^6.3.1",
"cssnano-utils": "^3.1.0",
@@ -2431,8 +2265,7 @@
},
"node_modules/@edx/frontend-build/node_modules/cssnano-utils": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
- "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
+ "license": "MIT",
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2442,16 +2275,14 @@
},
"node_modules/@edx/frontend-build/node_modules/diff-sequences": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/@edx/frontend-build/node_modules/dom-serializer": {
"version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
@@ -2463,8 +2294,7 @@
},
"node_modules/@edx/frontend-build/node_modules/domhandler": {
"version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.2.0"
},
@@ -2477,8 +2307,7 @@
},
"node_modules/@edx/frontend-build/node_modules/domutils": {
"version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
@@ -2490,16 +2319,14 @@
},
"node_modules/@edx/frontend-build/node_modules/dotenv": {
"version": "8.6.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
- "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=10"
}
},
"node_modules/@edx/frontend-build/node_modules/dotenv-webpack": {
"version": "7.1.1",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-7.1.1.tgz",
- "integrity": "sha512-xw/19VqHDkXALtBOJAnnrSU/AZDIQRXczAmJyp0lZv6SH2aBLzUTl96W1MVryJZ7okZ+djZS4Gj4KlZ0xP7deA==",
+ "license": "MIT",
"dependencies": {
"dotenv-defaults": "^2.0.2"
},
@@ -2512,16 +2339,14 @@
},
"node_modules/@edx/frontend-build/node_modules/entities": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "license": "BSD-2-Clause",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/@edx/frontend-build/node_modules/escape-string-regexp": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -2531,16 +2356,14 @@
},
"node_modules/@edx/frontend-build/node_modules/filesize": {
"version": "8.0.7",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
- "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/@edx/frontend-build/node_modules/globby": {
"version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "license": "MIT",
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
@@ -2558,8 +2381,7 @@
},
"node_modules/@edx/frontend-build/node_modules/gzip-size": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
- "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+ "license": "MIT",
"dependencies": {
"duplexer": "^0.1.2"
},
@@ -2572,16 +2394,14 @@
},
"node_modules/@edx/frontend-build/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@edx/frontend-build/node_modules/html-minifier-terser": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
- "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+ "license": "MIT",
"dependencies": {
"camel-case": "^4.1.2",
"clean-css": "^5.2.2",
@@ -2600,8 +2420,7 @@
},
"node_modules/@edx/frontend-build/node_modules/html-webpack-plugin": {
"version": "5.5.0",
- "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
- "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+ "license": "MIT",
"dependencies": {
"@types/html-minifier-terser": "^6.0.0",
"html-minifier-terser": "^6.0.2",
@@ -2622,8 +2441,6 @@
},
"node_modules/@edx/frontend-build/node_modules/htmlparser2": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
- "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -2631,6 +2448,7 @@
"url": "https://github.com/sponsors/fb55"
}
],
+ "license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.0.0",
@@ -2640,8 +2458,7 @@
},
"node_modules/@edx/frontend-build/node_modules/immer": {
"version": "9.0.19",
- "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz",
- "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -2649,8 +2466,7 @@
},
"node_modules/@edx/frontend-build/node_modules/jest-diff": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^26.6.2",
@@ -2663,16 +2479,14 @@
},
"node_modules/@edx/frontend-build/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/@edx/frontend-build/node_modules/mini-css-extract-plugin": {
"version": "1.6.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz",
- "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==",
+ "license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0",
@@ -2691,8 +2505,7 @@
},
"node_modules/@edx/frontend-build/node_modules/normalize-url": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
- "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -2702,8 +2515,7 @@
},
"node_modules/@edx/frontend-build/node_modules/open": {
"version": "8.4.0",
- "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
- "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+ "license": "MIT",
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
@@ -2718,8 +2530,6 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss": {
"version": "8.4.21",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
- "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
"funding": [
{
"type": "opencollective",
@@ -2730,6 +2540,7 @@
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
+ "license": "MIT",
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
@@ -2741,8 +2552,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-calc": {
"version": "8.2.4",
- "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
- "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
+ "license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.0.9",
"postcss-value-parser": "^4.2.0"
@@ -2753,8 +2563,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-colormin": {
"version": "5.3.1",
- "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
- "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"caniuse-api": "^3.0.0",
@@ -2770,8 +2579,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-convert-values": {
"version": "5.1.3",
- "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
- "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"postcss-value-parser": "^4.2.0"
@@ -2785,8 +2593,6 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-custom-media": {
"version": "9.1.4",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.4.tgz",
- "integrity": "sha512-4A7WEG3iIyKwfpxL5bkuSlHoHHGRTHl0212Z3uvpwJPyVfZJlkZAQNNgVC+oogrJgksDnfKyuuMbG6HafZPW8Q==",
"funding": [
{
"type": "github",
@@ -2797,6 +2603,7 @@
"url": "https://opencollective.com/csstools"
}
],
+ "license": "MIT",
"dependencies": {
"@csstools/cascade-layer-name-parser": "^1.0.2",
"@csstools/css-parser-algorithms": "^2.1.1",
@@ -2812,8 +2619,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-discard-comments": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
- "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
+ "license": "MIT",
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2823,8 +2629,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-discard-duplicates": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
- "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
+ "license": "MIT",
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2834,8 +2639,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-discard-empty": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
- "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
+ "license": "MIT",
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2845,8 +2649,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-discard-overridden": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
- "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
+ "license": "MIT",
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2856,8 +2659,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-merge-longhand": {
"version": "5.1.7",
- "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
- "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0",
"stylehacks": "^5.1.1"
@@ -2871,8 +2673,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-merge-rules": {
"version": "5.1.4",
- "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
- "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"caniuse-api": "^3.0.0",
@@ -2888,8 +2689,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-minify-font-values": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
- "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -2902,8 +2702,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-minify-gradients": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
- "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
+ "license": "MIT",
"dependencies": {
"colord": "^2.9.1",
"cssnano-utils": "^3.1.0",
@@ -2918,8 +2717,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-minify-params": {
"version": "5.1.4",
- "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
- "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"cssnano-utils": "^3.1.0",
@@ -2934,8 +2732,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-minify-selectors": {
"version": "5.2.1",
- "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
- "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
+ "license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.0.5"
},
@@ -2948,8 +2745,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-charset": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
- "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
+ "license": "MIT",
"engines": {
"node": "^10 || ^12 || >=14.0"
},
@@ -2959,8 +2755,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-display-values": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
- "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -2973,8 +2768,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-positions": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
- "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -2987,8 +2781,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-repeat-style": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
- "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3001,8 +2794,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-string": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
- "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3015,8 +2807,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-timing-functions": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
- "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3029,8 +2820,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-unicode": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
- "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"postcss-value-parser": "^4.2.0"
@@ -3044,8 +2834,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-url": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
- "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
+ "license": "MIT",
"dependencies": {
"normalize-url": "^6.0.1",
"postcss-value-parser": "^4.2.0"
@@ -3059,8 +2848,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-normalize-whitespace": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
- "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3073,8 +2861,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-ordered-values": {
"version": "5.1.3",
- "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
- "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
+ "license": "MIT",
"dependencies": {
"cssnano-utils": "^3.1.0",
"postcss-value-parser": "^4.2.0"
@@ -3088,8 +2875,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-reduce-initial": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
- "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"caniuse-api": "^3.0.0"
@@ -3103,8 +2889,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-reduce-transforms": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
- "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
@@ -3117,8 +2902,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-svgo": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
- "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
+ "license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0",
"svgo": "^2.7.0"
@@ -3132,8 +2916,7 @@
},
"node_modules/@edx/frontend-build/node_modules/postcss-unique-selectors": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
- "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
+ "license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.0.5"
},
@@ -3146,8 +2929,7 @@
},
"node_modules/@edx/frontend-build/node_modules/pretty-error": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
- "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+ "license": "MIT",
"dependencies": {
"lodash": "^4.17.20",
"renderkid": "^3.0.0"
@@ -3155,8 +2937,7 @@
},
"node_modules/@edx/frontend-build/node_modules/react-dev-utils": {
"version": "12.0.1",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
- "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.16.0",
"address": "^1.1.2",
@@ -3189,8 +2970,7 @@
},
"node_modules/@edx/frontend-build/node_modules/react-dev-utils/node_modules/fork-ts-checker-webpack-plugin": {
"version": "6.5.2",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz",
- "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
@@ -3227,16 +3007,14 @@
},
"node_modules/@edx/frontend-build/node_modules/react-dev-utils/node_modules/loader-utils": {
"version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz",
- "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==",
+ "license": "MIT",
"engines": {
"node": ">= 12.13.0"
}
},
"node_modules/@edx/frontend-build/node_modules/react-dev-utils/node_modules/schema-utils": {
"version": "2.7.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
- "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
@@ -3252,8 +3030,7 @@
},
"node_modules/@edx/frontend-build/node_modules/react-dev-utils/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -3266,16 +3043,14 @@
},
"node_modules/@edx/frontend-build/node_modules/react-dev-utils/node_modules/tapable": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@edx/frontend-build/node_modules/renderkid": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
- "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+ "license": "MIT",
"dependencies": {
"css-select": "^4.1.3",
"dom-converter": "^0.2.0",
@@ -3286,24 +3061,21 @@
},
"node_modules/@edx/frontend-build/node_modules/shell-quote": {
"version": "1.7.4",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz",
- "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@edx/frontend-build/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@edx/frontend-build/node_modules/stylehacks": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
- "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.4",
"postcss-selector-parser": "^6.0.4"
@@ -3317,8 +3089,7 @@
},
"node_modules/@edx/frontend-build/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -3328,16 +3099,14 @@
},
"node_modules/@edx/frontend-build/node_modules/tapable": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@edx/frontend-build/node_modules/terser": {
"version": "5.15.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
- "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
+ "license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -3353,13 +3122,11 @@
},
"node_modules/@edx/frontend-build/node_modules/terser/node_modules/commander": {
"version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ "license": "MIT"
},
"node_modules/@edx/frontend-build/node_modules/webpack-merge": {
"version": "5.8.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
- "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "license": "MIT",
"dependencies": {
"clone-deep": "^4.0.1",
"wildcard": "^2.0.0"
@@ -3369,11 +3136,10 @@
}
},
"node_modules/@edx/frontend-enterprise-catalog-search": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-catalog-search/-/frontend-enterprise-catalog-search-4.2.0.tgz",
- "integrity": "sha512-rg/kix5IcQZFNaV7v4qCyZQJYm7ilXyjBIbnszY9GaK75crAKcezoRYruS36TNeYLuUo2ri8NwrZ73OxFuInwg==",
+ "version": "4.5.0",
+ "license": "AGPL-3.0",
"dependencies": {
- "@edx/frontend-enterprise-utils": "^3.2.0",
+ "@edx/frontend-enterprise-utils": "^3.4.0",
"classnames": "2.2.5",
"lodash.debounce": "4.0.8",
"prop-types": "15.7.2"
@@ -3383,100 +3149,89 @@
"@edx/paragon": "^19.15.0 || ^20.0.0",
"@fortawesome/free-solid-svg-icons": "^5.8.1",
"@fortawesome/react-fontawesome": "^0.1.4",
- "react": "^16.12.0",
- "react-dom": "^16.12.0",
+ "react": "^16.12.0 || ^17.0.0",
+ "react-dom": "^16.12.0 || ^17.0.0",
"react-instantsearch-dom": "^6.8.3",
"react-router-dom": "^5.2.0"
}
},
"node_modules/@edx/frontend-enterprise-catalog-search/node_modules/classnames": {
"version": "2.2.5",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
- "integrity": "sha512-DTt3GhOUDKhh4ONwIJW4lmhyotQmV2LjNlGK/J2hkwUcqcbKkCLAdJPtxQnxnlc7SR3f1CEXCyMmc7WLUsWbNA=="
+ "license": "MIT"
},
"node_modules/@edx/frontend-enterprise-hotjar": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-hotjar/-/frontend-enterprise-hotjar-1.3.0.tgz",
- "integrity": "sha512-50xawVceXsj7Olr5u2UnmBOnyOLj8WHRdr2Y9/vR21U/Kd3Qa/bj8eSx8p4HqSlFbVMcKwfgszcBZx76nmaldw==",
+ "version": "1.4.0",
+ "license": "AGPL-3.0",
"peerDependencies": {
- "react": "^16.12.0",
- "react-dom": "^16.12.0",
+ "react": "^16.12.0 || ^17.0.0",
+ "react-dom": "^16.12.0 || ^17.0.0",
"react-router-dom": "^5.2.0"
}
},
"node_modules/@edx/frontend-enterprise-logistration": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-logistration/-/frontend-enterprise-logistration-3.2.0.tgz",
- "integrity": "sha512-iM7EceyAMGWnCQTVIzRhMxVHfSY/KdkiX7Whb1e7SbL/e7dB2MtP/MYiAfmk2qDGubzDcfdHVaxevQxdP6l5pg==",
+ "version": "3.4.0",
+ "license": "AGPL-3.0",
"dependencies": {
- "@edx/frontend-enterprise-utils": "^3.2.0",
+ "@edx/frontend-enterprise-utils": "^3.4.0",
"prop-types": "15.7.2"
},
"peerDependencies": {
"@edx/frontend-platform": "^4.0.1",
- "react": "^16.12.0",
- "react-dom": "^16.12.0",
+ "react": "^16.12.0 || ^17.0.0",
+ "react-dom": "^16.12.0 || ^17.0.0",
"react-router-dom": "^5.2.0"
}
},
"node_modules/@edx/frontend-enterprise-utils": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-3.2.0.tgz",
- "integrity": "sha512-3lXRu2QTiOaIF4wljNLjeoUSRFseDnMbw/+/RmK1dAplqvoi632fX9y0lMm0q8Nnr8FP9XyxxAoOYeDQwGIQ+w==",
+ "version": "3.4.0",
+ "license": "AGPL-3.0",
"dependencies": {
- "@testing-library/react": "11.2.6",
+ "@testing-library/react": "12.1.4",
"history": "4.10.1"
},
"peerDependencies": {
"@edx/frontend-platform": "^4.0.1",
- "react": "^16.12.0",
- "react-dom": "^16.12.0",
+ "react": "^16.12.0 || ^17.0.0",
+ "react-dom": "^16.12.0 || ^17.0.0",
"react-router-dom": "^5.2.0"
}
},
"node_modules/@edx/frontend-enterprise-utils/node_modules/@testing-library/dom": {
- "version": "7.31.2",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
- "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
+ "version": "8.20.1",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
- "@types/aria-query": "^4.2.0",
- "aria-query": "^4.2.2",
+ "@types/aria-query": "^5.0.1",
+ "aria-query": "5.1.3",
"chalk": "^4.1.0",
- "dom-accessibility-api": "^0.5.6",
- "lz-string": "^1.4.4",
- "pretty-format": "^26.6.2"
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "pretty-format": "^27.0.2"
},
"engines": {
- "node": ">=10"
+ "node": ">=12"
}
},
"node_modules/@edx/frontend-enterprise-utils/node_modules/@testing-library/react": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz",
- "integrity": "sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ==",
+ "version": "12.1.4",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^7.28.1"
+ "@testing-library/dom": "^8.0.0",
+ "@types/react-dom": "*"
},
"engines": {
- "node": ">=10"
+ "node": ">=12"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
},
- "node_modules/@edx/frontend-enterprise-utils/node_modules/@types/aria-query": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="
- },
"node_modules/@edx/frontend-enterprise-utils/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -3487,22 +3242,9 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/@edx/frontend-enterprise-utils/node_modules/aria-query": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
- "dependencies": {
- "@babel/runtime": "^7.10.2",
- "@babel/runtime-corejs3": "^7.10.2"
- },
- "engines": {
- "node": ">=6.0"
- }
- },
"node_modules/@edx/frontend-enterprise-utils/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -3516,8 +3258,7 @@
},
"node_modules/@edx/frontend-enterprise-utils/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -3527,21 +3268,44 @@
},
"node_modules/@edx/frontend-enterprise-utils/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@edx/frontend-enterprise-utils/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/pretty-format": {
+ "version": "27.5.1",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@edx/frontend-enterprise-utils/node_modules/react-is": {
+ "version": "17.0.2",
+ "license": "MIT"
+ },
"node_modules/@edx/frontend-enterprise-utils/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -3550,9 +3314,8 @@
}
},
"node_modules/@edx/frontend-platform": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.0.1.tgz",
- "integrity": "sha512-79uo/iQJ7P1tIY0MHTQ874W/NrNkcDe4BFC/0AvKF4DNrkqq9AkUFKqQ3hvd8E9C1EK189KlcAmo6FFQ67IFXg==",
+ "version": "4.4.0",
+ "license": "AGPL-3.0",
"dependencies": {
"@cospired/i18n-iso-languages": "2.2.0",
"@formatjs/intl-pluralrules": "4.3.3",
@@ -3575,13 +3338,14 @@
"universal-cookie": "4.0.4"
},
"bin": {
+ "intl-imports.js": "i18n/scripts/intl-imports.js",
"transifex-utils.js": "i18n/scripts/transifex-utils.js"
},
"peerDependencies": {
"@edx/paragon": ">= 10.0.0 < 21.0.0",
"prop-types": "^15.7.2",
- "react": "^16.9.0",
- "react-dom": "^16.9.0",
+ "react": "^16.9.0 || ^17.0.0",
+ "react-dom": "^16.9.0 || ^17.0.0",
"react-redux": "^7.1.1",
"react-router-dom": "^5.0.1",
"redux": "^4.0.4"
@@ -3589,16 +3353,21 @@
},
"node_modules/@edx/new-relic-source-map-webpack-plugin": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.0.2.tgz",
- "integrity": "sha512-jwu9WjRtEbv0rvPHGCnhbQbbv6+DTADShj43NQVsuAyD823znjutgoHnlc+9HIOiYiIxjz/wIMcGVwjrTnMceQ==",
+ "license": "AGPL-3.0",
"dependencies": {
"@newrelic/publish-sourcemap": "^5.0.1"
}
},
"node_modules/@edx/paragon": {
"version": "20.46.3",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.46.3.tgz",
- "integrity": "sha512-cHxoxoOREVFbBqW9IRAtlIAQo1lcF9JJXkLoEw1Vam6oetKSa5Mc0SL5kykbV+1iRPP7kS8A0Csf5nRr0oolLQ==",
+ "license": "Apache-2.0",
+ "workspaces": [
+ "example",
+ "component-generator",
+ "www",
+ "icons",
+ "dependent-usage-analyzer"
+ ],
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
@@ -3634,18 +3403,16 @@
},
"node_modules/@edx/paragon/node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.2.1",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz",
- "integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ==",
"hasInstallScript": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@edx/paragon/node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.2.1",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz",
- "integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==",
"hasInstallScript": true,
+ "license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.2.1"
},
@@ -3655,8 +3422,7 @@
},
"node_modules/@edx/paragon/node_modules/@fortawesome/react-fontawesome": {
"version": "0.1.19",
- "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz",
- "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==",
+ "license": "MIT",
"dependencies": {
"prop-types": "^15.8.1"
},
@@ -3667,21 +3433,18 @@
},
"node_modules/@edx/paragon/node_modules/brace-expansion": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@edx/paragon/node_modules/classnames": {
"version": "2.3.2",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
- "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ "license": "MIT"
},
"node_modules/@edx/paragon/node_modules/glob": {
"version": "8.0.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
- "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -3698,8 +3461,7 @@
},
"node_modules/@edx/paragon/node_modules/minimatch": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
- "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -3709,8 +3471,7 @@
},
"node_modules/@edx/paragon/node_modules/prop-types": {
"version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -3719,16 +3480,14 @@
},
"node_modules/@edx/typescript-config": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@edx/typescript-config/-/typescript-config-1.0.1.tgz",
- "integrity": "sha512-w0g3nIX9oEch8Rip8q8sb/nrurGEHA1BEjK/I1LAQwA44K4FPMWvyvabmZErrdTJ9sXcZL10aWD3bat1obV8Bg==",
+ "license": "MIT",
"peerDependencies": {
"typescript": "^4.9.4"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
- "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "license": "MIT",
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
@@ -3741,16 +3500,14 @@
},
"node_modules/@eslint-community/regexpp": {
"version": "4.5.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz",
- "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==",
+ "license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz",
- "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==",
+ "license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -3771,13 +3528,11 @@
},
"node_modules/@eslint/eslintrc/node_modules/argparse": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ "license": "Python-2.0"
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "license": "MIT",
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -3790,8 +3545,7 @@
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -3801,8 +3555,7 @@
},
"node_modules/@eslint/eslintrc/node_modules/type-fest": {
"version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
@@ -3812,17 +3565,15 @@
},
"node_modules/@eslint/js": {
"version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
- "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
+ "license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@faker-js/faker": {
"version": "7.6.0",
- "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz",
- "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
@@ -3830,8 +3581,7 @@
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.11.4",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
- "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
+ "license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "0.2.25",
"tslib": "^2.1.0"
@@ -3839,16 +3589,14 @@
},
"node_modules/@formatjs/fast-memoize": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
- "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz",
- "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/icu-skeleton-parser": "1.3.6",
@@ -3857,8 +3605,7 @@
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.3.6",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz",
- "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"tslib": "^2.1.0"
@@ -3866,8 +3613,7 @@
},
"node_modules/@formatjs/intl": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz",
- "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/fast-memoize": "1.2.1",
@@ -3888,8 +3634,7 @@
},
"node_modules/@formatjs/intl-displaynames": {
"version": "5.4.3",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz",
- "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/intl-localematcher": "0.2.25",
@@ -3898,8 +3643,7 @@
},
"node_modules/@formatjs/intl-listformat": {
"version": "6.5.3",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz",
- "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/intl-localematcher": "0.2.25",
@@ -3908,16 +3652,14 @@
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.2.25",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
- "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/intl-numberformat": {
"version": "5.7.6",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz",
- "integrity": "sha512-ZlZfYtvbVHYZY5OG3RXizoCwxKxEKOrzEe2YOw9wbzoxF3PmFn0SAgojCFGLyNXkkR6xVxlylhbuOPf1dkIVNg==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.4.0",
"tslib": "^2.0.1"
@@ -3925,16 +3667,14 @@
},
"node_modules/@formatjs/intl-numberformat/node_modules/@formatjs/ecma402-abstract": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz",
- "integrity": "sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.0.1"
}
},
"node_modules/@formatjs/intl-pluralrules": {
"version": "4.3.3",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz",
- "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/intl-localematcher": "0.2.25",
@@ -3943,8 +3683,7 @@
},
"node_modules/@formatjs/intl-relativetimeformat": {
"version": "10.0.1",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz",
- "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==",
+ "license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/intl-localematcher": "0.2.25",
@@ -3953,8 +3692,7 @@
},
"node_modules/@formatjs/ts-transformer": {
"version": "2.13.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz",
- "integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==",
+ "license": "MIT",
"dependencies": {
"intl-messageformat-parser": "6.1.2",
"tslib": "^2.0.1",
@@ -3971,17 +3709,14 @@
},
"node_modules/@formatjs/ts-transformer/node_modules/@formatjs/ecma402-abstract": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz",
- "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.0.1"
}
},
"node_modules/@formatjs/ts-transformer/node_modules/intl-messageformat-parser": {
"version": "6.1.2",
- "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz",
- "integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==",
- "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
+ "license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "1.5.0",
"tslib": "^2.0.1"
@@ -3989,9 +3724,8 @@
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "0.2.36",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
- "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
"hasInstallScript": true,
+ "license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
@@ -3999,9 +3733,8 @@
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "1.2.35",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz",
- "integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==",
"hasInstallScript": true,
+ "license": "MIT",
"peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.35"
@@ -4012,9 +3745,8 @@
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "5.15.3",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz",
- "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==",
"hasInstallScript": true,
+ "license": "(CC-BY-4.0 AND MIT)",
"peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.35"
@@ -4025,8 +3757,7 @@
},
"node_modules/@fortawesome/react-fontawesome": {
"version": "0.1.14",
- "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
- "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"prop-types": "^15.7.2"
@@ -4038,8 +3769,7 @@
},
"node_modules/@fullhuman/postcss-purgecss": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz",
- "integrity": "sha512-onDS/b/2pMRzqSoj4qOs2tYFmOpaspjTAgvACIHMPiicu1ptajiBruTrjBzTKdxWdX0ldaBb7wj8nEaTLyFkJw==",
+ "license": "MIT",
"dependencies": {
"purgecss": "^5.0.0"
},
@@ -4049,8 +3779,7 @@
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.8",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
- "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "license": "Apache-2.0",
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -4062,8 +3791,7 @@
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "license": "Apache-2.0",
"engines": {
"node": ">=12.22"
},
@@ -4074,13 +3802,11 @@
},
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
- "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+ "license": "BSD-3-Clause"
},
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "license": "ISC",
"dependencies": {
"camelcase": "^5.3.1",
"find-up": "^4.1.0",
@@ -4094,16 +3820,14 @@
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": {
"version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -4114,8 +3838,7 @@
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -4125,8 +3848,7 @@
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -4139,8 +3861,7 @@
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -4150,16 +3871,14 @@
},
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/console": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz",
- "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"@types/node": "*",
@@ -4174,8 +3893,7 @@
},
"node_modules/@jest/console/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -4188,8 +3906,7 @@
},
"node_modules/@jest/console/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -4203,8 +3920,7 @@
},
"node_modules/@jest/console/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -4214,21 +3930,18 @@
},
"node_modules/@jest/console/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/console/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/console/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -4246,16 +3959,14 @@
},
"node_modules/@jest/console/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/console/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -4265,8 +3976,7 @@
},
"node_modules/@jest/core": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz",
- "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==",
+ "license": "MIT",
"dependencies": {
"@jest/console": "^26.6.2",
"@jest/reporters": "^26.6.2",
@@ -4303,8 +4013,7 @@
},
"node_modules/@jest/core/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -4317,8 +4026,7 @@
},
"node_modules/@jest/core/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -4332,8 +4040,7 @@
},
"node_modules/@jest/core/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -4343,21 +4050,18 @@
},
"node_modules/@jest/core/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/core/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/core/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -4375,8 +4079,7 @@
},
"node_modules/@jest/core/node_modules/rimraf": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
@@ -4389,16 +4092,14 @@
},
"node_modules/@jest/core/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/core/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -4408,8 +4109,7 @@
},
"node_modules/@jest/environment": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz",
- "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==",
+ "license": "MIT",
"dependencies": {
"@jest/fake-timers": "^26.6.2",
"@jest/types": "^26.6.2",
@@ -4422,9 +4122,8 @@
},
"node_modules/@jest/expect-utils": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
- "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"jest-get-type": "^29.4.3"
},
@@ -4434,8 +4133,7 @@
},
"node_modules/@jest/fake-timers": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz",
- "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"@sinonjs/fake-timers": "^6.0.1",
@@ -4450,8 +4148,7 @@
},
"node_modules/@jest/fake-timers/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -4464,8 +4161,7 @@
},
"node_modules/@jest/fake-timers/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -4479,8 +4175,7 @@
},
"node_modules/@jest/fake-timers/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -4490,21 +4185,18 @@
},
"node_modules/@jest/fake-timers/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/fake-timers/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/fake-timers/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -4522,16 +4214,14 @@
},
"node_modules/@jest/fake-timers/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/fake-timers/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -4541,8 +4231,7 @@
},
"node_modules/@jest/globals": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz",
- "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==",
+ "license": "MIT",
"dependencies": {
"@jest/environment": "^26.6.2",
"@jest/types": "^26.6.2",
@@ -4554,8 +4243,7 @@
},
"node_modules/@jest/globals/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -4568,8 +4256,7 @@
},
"node_modules/@jest/globals/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -4583,8 +4270,7 @@
},
"node_modules/@jest/globals/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -4594,21 +4280,18 @@
},
"node_modules/@jest/globals/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/globals/node_modules/diff-sequences": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/@jest/globals/node_modules/expect": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"ansi-styles": "^4.0.0",
@@ -4623,16 +4306,14 @@
},
"node_modules/@jest/globals/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/globals/node_modules/jest-diff": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^26.6.2",
@@ -4645,16 +4326,14 @@
},
"node_modules/@jest/globals/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/@jest/globals/node_modules/jest-matcher-utils": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"jest-diff": "^26.6.2",
@@ -4667,8 +4346,7 @@
},
"node_modules/@jest/globals/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -4686,16 +4364,14 @@
},
"node_modules/@jest/globals/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/globals/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -4705,8 +4381,7 @@
},
"node_modules/@jest/reporters": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz",
- "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==",
+ "license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^0.2.3",
"@jest/console": "^26.6.2",
@@ -4742,8 +4417,7 @@
},
"node_modules/@jest/reporters/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -4756,8 +4430,7 @@
},
"node_modules/@jest/reporters/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -4771,8 +4444,7 @@
},
"node_modules/@jest/reporters/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -4782,21 +4454,18 @@
},
"node_modules/@jest/reporters/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/reporters/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
- "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@babel/core": "^7.7.5",
"@istanbuljs/schema": "^0.1.2",
@@ -4809,24 +4478,21 @@
},
"node_modules/@jest/reporters/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/reporters/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@jest/reporters/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -4836,9 +4502,8 @@
},
"node_modules/@jest/schemas": {
"version": "29.4.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
- "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@sinclair/typebox": "^0.25.16"
},
@@ -4848,8 +4513,7 @@
},
"node_modules/@jest/source-map": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz",
- "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==",
+ "license": "MIT",
"dependencies": {
"callsites": "^3.0.0",
"graceful-fs": "^4.2.4",
@@ -4861,16 +4525,14 @@
},
"node_modules/@jest/source-map/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@jest/test-result": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz",
- "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==",
+ "license": "MIT",
"dependencies": {
"@jest/console": "^26.6.2",
"@jest/types": "^26.6.2",
@@ -4883,8 +4545,7 @@
},
"node_modules/@jest/test-sequencer": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz",
- "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==",
+ "license": "MIT",
"dependencies": {
"@jest/test-result": "^26.6.2",
"graceful-fs": "^4.2.4",
@@ -4898,8 +4559,7 @@
},
"node_modules/@jest/transform": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz",
- "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==",
+ "license": "MIT",
"dependencies": {
"@babel/core": "^7.1.0",
"@jest/types": "^26.6.2",
@@ -4923,8 +4583,7 @@
},
"node_modules/@jest/transform/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -4937,8 +4596,7 @@
},
"node_modules/@jest/transform/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -4952,8 +4610,7 @@
},
"node_modules/@jest/transform/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -4963,37 +4620,32 @@
},
"node_modules/@jest/transform/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/transform/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/transform/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/transform/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@jest/transform/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5003,8 +4655,7 @@
},
"node_modules/@jest/types": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
- "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
+ "license": "MIT",
"dependencies": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
@@ -5018,8 +4669,7 @@
},
"node_modules/@jest/types/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5032,8 +4682,7 @@
},
"node_modules/@jest/types/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5047,8 +4696,7 @@
},
"node_modules/@jest/types/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5058,21 +4706,18 @@
},
"node_modules/@jest/types/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/@jest/types/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@jest/types/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5082,8 +4727,7 @@
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10"
@@ -5094,24 +4738,21 @@
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
- "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
- "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
- "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@@ -5119,8 +4760,7 @@
},
"node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -5132,13 +4772,11 @@
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
- "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ "license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.17",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
- "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14"
@@ -5146,13 +4784,11 @@
},
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
- "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
+ "license": "MIT"
},
"node_modules/@newrelic/publish-sourcemap": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@newrelic/publish-sourcemap/-/publish-sourcemap-5.1.0.tgz",
- "integrity": "sha512-pOpW0InKZp/DXUmD3h6vaCGdtMDY5LyzzKvq3S3MBwTKm5Qc5ka3yZC73sLAMOXnjKZmdyG3d8A5LC+LawOEpA==",
+ "license": "New Relic proprietary",
"dependencies": {
"superagent": "^3.4.1",
"yargs": "^16.0.3"
@@ -5165,14 +4801,12 @@
},
"node_modules/@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents.3",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
- "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
+ "license": "MIT",
"optional": true
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -5183,16 +4817,14 @@
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -5203,8 +4835,7 @@
},
"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
"version": "0.5.10",
- "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz",
- "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==",
+ "license": "MIT",
"dependencies": {
"ansi-html-community": "^0.0.8",
"common-path-prefix": "^3.0.0",
@@ -5252,21 +4883,18 @@
},
"node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": {
"version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.21",
- "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz",
- "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g=="
+ "license": "MIT"
},
"node_modules/@popperjs/core": {
"version": "2.11.6",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
- "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@@ -5274,16 +4902,14 @@
},
"node_modules/@restart/context": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
- "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==",
+ "license": "MIT",
"peerDependencies": {
"react": ">=16.3.2"
}
},
"node_modules/@restart/hooks": {
"version": "0.4.7",
- "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz",
- "integrity": "sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==",
+ "license": "MIT",
"dependencies": {
"dequal": "^2.0.2"
},
@@ -5293,30 +4919,26 @@
},
"node_modules/@sinclair/typebox": {
"version": "0.25.24",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
- "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@sinonjs/commons": {
"version": "1.8.5",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz",
- "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==",
+ "license": "BSD-3-Clause",
"dependencies": {
"type-detect": "4.0.8"
}
},
"node_modules/@sinonjs/fake-timers": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
- "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@sinonjs/commons": "^1.7.0"
}
},
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz",
- "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5330,8 +4952,7 @@
},
"node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
- "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
+ "license": "MIT",
"engines": {
"node": ">=14"
},
@@ -5345,8 +4966,7 @@
},
"node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
- "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+ "license": "MIT",
"engines": {
"node": ">=14"
},
@@ -5360,8 +4980,7 @@
},
"node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz",
- "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5375,8 +4994,7 @@
},
"node_modules/@svgr/babel-plugin-svg-dynamic-title": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz",
- "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5390,8 +5008,7 @@
},
"node_modules/@svgr/babel-plugin-svg-em-dimensions": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz",
- "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5405,8 +5022,7 @@
},
"node_modules/@svgr/babel-plugin-transform-react-native-svg": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz",
- "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5420,8 +5036,7 @@
},
"node_modules/@svgr/babel-plugin-transform-svg-component": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz",
- "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==",
+ "license": "MIT",
"engines": {
"node": ">=12"
},
@@ -5435,8 +5050,7 @@
},
"node_modules/@svgr/babel-preset": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz",
- "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==",
+ "license": "MIT",
"dependencies": {
"@svgr/babel-plugin-add-jsx-attribute": "^6.5.1",
"@svgr/babel-plugin-remove-jsx-attribute": "*",
@@ -5460,8 +5074,7 @@
},
"node_modules/@svgr/core": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz",
- "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==",
+ "license": "MIT",
"dependencies": {
"@babel/core": "^7.19.6",
"@svgr/babel-preset": "^6.5.1",
@@ -5479,8 +5092,7 @@
},
"node_modules/@svgr/hast-util-to-babel-ast": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz",
- "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.20.0",
"entities": "^4.4.0"
@@ -5495,8 +5107,7 @@
},
"node_modules/@svgr/plugin-jsx": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz",
- "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==",
+ "license": "MIT",
"dependencies": {
"@babel/core": "^7.19.6",
"@svgr/babel-preset": "^6.5.1",
@@ -5516,8 +5127,7 @@
},
"node_modules/@svgr/plugin-svgo": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz",
- "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==",
+ "license": "MIT",
"dependencies": {
"cosmiconfig": "^7.0.1",
"deepmerge": "^4.2.2",
@@ -5536,8 +5146,7 @@
},
"node_modules/@svgr/webpack": {
"version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz",
- "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==",
+ "license": "MIT",
"dependencies": {
"@babel/core": "^7.19.6",
"@babel/plugin-transform-react-constant-elements": "^7.18.12",
@@ -5558,8 +5167,7 @@
},
"node_modules/@tanstack/match-sorter-utils": {
"version": "8.8.4",
- "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz",
- "integrity": "sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==",
+ "license": "MIT",
"dependencies": {
"remove-accents": "0.4.2"
},
@@ -5573,8 +5181,7 @@
},
"node_modules/@tanstack/query-core": {
"version": "4.36.1",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
- "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
@@ -5582,8 +5189,7 @@
},
"node_modules/@tanstack/react-query": {
"version": "4.36.1",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz",
- "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==",
+ "license": "MIT",
"dependencies": {
"@tanstack/query-core": "4.36.1",
"use-sync-external-store": "^1.2.0"
@@ -5608,8 +5214,7 @@
},
"node_modules/@tanstack/react-query-devtools": {
"version": "4.36.1",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.36.1.tgz",
- "integrity": "sha512-WYku83CKP3OevnYSG8Y/QO9g0rT75v1om5IvcWUwiUZJ4LanYGLVCZ8TdFG5jfsq4Ej/lu2wwDAULEUnRIMBSw==",
+ "license": "MIT",
"dependencies": {
"@tanstack/match-sorter-utils": "^8.7.0",
"superjson": "^1.10.0",
@@ -5627,9 +5232,8 @@
},
"node_modules/@testing-library/dom": {
"version": "9.3.1",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz",
- "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -5646,9 +5250,8 @@
},
"node_modules/@testing-library/dom/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5661,9 +5264,8 @@
},
"node_modules/@testing-library/dom/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5677,9 +5279,8 @@
},
"node_modules/@testing-library/dom/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5689,24 +5290,21 @@
},
"node_modules/@testing-library/dom/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@testing-library/dom/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@testing-library/dom/node_modules/pretty-format": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
- "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -5718,9 +5316,8 @@
},
"node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -5730,15 +5327,13 @@
},
"node_modules/@testing-library/dom/node_modules/react-is": {
"version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@testing-library/dom/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5748,9 +5343,8 @@
},
"node_modules/@testing-library/jest-dom": {
"version": "5.16.5",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz",
- "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@adobe/css-tools": "^4.0.1",
"@babel/runtime": "^7.9.2",
@@ -5770,9 +5364,8 @@
},
"node_modules/@testing-library/jest-dom/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5785,9 +5378,8 @@
},
"node_modules/@testing-library/jest-dom/node_modules/chalk": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5798,9 +5390,8 @@
},
"node_modules/@testing-library/jest-dom/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5810,24 +5401,21 @@
},
"node_modules/@testing-library/jest-dom/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@testing-library/jest-dom/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@testing-library/jest-dom/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5837,9 +5425,8 @@
},
"node_modules/@testing-library/react": {
"version": "11.2.7",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.7.tgz",
- "integrity": "sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@testing-library/dom": "^7.28.1"
@@ -5854,9 +5441,8 @@
},
"node_modules/@testing-library/react-hooks": {
"version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-5.0.3.tgz",
- "integrity": "sha512-UrnnRc5II7LMH14xsYNm/WRch/67cBafmrSQcyFh0v+UUmSf1uzfB7zn5jQXSettGwOSxJwdQUN7PgkT0w22Lg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@types/react": ">=16.9.0",
@@ -5881,9 +5467,8 @@
},
"node_modules/@testing-library/react/node_modules/@testing-library/dom": {
"version": "7.31.2",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz",
- "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -5900,15 +5485,13 @@
},
"node_modules/@testing-library/react/node_modules/@types/aria-query": {
"version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@testing-library/react/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5921,9 +5504,8 @@
},
"node_modules/@testing-library/react/node_modules/aria-query": {
"version": "4.2.2",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.10.2",
"@babel/runtime-corejs3": "^7.10.2"
@@ -5934,9 +5516,8 @@
},
"node_modules/@testing-library/react/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5950,9 +5531,8 @@
},
"node_modules/@testing-library/react/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5962,24 +5542,21 @@
},
"node_modules/@testing-library/react/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@testing-library/react/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@testing-library/react/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5989,9 +5566,8 @@
},
"node_modules/@testing-library/user-event": {
"version": "12.8.3",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz",
- "integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
@@ -6005,30 +5581,25 @@
},
"node_modules/@tootallnate/once": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
- "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/@trysound/sax": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+ "license": "ISC",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/@types/aria-query": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz",
- "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==",
- "dev": true
+ "license": "MIT"
},
"node_modules/@types/babel__core": {
"version": "7.1.20",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz",
- "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==",
+ "license": "MIT",
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0",
@@ -6039,16 +5610,14 @@
},
"node_modules/@types/babel__generator": {
"version": "7.6.4",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
- "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__template": {
"version": "7.4.1",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
- "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+ "license": "MIT",
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
@@ -6056,16 +5625,14 @@
},
"node_modules/@types/babel__traverse": {
"version": "7.18.2",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz",
- "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.3.0"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.2",
- "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
- "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "license": "MIT",
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
@@ -6073,24 +5640,21 @@
},
"node_modules/@types/bonjour": {
"version": "3.5.10",
- "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz",
- "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.35",
- "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
- "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/connect-history-api-fallback": {
"version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz",
- "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==",
+ "license": "MIT",
"dependencies": {
"@types/express-serve-static-core": "*",
"@types/node": "*"
@@ -6098,13 +5662,11 @@
},
"node_modules/@types/cookie": {
"version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
- "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
+ "license": "MIT"
},
"node_modules/@types/eslint": {
"version": "8.4.10",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
- "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
+ "license": "MIT",
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -6112,8 +5674,7 @@
},
"node_modules/@types/eslint-scope": {
"version": "3.7.4",
- "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
- "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
+ "license": "MIT",
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
@@ -6121,13 +5682,11 @@
},
"node_modules/@types/estree": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
- "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
+ "license": "MIT"
},
"node_modules/@types/express": {
"version": "4.17.16",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.16.tgz",
- "integrity": "sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==",
+ "license": "MIT",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.31",
@@ -6137,8 +5696,7 @@
},
"node_modules/@types/express-serve-static-core": {
"version": "4.17.33",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
- "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
@@ -6147,16 +5705,14 @@
},
"node_modules/@types/fs-extra": {
"version": "9.0.13",
- "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
- "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/glob": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "license": "MIT",
"dependencies": {
"@types/minimatch": "*",
"@types/node": "*"
@@ -6164,24 +5720,21 @@
},
"node_modules/@types/graceful-fs": {
"version": "4.1.5",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
- "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/hast": {
"version": "2.3.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
- "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
- "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "license": "MIT",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
@@ -6189,49 +5742,42 @@
},
"node_modules/@types/html-minifier-terser": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz",
- "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==",
+ "license": "MIT",
"peer": true
},
"node_modules/@types/http-proxy": {
"version": "1.17.9",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
- "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/invariant": {
"version": "2.2.35",
- "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
- "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
+ "license": "MIT"
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g=="
+ "license": "MIT"
},
"node_modules/@types/istanbul-lib-report": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+ "license": "MIT",
"dependencies": {
"@types/istanbul-lib-coverage": "*"
}
},
"node_modules/@types/istanbul-reports": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+ "license": "MIT",
"dependencies": {
"@types/istanbul-lib-report": "*"
}
},
"node_modules/@types/jest": {
"version": "29.5.2",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.2.tgz",
- "integrity": "sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"expect": "^29.0.0",
"pretty-format": "^29.0.0"
@@ -6239,9 +5785,8 @@
},
"node_modules/@types/jest/node_modules/ansi-styles": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -6251,9 +5796,8 @@
},
"node_modules/@types/jest/node_modules/pretty-format": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
- "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
@@ -6265,77 +5809,63 @@
},
"node_modules/@types/jest/node_modules/react-is": {
"version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
- "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
+ "license": "MIT"
},
"node_modules/@types/json5": {
"version": "0.0.29",
- "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
- "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
+ "license": "MIT"
},
"node_modules/@types/mdast": {
"version": "3.0.10",
- "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
- "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/mime": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
- "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
+ "license": "MIT"
},
"node_modules/@types/minimatch": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
- "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA=="
+ "license": "MIT"
},
"node_modules/@types/node": {
"version": "18.11.9",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
- "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg=="
+ "license": "MIT"
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
- "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
+ "license": "MIT"
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
- "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
+ "license": "MIT"
},
"node_modules/@types/prettier": {
"version": "2.7.1",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow=="
+ "license": "MIT"
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
- "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
+ "license": "MIT"
},
"node_modules/@types/qs": {
"version": "6.9.7",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
- "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
+ "license": "MIT"
},
"node_modules/@types/range-parser": {
"version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
- "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
+ "license": "MIT"
},
"node_modules/@types/react": {
"version": "18.0.25",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz",
- "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==",
+ "license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -6344,17 +5874,14 @@
},
"node_modules/@types/react-dom": {
"version": "18.0.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz",
- "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==",
- "dev": true,
+ "license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-redux": {
"version": "7.1.24",
- "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
- "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==",
+ "license": "MIT",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
@@ -6364,57 +5891,48 @@
},
"node_modules/@types/react-test-renderer": {
"version": "18.0.0",
- "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz",
- "integrity": "sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.5",
- "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
+ "license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/retry": {
"version": "0.12.0",
- "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
- "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
+ "license": "MIT"
},
"node_modules/@types/scheduler": {
"version": "0.16.2",
- "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
- "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
+ "license": "MIT"
},
"node_modules/@types/schema-utils": {
"version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@types/schema-utils/-/schema-utils-2.4.0.tgz",
- "integrity": "sha512-454hrj5gz/FXcUE20ygfEiN4DxZ1sprUo0V1gqIqkNZ/CzoEzAZEll2uxMsuyz6BYjiQan4Aa65xbTemfzW9hQ==",
- "deprecated": "This is a stub types definition. schema-utils provides its own type definitions, so you do not need this installed.",
+ "license": "MIT",
"dependencies": {
"schema-utils": "*"
}
},
"node_modules/@types/semver": {
"version": "7.5.0",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
- "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw=="
+ "license": "MIT"
},
"node_modules/@types/serve-index": {
"version": "1.9.1",
- "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==",
+ "license": "MIT",
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/serve-static": {
"version": "1.15.0",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "license": "MIT",
"dependencies": {
"@types/mime": "*",
"@types/node": "*"
@@ -6422,66 +5940,56 @@
},
"node_modules/@types/sockjs": {
"version": "0.3.33",
- "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
- "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/source-list-map": {
"version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
- "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA=="
+ "license": "MIT"
},
"node_modules/@types/stack-utils": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
+ "license": "MIT"
},
"node_modules/@types/tapable": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
- "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ=="
+ "license": "MIT"
},
"node_modules/@types/testing-library__jest-dom": {
"version": "5.14.6",
- "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz",
- "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/jest": "*"
}
},
"node_modules/@types/uglify-js": {
"version": "3.17.1",
- "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz",
- "integrity": "sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==",
+ "license": "MIT",
"dependencies": {
"source-map": "^0.6.1"
}
},
"node_modules/@types/uglify-js/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@types/unist": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
- "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
+ "license": "MIT"
},
"node_modules/@types/warning": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
- "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA=="
+ "license": "MIT"
},
"node_modules/@types/webpack": {
"version": "4.41.33",
- "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz",
- "integrity": "sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/tapable": "^1",
@@ -6493,8 +6001,7 @@
},
"node_modules/@types/webpack-sources": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz",
- "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/source-list-map": "*",
@@ -6503,45 +6010,39 @@
},
"node_modules/@types/webpack-sources/node_modules/source-map": {
"version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/@types/webpack/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@types/ws": {
"version": "8.5.4",
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
- "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yargs": {
"version": "15.0.14",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz",
- "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==",
+ "license": "MIT",
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/@types/yargs-parser": {
"version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA=="
+ "license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz",
- "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==",
+ "license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.59.9",
@@ -6573,8 +6074,7 @@
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
"version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -6587,8 +6087,7 @@
},
"node_modules/@typescript-eslint/parser": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz",
- "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/scope-manager": "5.59.9",
"@typescript-eslint/types": "5.59.9",
@@ -6613,8 +6112,7 @@
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz",
- "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==",
+ "license": "MIT",
"dependencies": {
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/visitor-keys": "5.59.9"
@@ -6629,8 +6127,7 @@
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz",
- "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==",
+ "license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "5.59.9",
"@typescript-eslint/utils": "5.59.9",
@@ -6655,8 +6152,7 @@
},
"node_modules/@typescript-eslint/types": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz",
- "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==",
+ "license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -6667,8 +6163,7 @@
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz",
- "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==",
+ "license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/visitor-keys": "5.59.9",
@@ -6693,16 +6188,14 @@
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/array-union": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/globby": {
"version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "license": "MIT",
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
@@ -6720,8 +6213,7 @@
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -6734,16 +6226,14 @@
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz",
- "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==",
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9",
@@ -6767,8 +6257,7 @@
},
"node_modules/@typescript-eslint/utils/node_modules/semver": {
"version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -6781,8 +6270,7 @@
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.59.9",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz",
- "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==",
+ "license": "MIT",
"dependencies": {
"@typescript-eslint/types": "5.59.9",
"eslint-visitor-keys": "^3.3.0"
@@ -6797,8 +6285,7 @@
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
- "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
@@ -6806,23 +6293,19 @@
},
"node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
- "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
+ "license": "MIT"
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
- "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
+ "license": "MIT"
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
- "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
+ "license": "MIT"
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
- "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.1",
"@webassemblyjs/helper-api-error": "1.11.1",
@@ -6831,13 +6314,11 @@
},
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
- "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
+ "license": "MIT"
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
- "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -6847,29 +6328,25 @@
},
"node_modules/@webassemblyjs/ieee754": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
- "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+ "license": "MIT",
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
},
"node_modules/@webassemblyjs/leb128": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
- "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+ "license": "Apache-2.0",
"dependencies": {
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
- "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
+ "license": "MIT"
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
- "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -6883,8 +6360,7 @@
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
- "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
@@ -6895,8 +6371,7 @@
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
- "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -6906,8 +6381,7 @@
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
- "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-api-error": "1.11.1",
@@ -6919,8 +6393,7 @@
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
- "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+ "license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@xtuc/long": "4.2.2"
@@ -6928,8 +6401,7 @@
},
"node_modules/@webpack-cli/configtest": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
- "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
+ "license": "MIT",
"engines": {
"node": ">=14.15.0"
},
@@ -6940,8 +6412,7 @@
},
"node_modules/@webpack-cli/info": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
- "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
+ "license": "MIT",
"engines": {
"node": ">=14.15.0"
},
@@ -6952,8 +6423,7 @@
},
"node_modules/@webpack-cli/serve": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
- "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
+ "license": "MIT",
"engines": {
"node": ">=14.15.0"
},
@@ -6967,15 +6437,56 @@
}
}
},
+ "node_modules/@wojtekmaj/enzyme-adapter-react-17": {
+ "version": "0.8.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@wojtekmaj/enzyme-adapter-utils": "^0.2.0",
+ "enzyme-shallow-equal": "^1.0.0",
+ "has": "^1.0.0",
+ "prop-types": "^15.7.0",
+ "react-is": "^17.0.0",
+ "react-test-renderer": "^17.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/enzyme-adapter-react-17?sponsor=1"
+ },
+ "peerDependencies": {
+ "enzyme": "^3.0.0",
+ "react": "^17.0.0-0",
+ "react-dom": "^17.0.0-0"
+ }
+ },
+ "node_modules/@wojtekmaj/enzyme-adapter-react-17/node_modules/react-is": {
+ "version": "17.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@wojtekmaj/enzyme-adapter-utils": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function.prototype.name": "^1.1.0",
+ "has": "^1.0.0",
+ "object.fromentries": "^2.0.0",
+ "prop-types": "^15.7.0"
+ },
+ "funding": {
+ "url": "https://github.com/wojtekmaj/enzyme-adapter-utils?sponsor=1"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0-0"
+ }
+ },
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
- "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
+ "license": "BSD-3-Clause"
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
- "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
+ "license": "Apache-2.0"
},
"node_modules/@yarnpkg/lockfile": {
"version": "1.1.0",
@@ -6985,13 +6496,11 @@
},
"node_modules/abab": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
- "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
+ "license": "BSD-3-Clause"
},
"node_modules/accepts": {
"version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
@@ -7002,8 +6511,7 @@
},
"node_modules/acorn": {
"version": "8.8.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
- "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+ "license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@@ -7013,8 +6521,7 @@
},
"node_modules/acorn-globals": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
- "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
+ "license": "MIT",
"dependencies": {
"acorn": "^7.1.1",
"acorn-walk": "^7.1.1"
@@ -7022,8 +6529,7 @@
},
"node_modules/acorn-globals/node_modules/acorn": {
"version": "7.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
- "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@@ -7033,40 +6539,35 @@
},
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
- "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
- "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
+ "license": "MIT",
"peerDependencies": {
"acorn": "^8"
}
},
"node_modules/acorn-jsx": {
"version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
- "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+ "license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/address": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz",
- "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.12.0"
}
},
"node_modules/adjust-sourcemap-loader": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
- "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
+ "license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"regex-parser": "^2.2.11"
@@ -7077,8 +6578,7 @@
},
"node_modules/agent-base": {
"version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
"dependencies": {
"debug": "4"
},
@@ -7086,33 +6586,9 @@
"node": ">= 6.0.0"
}
},
- "node_modules/airbnb-prop-types": {
- "version": "2.16.0",
- "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz",
- "integrity": "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==",
- "dev": true,
- "dependencies": {
- "array.prototype.find": "^2.1.1",
- "function.prototype.name": "^1.1.2",
- "is-regex": "^1.1.0",
- "object-is": "^1.1.2",
- "object.assign": "^4.1.0",
- "object.entries": "^1.1.2",
- "prop-types": "^15.7.2",
- "prop-types-exact": "^1.2.0",
- "react-is": "^16.13.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- },
- "peerDependencies": {
- "react": "^0.14 || ^15.0.0 || ^16.0.0-alpha"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -7126,8 +6602,7 @@
},
"node_modules/ajv-errors": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
- "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "license": "MIT",
"peer": true,
"peerDependencies": {
"ajv": ">=5.0.0"
@@ -7135,8 +6610,7 @@
},
"node_modules/ajv-formats": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
- "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
@@ -7151,8 +6625,7 @@
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -7166,21 +6639,18 @@
},
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "license": "MIT"
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
- "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "license": "MIT",
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/algoliasearch": {
"version": "4.8.3",
- "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.8.3.tgz",
- "integrity": "sha512-pljX9jEE2TQ3i1JayhG8afNdE8UuJg3O9c7unW6QO67yRWCKr6b0t5aKC3hSVtjt7pA2TQXLKoAISb4SHx9ozQ==",
+ "license": "MIT",
"dependencies": {
"@algolia/cache-browser-local-storage": "4.8.3",
"@algolia/cache-common": "4.8.3",
@@ -7200,8 +6670,7 @@
},
"node_modules/algoliasearch-helper": {
"version": "3.11.1",
- "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.1.tgz",
- "integrity": "sha512-mvsPN3eK4E0bZG0/WlWJjeqe/bUD2KOEVOl0GyL/TGXn6wcpZU8NOuztGHCUKXkyg5gq6YzUakVTmnmSSO5Yiw==",
+ "license": "MIT",
"dependencies": {
"@algolia/events": "^4.0.1"
},
@@ -7211,8 +6680,7 @@
},
"node_modules/ansi-escapes": {
"version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "license": "MIT",
"dependencies": {
"type-fest": "^0.21.3"
},
@@ -7225,27 +6693,24 @@
},
"node_modules/ansi-html-community": {
"version": "0.0.8",
- "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
- "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
"engines": [
"node >= 0.8.0"
],
+ "license": "Apache-2.0",
"bin": {
"ansi-html": "bin/ansi-html"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^1.9.0"
},
@@ -7255,8 +6720,7 @@
},
"node_modules/anymatch": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -7267,16 +6731,14 @@
},
"node_modules/argparse": {
"version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "license": "MIT",
"dependencies": {
"sprintf-js": "~1.0.2"
}
},
"node_modules/aria-hidden": {
"version": "1.2.2",
- "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.2.tgz",
- "integrity": "sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
@@ -7295,40 +6757,35 @@
},
"node_modules/aria-query": {
"version": "5.1.3",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
- "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+ "license": "Apache-2.0",
"dependencies": {
"deep-equal": "^2.0.5"
}
},
"node_modules/arr-diff": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/arr-flatten": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/arr-union": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/array-buffer-byte-length": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
- "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"is-array-buffer": "^3.0.1"
@@ -7339,13 +6796,11 @@
},
"node_modules/array-flatten": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
- "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ=="
+ "license": "MIT"
},
"node_modules/array-includes": {
"version": "3.1.6",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
- "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -7362,8 +6817,7 @@
},
"node_modules/array-union": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
- "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "license": "MIT",
"dependencies": {
"array-uniq": "^1.0.1"
},
@@ -7373,25 +6827,22 @@
},
"node_modules/array-uniq": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
- "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/array-unique": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/array.prototype.filter": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.2.tgz",
- "integrity": "sha512-us+UrmGOilqttSOgoWZTpOvHu68vZT2YCjc/H4vhu56vzZpaDFBhB+Se2UwqWzMKbDv7Myq5M5pcZLAtUvTQdQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -7406,25 +6857,9 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/array.prototype.find": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.1.tgz",
- "integrity": "sha512-I2ri5Z9uMpMvnsNrHre9l3PaX+z9D0/z6F7Yt2u15q7wt0I62g5kX6xUKR1SJiefgG+u2/gJUmM8B47XRvQR6w==",
- "dev": true,
- "dependencies": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.4",
- "es-abstract": "^1.20.4",
- "es-shim-unscopables": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/array.prototype.flat": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
- "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -7440,8 +6875,7 @@
},
"node_modules/array.prototype.flatmap": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz",
- "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -7457,8 +6891,7 @@
},
"node_modules/array.prototype.reduce": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz",
- "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"call-bind": "^1.0.2",
@@ -7476,8 +6909,7 @@
},
"node_modules/array.prototype.tosorted": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz",
- "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -7488,39 +6920,33 @@
},
"node_modules/assert-ok": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-ok/-/assert-ok-1.0.0.tgz",
- "integrity": "sha512-lCvYmCpMl8c1tp9ynExhoDEk0gGW43SVVC3RE1VYrrVKhNMy8GHfdiwZdoIM6a605s56bUAbENQxtOC0uZp3wg=="
+ "license": "MIT"
},
"node_modules/assign-symbols": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ast-types-flow": {
"version": "0.0.7",
- "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
- "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
+ "license": "ISC"
},
"node_modules/asynckit": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ "license": "MIT"
},
"node_modules/at-least-node": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
- "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "license": "ISC",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/atob": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "license": "(MIT OR Apache-2.0)",
"bin": {
"atob": "bin/atob.js"
},
@@ -7530,16 +6956,13 @@
},
"node_modules/attr-accept": {
"version": "2.2.2",
- "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
- "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/autoprefixer": {
"version": "10.4.14",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
- "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
"funding": [
{
"type": "opencollective",
@@ -7550,6 +6973,7 @@
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
}
],
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.5",
"caniuse-lite": "^1.0.30001464",
@@ -7570,8 +6994,7 @@
},
"node_modules/available-typed-arrays": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
- "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -7581,16 +7004,14 @@
},
"node_modules/axe-core": {
"version": "4.7.2",
- "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz",
- "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==",
+ "license": "MPL-2.0",
"engines": {
"node": ">=4"
}
},
"node_modules/axios": {
"version": "0.27.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
- "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
@@ -7598,8 +7019,7 @@
},
"node_modules/axios-cache-interceptor": {
"version": "0.10.7",
- "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-0.10.7.tgz",
- "integrity": "sha512-UjpxChG5DpF6Kf1IPGMLOzRDNL8ZNS6TOn1jTaVvCE7cWFU904jJwi0T1s+IbijpnLEjK2iq5uLIuR8Sj+RsFQ==",
+ "license": "MIT",
"dependencies": {
"cache-parser": "^1.2.4",
"fast-defer": "^1.1.7",
@@ -7611,8 +7031,7 @@
},
"node_modules/axios-mock-adapter": {
"version": "1.19.0",
- "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz",
- "integrity": "sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"is-buffer": "^2.0.3"
@@ -7623,8 +7042,7 @@
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -7636,16 +7054,14 @@
},
"node_modules/axobject-query": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
- "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
+ "license": "Apache-2.0",
"dependencies": {
"deep-equal": "^2.0.5"
}
},
"node_modules/babel-jest": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
- "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==",
+ "license": "MIT",
"dependencies": {
"@jest/transform": "^26.6.2",
"@jest/types": "^26.6.2",
@@ -7665,8 +7081,7 @@
},
"node_modules/babel-jest/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -7679,8 +7094,7 @@
},
"node_modules/babel-jest/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -7694,8 +7108,7 @@
},
"node_modules/babel-jest/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -7705,29 +7118,25 @@
},
"node_modules/babel-jest/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/babel-jest/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/babel-jest/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/babel-jest/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -7737,8 +7146,7 @@
},
"node_modules/babel-loader": {
"version": "9.1.2",
- "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz",
- "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==",
+ "license": "MIT",
"dependencies": {
"find-cache-dir": "^3.3.2",
"schema-utils": "^4.0.0"
@@ -7753,8 +7161,7 @@
},
"node_modules/babel-loader/node_modules/ajv": {
"version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -7768,8 +7175,7 @@
},
"node_modules/babel-loader/node_modules/ajv-keywords": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
@@ -7779,13 +7185,11 @@
},
"node_modules/babel-loader/node_modules/json-schema-traverse": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "license": "MIT"
},
"node_modules/babel-loader/node_modules/schema-utils": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
- "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
@@ -7802,8 +7206,7 @@
},
"node_modules/babel-plugin-istanbul": {
"version": "6.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
- "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@istanbuljs/load-nyc-config": "^1.0.0",
@@ -7817,8 +7220,7 @@
},
"node_modules/babel-plugin-jest-hoist": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz",
- "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==",
+ "license": "MIT",
"dependencies": {
"@babel/template": "^7.3.3",
"@babel/types": "^7.3.3",
@@ -7831,8 +7233,7 @@
},
"node_modules/babel-plugin-polyfill-corejs2": {
"version": "0.3.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
- "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==",
+ "license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.17.7",
"@babel/helper-define-polyfill-provider": "^0.3.3",
@@ -7844,8 +7245,7 @@
},
"node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz",
- "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.3.3",
"core-js-compat": "^3.25.1"
@@ -7856,8 +7256,7 @@
},
"node_modules/babel-plugin-polyfill-regenerator": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz",
- "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.3.3"
},
@@ -7867,9 +7266,7 @@
},
"node_modules/babel-plugin-react-intl": {
"version": "7.9.4",
- "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz",
- "integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==",
- "deprecated": "this package has been renamed to babel-plugin-formatjs",
+ "license": "BSD-3-Clause",
"dependencies": {
"@babel/core": "^7.9.0",
"@babel/helper-plugin-utils": "^7.8.3",
@@ -7885,8 +7282,7 @@
},
"node_modules/babel-plugin-react-intl/node_modules/schema-utils": {
"version": "2.7.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
- "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
@@ -7902,8 +7298,7 @@
},
"node_modules/babel-plugin-transform-imports": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-imports/-/babel-plugin-transform-imports-2.0.0.tgz",
- "integrity": "sha512-65ewumYJ85QiXdcB/jmiU0y0jg6eL6CdnDqQAqQ8JMOKh1E52VPG3NJzbVKWcgovUR5GBH8IWpCXQ7I8Q3wjgw==",
+ "license": "ISC",
"dependencies": {
"@babel/types": "^7.4",
"is-valid-path": "^0.1.1"
@@ -7911,8 +7306,7 @@
},
"node_modules/babel-polyfill": {
"version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
- "integrity": "sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==",
+ "license": "MIT",
"dependencies": {
"babel-runtime": "^6.26.0",
"core-js": "^2.5.0",
@@ -7921,20 +7315,16 @@
},
"node_modules/babel-polyfill/node_modules/core-js": {
"version": "2.6.12",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
- "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
- "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
- "hasInstallScript": true
+ "hasInstallScript": true,
+ "license": "MIT"
},
"node_modules/babel-polyfill/node_modules/regenerator-runtime": {
"version": "0.10.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
- "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w=="
+ "license": "MIT"
},
"node_modules/babel-preset-current-node-syntax": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
- "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+ "license": "MIT",
"dependencies": {
"@babel/plugin-syntax-async-generators": "^7.8.4",
"@babel/plugin-syntax-bigint": "^7.8.3",
@@ -7955,8 +7345,7 @@
},
"node_modules/babel-preset-jest": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz",
- "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==",
+ "license": "MIT",
"dependencies": {
"babel-plugin-jest-hoist": "^26.6.2",
"babel-preset-current-node-syntax": "^1.0.0"
@@ -7970,8 +7359,7 @@
},
"node_modules/babel-runtime": {
"version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
- "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+ "license": "MIT",
"dependencies": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
@@ -7979,20 +7367,16 @@
},
"node_modules/babel-runtime/node_modules/core-js": {
"version": "2.6.12",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
- "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
- "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
- "hasInstallScript": true
+ "hasInstallScript": true,
+ "license": "MIT"
},
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
"version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
- "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+ "license": "MIT"
},
"node_modules/bail": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
- "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -8000,13 +7384,11 @@
},
"node_modules/balanced-match": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ "license": "MIT"
},
"node_modules/base": {
"version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "license": "MIT",
"dependencies": {
"cache-base": "^1.0.1",
"class-utils": "^0.3.5",
@@ -8022,8 +7404,7 @@
},
"node_modules/base/node_modules/define-property": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^1.0.0"
},
@@ -8033,16 +7414,13 @@
},
"node_modules/base/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
@@ -8056,104 +7434,30 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "MIT"
},
"node_modules/batch": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
- "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw=="
+ "license": "MIT"
},
"node_modules/big.js": {
"version": "5.2.2",
- "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
- "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "license": "MIT",
"engines": {
"node": "*"
}
},
- "node_modules/bin-check/node_modules/cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
- "extraneous": true,
- "dependencies": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "node_modules/bin-check/node_modules/execa": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
- "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==",
- "extraneous": true,
- "dependencies": {
- "cross-spawn": "^5.0.1",
- "get-stream": "^3.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/bin-check/node_modules/get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==",
- "extraneous": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/bin-check/node_modules/shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
- "extraneous": true,
- "dependencies": {
- "shebang-regex": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/bin-check/node_modules/shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
- "extraneous": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/bin-check/node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "extraneous": true,
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
"node_modules/binary-extensions": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/bl": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
@@ -8162,8 +7466,7 @@
},
"node_modules/bl/node_modules/readable-stream": {
"version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -8175,8 +7478,7 @@
},
"node_modules/body-parser": {
"version": "1.20.1",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
- "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
@@ -8198,29 +7500,25 @@
},
"node_modules/body-parser/node_modules/bytes": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/bonjour-service": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz",
- "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==",
+ "license": "MIT",
"dependencies": {
"array-flatten": "^2.1.2",
"dns-equal": "^1.0.0",
@@ -8230,13 +7528,10 @@
},
"node_modules/boolbase": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ "license": "ISC"
},
"node_modules/bootstrap": {
"version": "4.6.2",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz",
- "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==",
"funding": [
{
"type": "github",
@@ -8247,6 +7542,7 @@
"url": "https://opencollective.com/bootstrap"
}
],
+ "license": "MIT",
"peerDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.16.1"
@@ -8254,8 +7550,7 @@
},
"node_modules/brace-expansion": {
"version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -8263,8 +7558,7 @@
},
"node_modules/braces": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "license": "MIT",
"dependencies": {
"fill-range": "^7.0.1"
},
@@ -8274,13 +7568,10 @@
},
"node_modules/browser-process-hrtime": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
- "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
+ "license": "BSD-2-Clause"
},
"node_modules/browserslist": {
"version": "4.21.7",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz",
- "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==",
"funding": [
{
"type": "opencollective",
@@ -8295,6 +7586,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001489",
"electron-to-chromium": "^1.4.411",
@@ -8310,8 +7602,7 @@
},
"node_modules/bs-logger": {
"version": "0.2.6",
- "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
- "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+ "license": "MIT",
"dependencies": {
"fast-json-stable-stringify": "2.x"
},
@@ -8321,16 +7612,13 @@
},
"node_modules/bser": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "license": "Apache-2.0",
"dependencies": {
"node-int64": "^0.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
@@ -8345,6 +7633,7 @@
"url": "https://feross.org/support"
}
],
+ "license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
@@ -8352,21 +7641,18 @@
},
"node_modules/buffer-from": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ "license": "MIT"
},
"node_modules/bytes": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/cache-base": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "license": "MIT",
"dependencies": {
"collection-visit": "^1.0.0",
"component-emitter": "^1.2.1",
@@ -8384,16 +7670,14 @@
},
"node_modules/cache-base/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/cache-parser": {
"version": "1.2.4",
- "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.4.tgz",
- "integrity": "sha512-O0KwuHuJnbHUrghHi2kGp0SxnWSIBXTYt7M8WVhW0kbPRUNUKoE/Of6e1rRD6AAxmfxFunKnt90yEK09D+sc5g=="
+ "license": "MIT"
},
"node_modules/call-bind": {
"version": "1.0.5",
@@ -8410,16 +7694,14 @@
},
"node_modules/callsites": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/camel-case": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
- "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "license": "MIT",
"dependencies": {
"pascal-case": "^3.1.2",
"tslib": "^2.0.3"
@@ -8427,8 +7709,7 @@
},
"node_modules/camelcase": {
"version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -8438,8 +7719,7 @@
},
"node_modules/caniuse-api": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
- "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.0.0",
"caniuse-lite": "^1.0.0",
@@ -8448,9 +7728,7 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001566",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
- "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
+ "version": "1.0.30001495",
"funding": [
{
"type": "opencollective",
@@ -8464,12 +7742,12 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
- ]
+ ],
+ "license": "CC-BY-4.0"
},
"node_modules/capture-exit": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
- "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==",
+ "license": "ISC",
"dependencies": {
"rsvp": "^4.8.4"
},
@@ -8479,16 +7757,14 @@
},
"node_modules/cast-array": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cast-array/-/cast-array-1.0.1.tgz",
- "integrity": "sha512-EiqtV+M9L42wd0IRgYjgVGDq7vdNBUUrdecd03QReJp8pIr59o2A1b0XfP+aCUlzLKx2E7zVetaogeJCtiHa+w==",
+ "license": "MIT",
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/chalk": {
"version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -8500,16 +7776,14 @@
},
"node_modules/char-regex": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/character-entities": {
"version": "1.2.4",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
- "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -8517,8 +7791,7 @@
},
"node_modules/character-entities-legacy": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
- "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -8526,8 +7799,7 @@
},
"node_modules/character-reference-invalid": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
- "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -8535,9 +7807,8 @@
},
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
- "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
- "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"cheerio-select": "^2.1.0",
"dom-serializer": "^2.0.0",
@@ -8556,9 +7827,8 @@
},
"node_modules/cheerio-select": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
- "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-select": "^5.1.0",
@@ -8573,14 +7843,13 @@
},
"node_modules/chokidar": {
"version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
+ "license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -8599,32 +7868,27 @@
},
"node_modules/chownr": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+ "license": "ISC"
},
"node_modules/chrome-trace-event": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
- "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "license": "MIT",
"engines": {
"node": ">=6.0"
}
},
"node_modules/ci-info": {
"version": "1.6.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
- "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/cjs-module-lexer": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz",
- "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw=="
+ "license": "MIT"
},
"node_modules/class-utils": {
"version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "license": "MIT",
"dependencies": {
"arr-union": "^3.1.0",
"define-property": "^0.2.5",
@@ -8637,8 +7901,7 @@
},
"node_modules/class-utils/node_modules/define-property": {
"version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^0.1.0"
},
@@ -8648,8 +7911,7 @@
},
"node_modules/class-utils/node_modules/is-accessor-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -8659,8 +7921,7 @@
},
"node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -8670,13 +7931,11 @@
},
"node_modules/class-utils/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/class-utils/node_modules/is-data-descriptor": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -8686,8 +7945,7 @@
},
"node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -8697,8 +7955,7 @@
},
"node_modules/class-utils/node_modules/is-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "license": "MIT",
"dependencies": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
@@ -8710,29 +7967,25 @@
},
"node_modules/class-utils/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/class-utils/node_modules/kind-of": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/classnames": {
"version": "2.2.6",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
- "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+ "license": "MIT"
},
"node_modules/clean-css": {
"version": "4.2.4",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
- "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"source-map": "~0.6.0"
@@ -8743,8 +7996,7 @@
},
"node_modules/clean-css/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
@@ -8752,8 +8004,7 @@
},
"node_modules/clean-webpack-plugin": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz",
- "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==",
+ "license": "MIT",
"dependencies": {
"@types/webpack": "^4.4.31",
"del": "^4.1.1"
@@ -8767,8 +8018,7 @@
},
"node_modules/cliui": {
"version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -8777,8 +8027,7 @@
},
"node_modules/clone-deep": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
- "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "license": "MIT",
"dependencies": {
"is-plain-object": "^2.0.4",
"kind-of": "^6.0.2",
@@ -8790,8 +8039,7 @@
},
"node_modules/co": {
"version": "4.6.0",
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "license": "MIT",
"engines": {
"iojs": ">= 1.0.0",
"node": ">= 0.12.0"
@@ -8799,13 +8047,11 @@
},
"node_modules/collect-v8-coverage": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
- "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg=="
+ "license": "MIT"
},
"node_modules/collection-visit": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+ "license": "MIT",
"dependencies": {
"map-visit": "^1.0.0",
"object-visit": "^1.0.0"
@@ -8816,8 +8062,7 @@
},
"node_modules/color": {
"version": "3.1.3",
- "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
- "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^1.9.1",
"color-string": "^1.5.4"
@@ -8825,26 +8070,22 @@
},
"node_modules/color-contrast-checker": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/color-contrast-checker/-/color-contrast-checker-2.1.0.tgz",
- "integrity": "sha512-6Y0aIEej3pwZTVlicIqVzhO6T4izDWouaIXnYoDdTuFFAMQ9nnN0dgHNP9J94jRnH6asjPq1/wzUKxwoNbWtRQ=="
+ "license": "Apache-2.0"
},
"node_modules/color-convert": {
"version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "license": "MIT",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ "license": "MIT"
},
"node_modules/color-string": {
"version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
@@ -8852,13 +8093,11 @@
},
"node_modules/colord": {
"version": "2.9.3",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
- "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
+ "license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
@@ -8868,8 +8107,7 @@
},
"node_modules/comma-separated-tokens": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
- "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -8877,31 +8115,26 @@
},
"node_modules/commander": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/common-path-prefix": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
- "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="
+ "license": "ISC"
},
"node_modules/commondir": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
- "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
+ "license": "MIT"
},
"node_modules/component-emitter": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
- "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
+ "license": "MIT"
},
"node_modules/compressible": {
"version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "license": "MIT",
"dependencies": {
"mime-db": ">= 1.43.0 < 2"
},
@@ -8911,8 +8144,7 @@
},
"node_modules/compression": {
"version": "1.7.4",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
- "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "license": "MIT",
"dependencies": {
"accepts": "~1.3.5",
"bytes": "3.0.0",
@@ -8928,44 +8160,37 @@
},
"node_modules/compression/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/compression/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/compression/node_modules/safe-buffer": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ "license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+ "license": "MIT"
},
"node_modules/confusing-browser-globals": {
"version": "1.0.11",
- "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
- "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA=="
+ "license": "MIT"
},
"node_modules/connect-history-api-fallback": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
- "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+ "license": "MIT",
"engines": {
"node": ">=0.8"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
@@ -8975,39 +8200,33 @@
},
"node_modules/content-type": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/convert-source-map": {
"version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ "license": "MIT"
},
"node_modules/cookie": {
"version": "0.4.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
- "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+ "license": "MIT"
},
"node_modules/cookiejar": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
- "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="
+ "license": "MIT"
},
"node_modules/copy-anything": {
"version": "3.0.5",
- "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
- "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+ "license": "MIT",
"dependencies": {
"is-what": "^4.1.8"
},
@@ -9020,18 +8239,15 @@
},
"node_modules/copy-descriptor": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/core-js": {
"version": "3.7.0",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz",
- "integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA==",
- "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"hasInstallScript": true,
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
@@ -9039,8 +8255,7 @@
},
"node_modules/core-js-compat": {
"version": "3.30.2",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz",
- "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==",
+ "license": "MIT",
"dependencies": {
"browserslist": "^4.21.5"
},
@@ -9051,9 +8266,8 @@
},
"node_modules/core-js-pure": {
"version": "3.31.0",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.31.0.tgz",
- "integrity": "sha512-/AnE9Y4OsJZicCzIe97JP5XoPKQJfTuEG43aEVLFJGOJpyqELod+pE6LEl63DfG1Mp8wX97LDaDpy1GmLEUxlg==",
"hasInstallScript": true,
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
@@ -9061,13 +8275,11 @@
},
"node_modules/core-util-is": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ "license": "MIT"
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
- "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -9081,8 +8293,7 @@
},
"node_modules/cross-spawn": {
"version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -9094,8 +8305,7 @@
},
"node_modules/css-declaration-sorter": {
"version": "6.4.0",
- "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
- "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==",
+ "license": "ISC",
"engines": {
"node": "^10 || ^12 || >=14"
},
@@ -9105,9 +8315,8 @@
},
"node_modules/css-loader": {
"version": "5.2.6",
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.6.tgz",
- "integrity": "sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"icss-utils": "^5.1.0",
"loader-utils": "^2.0.0",
@@ -9133,9 +8342,8 @@
},
"node_modules/css-loader/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -9148,14 +8356,12 @@
},
"node_modules/css-mediaquery": {
"version": "0.1.2",
- "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz",
- "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q=="
+ "license": "BSD"
},
"node_modules/css-select": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
- "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
@@ -9169,8 +8375,7 @@
},
"node_modules/css-tree": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
- "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+ "license": "MIT",
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
@@ -9181,16 +8386,14 @@
},
"node_modules/css-tree/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/css-what": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
},
@@ -9200,14 +8403,12 @@
},
"node_modules/css.escape": {
"version": "1.5.1",
- "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
- "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/cssesc": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
},
@@ -9217,14 +8418,12 @@
},
"node_modules/cssfontparser": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz",
- "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/csso": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
- "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+ "license": "MIT",
"dependencies": {
"css-tree": "^1.1.2"
},
@@ -9234,13 +8433,11 @@
},
"node_modules/cssom": {
"version": "0.4.4",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
- "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw=="
+ "license": "MIT"
},
"node_modules/cssstyle": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
- "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "license": "MIT",
"dependencies": {
"cssom": "~0.3.6"
},
@@ -9250,18 +8447,15 @@
},
"node_modules/cssstyle/node_modules/cssom": {
"version": "0.3.8",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
- "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
+ "license": "MIT"
},
"node_modules/csstype": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
- "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
+ "license": "MIT"
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
- "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
+ "license": "BSD-2-Clause"
},
"node_modules/dash-embedded-component": {
"version": "2.0.2",
@@ -9289,61 +8483,28 @@
},
"node_modules/dash-embedded-component/node_modules/check-prop-types": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/check-prop-types/-/check-prop-types-1.1.2.tgz",
- "integrity": "sha512-hGDrZ1yhRgKuP1yzZ5sUX/PPmlKBLOF1GyF0Z008Sienko3BFZmlCXnmq+npRTIL/WlFCUzThyd+F5PQnnT1ug==",
+ "license": "MIT",
"peerDependencies": {
"prop-types": "<=15.6.0"
}
},
- "node_modules/dash-embedded-component/node_modules/react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
- },
- "node_modules/dash-embedded-component/node_modules/react-redux": {
- "version": "7.2.9",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
- "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
- "dependencies": {
- "@babel/runtime": "^7.15.4",
- "@types/react-redux": "^7.1.20",
- "hoist-non-react-statics": "^3.3.2",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.7.2",
- "react-is": "^17.0.2"
- },
- "peerDependencies": {
- "react": "^16.8.3 || ^17 || ^18"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- },
- "react-native": {
- "optional": true
- }
- }
- },
"node_modules/dash-embedded-component/node_modules/redux": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
- "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/dash-embedded-component/node_modules/uuid": {
"version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/data-urls": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
- "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
+ "license": "MIT",
"dependencies": {
"abab": "^2.0.3",
"whatwg-mimetype": "^2.3.0",
@@ -9355,13 +8516,11 @@
},
"node_modules/dayjs": {
"version": "1.11.9",
- "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
- "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
+ "license": "MIT"
},
"node_modules/debug": {
"version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "license": "MIT",
"dependencies": {
"ms": "2.1.2"
},
@@ -9376,29 +8535,25 @@
},
"node_modules/decamelize": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/decimal.js": {
"version": "10.4.2",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz",
- "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA=="
+ "license": "MIT"
},
"node_modules/decode-uri-component": {
"version": "0.2.2",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
- "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
@@ -9409,36 +8564,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/decompress/node_modules/make-dir": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
- "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
- "extraneous": true,
- "dependencies": {
- "pify": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/decompress/node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "extraneous": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/deep-diff": {
"version": "0.3.8",
- "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
- "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug=="
+ "license": "MIT"
},
"node_modules/deep-equal": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz",
- "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==",
+ "license": "MIT",
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
"call-bind": "^1.0.2",
@@ -9465,34 +8597,29 @@
},
"node_modules/deep-equal/node_modules/isarray": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ "license": "MIT"
},
"node_modules/deep-extend": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+ "license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/default-gateway": {
"version": "6.0.3",
- "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
- "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
+ "license": "BSD-2-Clause",
"dependencies": {
"execa": "^5.0.0"
},
@@ -9502,8 +8629,7 @@
},
"node_modules/default-gateway/node_modules/execa": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "license": "MIT",
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.0",
@@ -9524,8 +8650,7 @@
},
"node_modules/default-gateway/node_modules/get-stream": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -9535,8 +8660,7 @@
},
"node_modules/default-gateway/node_modules/is-stream": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "license": "MIT",
"engines": {
"node": ">=8"
},
@@ -9546,8 +8670,7 @@
},
"node_modules/default-gateway/node_modules/npm-run-path": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "license": "MIT",
"dependencies": {
"path-key": "^3.0.0"
},
@@ -9570,16 +8693,14 @@
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
- "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/define-properties": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
- "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
+ "license": "MIT",
"dependencies": {
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
@@ -9593,8 +8714,7 @@
},
"node_modules/define-property": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^1.0.2",
"isobject": "^3.0.1"
@@ -9605,16 +8725,14 @@
},
"node_modules/define-property/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/del": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
- "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+ "license": "MIT",
"dependencies": {
"@types/glob": "^7.1.1",
"globby": "^6.1.0",
@@ -9630,40 +8748,35 @@
},
"node_modules/delayed-stream": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/dependency-graph": {
"version": "0.9.0",
- "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.9.0.tgz",
- "integrity": "sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/dequal": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/destroy": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
@@ -9671,34 +8784,29 @@
},
"node_modules/detect-libc": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
- "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+ "license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/detect-newline": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
- "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/detect-node": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
- "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
+ "license": "MIT"
},
"node_modules/detect-node-es": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
- "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
+ "license": "MIT"
},
"node_modules/detect-port-alt": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz",
- "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==",
+ "license": "MIT",
"dependencies": {
"address": "^1.0.1",
"debug": "^2.6.0"
@@ -9713,35 +8821,30 @@
},
"node_modules/detect-port-alt/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/detect-port-alt/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/diacritics": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
- "integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA=="
+ "license": "MIT"
},
"node_modules/diff-sequences": {
"version": "29.4.3",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
- "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "license": "MIT",
"dependencies": {
"path-type": "^4.0.0"
},
@@ -9751,19 +8854,16 @@
},
"node_modules/discontinuous-range": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
- "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/dns-equal": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
- "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg=="
+ "license": "MIT"
},
"node_modules/dns-packet": {
"version": "5.6.0",
- "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz",
- "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==",
+ "license": "MIT",
"dependencies": {
"@leichtgewicht/ip-codec": "^2.0.1"
},
@@ -9773,8 +8873,7 @@
},
"node_modules/doctrine": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "license": "Apache-2.0",
"dependencies": {
"esutils": "^2.0.2"
},
@@ -9784,21 +8883,18 @@
},
"node_modules/dom-accessibility-api": {
"version": "0.5.14",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz",
- "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg=="
+ "license": "MIT"
},
"node_modules/dom-converter": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
- "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "license": "MIT",
"dependencies": {
"utila": "~0.4"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
- "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
- "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
@@ -9806,8 +8902,7 @@
},
"node_modules/dom-serializer": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
@@ -9819,19 +8914,17 @@
},
"node_modules/domelementtype": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
- ]
+ ],
+ "license": "BSD-2-Clause"
},
"node_modules/domexception": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
- "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
+ "license": "MIT",
"dependencies": {
"webidl-conversions": "^5.0.0"
},
@@ -9841,16 +8934,14 @@
},
"node_modules/domexception/node_modules/webidl-conversions": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
- "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=8"
}
},
"node_modules/domhandler": {
"version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.3.0"
},
@@ -9863,8 +8954,7 @@
},
"node_modules/domutils": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
- "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+ "license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
@@ -9876,8 +8966,7 @@
},
"node_modules/dot-case": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
- "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "license": "MIT",
"dependencies": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
@@ -9885,24 +8974,21 @@
},
"node_modules/dotenv": {
"version": "8.2.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
- "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=8"
}
},
"node_modules/dotenv-defaults": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
- "integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
+ "license": "MIT",
"dependencies": {
"dotenv": "^8.2.0"
}
},
"node_modules/dotenv-webpack": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-3.0.0.tgz",
- "integrity": "sha512-HsBTgVbh9E71IdC6QcQTnAuVt11niA5QMKblcBqhVBxP975XPGMqxf21pqgVBPyRKn2GqPh5kSHMgjxeR/HEAA==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"dotenv-defaults": "^2.0.1"
@@ -9913,39 +8999,32 @@
},
"node_modules/duplexer": {
"version": "0.1.2",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
- "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
+ "license": "MIT"
},
"node_modules/ee-first": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ "license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.4.421",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.421.tgz",
- "integrity": "sha512-wZOyn3s/aQOtLGAwXMZfteQPN68kgls2wDAnYOA8kCjBvKVrW5RwmWVspxJYTqrcN7Y263XJVsC66VCIGzDO3g=="
+ "license": "ISC"
},
"node_modules/email-prop-type": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/email-prop-type/-/email-prop-type-3.0.1.tgz",
- "integrity": "sha512-tONZGMEOOkadp5OBftuVXU8DsceWmINxYK+pqPFB4LT5ODjrPX/esel3WGqbV7d6in5/MnZE4n4QcqOr4gh7dg==",
+ "license": "MIT",
"dependencies": {
"email-validator": "^2.0.4"
}
},
"node_modules/email-validator": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz",
- "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==",
"engines": {
"node": ">4.0"
}
},
"node_modules/emittery": {
"version": "0.7.2",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz",
- "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -9955,37 +9034,32 @@
},
"node_modules/emoji-regex": {
"version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+ "license": "MIT"
},
"node_modules/emojis-list": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
- "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/encodeurl": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.14.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz",
- "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==",
+ "license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -9996,16 +9070,14 @@
},
"node_modules/enhanced-resolve/node_modules/tapable": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/entities": {
"version": "4.4.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
- "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
@@ -10015,8 +9087,7 @@
},
"node_modules/envinfo": {
"version": "7.8.1",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
- "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+ "license": "MIT",
"bin": {
"envinfo": "dist/cli.js"
},
@@ -10026,9 +9097,8 @@
},
"node_modules/enzyme": {
"version": "3.11.0",
- "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz",
- "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"array.prototype.flat": "^1.2.3",
"cheerio": "^1.0.0-rc.3",
@@ -10057,86 +9127,10 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/enzyme-adapter-react-16": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz",
- "integrity": "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==",
- "dev": true,
- "dependencies": {
- "enzyme-adapter-utils": "^1.14.0",
- "enzyme-shallow-equal": "^1.0.4",
- "has": "^1.0.3",
- "object.assign": "^4.1.2",
- "object.values": "^1.1.2",
- "prop-types": "^15.7.2",
- "react-is": "^16.13.1",
- "react-test-renderer": "^16.0.0-0",
- "semver": "^5.7.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- },
- "peerDependencies": {
- "enzyme": "^3.0.0",
- "react": "^16.0.0-0",
- "react-dom": "^16.0.0-0"
- }
- },
- "node_modules/enzyme-adapter-react-16/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true,
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/enzyme-adapter-utils": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.1.tgz",
- "integrity": "sha512-JZgMPF1QOI7IzBj24EZoDpaeG/p8Os7WeBZWTJydpsH7JRStc7jYbHE4CmNQaLqazaGFyLM8ALWA3IIZvxW3PQ==",
- "dev": true,
- "dependencies": {
- "airbnb-prop-types": "^2.16.0",
- "function.prototype.name": "^1.1.5",
- "has": "^1.0.3",
- "object.assign": "^4.1.4",
- "object.fromentries": "^2.0.5",
- "prop-types": "^15.8.1",
- "semver": "^5.7.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- },
- "peerDependencies": {
- "react": "0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0"
- }
- },
- "node_modules/enzyme-adapter-utils/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "node_modules/enzyme-adapter-utils/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true,
- "bin": {
- "semver": "bin/semver"
- }
- },
"node_modules/enzyme-shallow-equal": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.5.tgz",
- "integrity": "sha512-i6cwm7hN630JXenxxJFBKzgLC3hMTafFQXflvzHgPmDhOBhxUWDe8AeRv1qp2/uWJ2Y8z5yLWMzmAfkTOiOCZg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has": "^1.0.3",
"object-is": "^1.1.5"
@@ -10147,24 +9141,21 @@
},
"node_modules/error-ex": {
"version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
}
},
"node_modules/error-stack-parser": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
- "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
+ "license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/es-abstract": {
"version": "1.21.2",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
- "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
+ "license": "MIT",
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
"available-typed-arrays": "^1.0.5",
@@ -10210,13 +9201,11 @@
},
"node_modules/es-array-method-boxes-properly": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
- "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
+ "license": "MIT"
},
"node_modules/es-get-iterator": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
- "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.3",
@@ -10234,18 +9223,15 @@
},
"node_modules/es-get-iterator/node_modules/isarray": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ "license": "MIT"
},
"node_modules/es-module-lexer": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz",
- "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg=="
+ "license": "MIT"
},
"node_modules/es-set-tostringtag": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
- "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+ "license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.3",
"has": "^1.0.3",
@@ -10257,16 +9243,14 @@
},
"node_modules/es-shim-unscopables": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
- "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+ "license": "MIT",
"dependencies": {
"has": "^1.0.3"
}
},
"node_modules/es-to-primitive": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "license": "MIT",
"dependencies": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
@@ -10281,34 +9265,29 @@
},
"node_modules/es6-error": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="
+ "license": "MIT"
},
"node_modules/escalade": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ "license": "MIT"
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/escodegen": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
- "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
@@ -10328,8 +9307,7 @@
},
"node_modules/escodegen/node_modules/levn": {
"version": "0.3.0",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
- "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "license": "MIT",
"dependencies": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
@@ -10340,8 +9318,7 @@
},
"node_modules/escodegen/node_modules/optionator": {
"version": "0.8.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
- "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "license": "MIT",
"dependencies": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
@@ -10356,16 +9333,13 @@
},
"node_modules/escodegen/node_modules/prelude-ls": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/escodegen/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
@@ -10373,8 +9347,7 @@
},
"node_modules/escodegen/node_modules/type-check": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
- "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "license": "MIT",
"dependencies": {
"prelude-ls": "~1.1.2"
},
@@ -10384,8 +9357,7 @@
},
"node_modules/eslint": {
"version": "8.38.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
- "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
@@ -10440,8 +9412,7 @@
},
"node_modules/eslint-config-airbnb": {
"version": "19.0.4",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
- "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+ "license": "MIT",
"dependencies": {
"eslint-config-airbnb-base": "^15.0.0",
"object.assign": "^4.1.2",
@@ -10460,8 +9431,7 @@
},
"node_modules/eslint-config-airbnb-base": {
"version": "15.0.0",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
- "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+ "license": "MIT",
"dependencies": {
"confusing-browser-globals": "^1.0.10",
"object.assign": "^4.1.2",
@@ -10478,8 +9448,7 @@
},
"node_modules/eslint-config-airbnb-typescript": {
"version": "17.0.0",
- "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz",
- "integrity": "sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==",
+ "license": "MIT",
"dependencies": {
"eslint-config-airbnb-base": "^15.0.0"
},
@@ -10492,8 +9461,7 @@
},
"node_modules/eslint-import-resolver-node": {
"version": "0.3.7",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
- "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==",
+ "license": "MIT",
"dependencies": {
"debug": "^3.2.7",
"is-core-module": "^2.11.0",
@@ -10502,16 +9470,14 @@
},
"node_modules/eslint-import-resolver-node/node_modules/debug": {
"version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "license": "MIT",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/eslint-module-utils": {
"version": "2.8.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz",
- "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==",
+ "license": "MIT",
"dependencies": {
"debug": "^3.2.7"
},
@@ -10526,16 +9492,14 @@
},
"node_modules/eslint-module-utils/node_modules/debug": {
"version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "license": "MIT",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/eslint-plugin-import": {
"version": "2.27.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
- "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
+ "license": "MIT",
"dependencies": {
"array-includes": "^3.1.6",
"array.prototype.flat": "^1.3.1",
@@ -10562,16 +9526,14 @@
},
"node_modules/eslint-plugin-import/node_modules/debug": {
"version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "license": "MIT",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/eslint-plugin-import/node_modules/doctrine": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "license": "Apache-2.0",
"dependencies": {
"esutils": "^2.0.2"
},
@@ -10581,8 +9543,7 @@
},
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.7.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz",
- "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.7",
"aria-query": "^5.1.3",
@@ -10610,8 +9571,7 @@
},
"node_modules/eslint-plugin-react": {
"version": "7.32.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz",
- "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==",
+ "license": "MIT",
"dependencies": {
"array-includes": "^3.1.6",
"array.prototype.flatmap": "^1.3.1",
@@ -10638,8 +9598,7 @@
},
"node_modules/eslint-plugin-react-hooks": {
"version": "4.6.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
- "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -10649,8 +9608,7 @@
},
"node_modules/eslint-plugin-react/node_modules/doctrine": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "license": "Apache-2.0",
"dependencies": {
"esutils": "^2.0.2"
},
@@ -10660,8 +9618,7 @@
},
"node_modules/eslint-plugin-react/node_modules/prop-types": {
"version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -10670,8 +9627,7 @@
},
"node_modules/eslint-plugin-react/node_modules/resolve": {
"version": "2.0.0-next.4",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
- "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
+ "license": "MIT",
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
@@ -10686,8 +9642,7 @@
},
"node_modules/eslint-scope": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
- "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@@ -10698,16 +9653,14 @@
},
"node_modules/eslint-scope/node_modules/estraverse": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
},
"node_modules/eslint-visitor-keys": {
"version": "3.4.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
- "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
+ "license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -10717,8 +9670,7 @@
},
"node_modules/eslint/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -10731,13 +9683,11 @@
},
"node_modules/eslint/node_modules/argparse": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ "license": "Python-2.0"
},
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -10751,8 +9701,7 @@
},
"node_modules/eslint/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -10762,13 +9711,11 @@
},
"node_modules/eslint/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -10778,8 +9725,7 @@
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "7.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
- "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -10790,8 +9736,7 @@
},
"node_modules/eslint/node_modules/glob-parent": {
"version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -10801,8 +9746,7 @@
},
"node_modules/eslint/node_modules/globals": {
"version": "13.19.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
- "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+ "license": "MIT",
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -10815,16 +9759,14 @@
},
"node_modules/eslint/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/eslint/node_modules/js-yaml": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -10834,8 +9776,7 @@
},
"node_modules/eslint/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -10845,8 +9786,7 @@
},
"node_modules/eslint/node_modules/type-fest": {
"version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
@@ -10856,8 +9796,7 @@
},
"node_modules/espree": {
"version": "9.5.2",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz",
- "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==",
+ "license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
@@ -10872,8 +9811,7 @@
},
"node_modules/esprima": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
@@ -10884,8 +9822,7 @@
},
"node_modules/esquery": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
- "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "license": "BSD-3-Clause",
"dependencies": {
"estraverse": "^5.1.0"
},
@@ -10895,8 +9832,7 @@
},
"node_modules/esrecurse": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -10906,50 +9842,43 @@
},
"node_modules/estraverse": {
"version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/eventemitter3": {
"version": "4.0.7",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
- "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+ "license": "MIT"
},
"node_modules/events": {
"version": "3.3.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/exec-sh": {
"version": "0.3.6",
- "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz",
- "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w=="
+ "license": "MIT"
},
"node_modules/execa": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "license": "MIT",
"dependencies": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
@@ -10965,8 +9894,7 @@
},
"node_modules/execa/node_modules/cross-spawn": {
"version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "license": "MIT",
"dependencies": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
@@ -10980,24 +9908,21 @@
},
"node_modules/execa/node_modules/path-key": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/execa/node_modules/semver": {
"version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/execa/node_modules/shebang-command": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+ "license": "MIT",
"dependencies": {
"shebang-regex": "^1.0.0"
},
@@ -11007,16 +9932,14 @@
},
"node_modules/execa/node_modules/shebang-regex": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/execa/node_modules/which": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
@@ -11026,16 +9949,13 @@
},
"node_modules/exit": {
"version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/expand-brackets": {
"version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+ "license": "MIT",
"dependencies": {
"debug": "^2.3.3",
"define-property": "^0.2.5",
@@ -11051,16 +9971,14 @@
},
"node_modules/expand-brackets/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/expand-brackets/node_modules/define-property": {
"version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^0.1.0"
},
@@ -11070,8 +9988,7 @@
},
"node_modules/expand-brackets/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -11081,8 +9998,7 @@
},
"node_modules/expand-brackets/node_modules/is-accessor-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -11092,8 +10008,7 @@
},
"node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -11103,13 +10018,11 @@
},
"node_modules/expand-brackets/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/expand-brackets/node_modules/is-data-descriptor": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -11119,8 +10032,7 @@
},
"node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -11130,8 +10042,7 @@
},
"node_modules/expand-brackets/node_modules/is-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "license": "MIT",
"dependencies": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
@@ -11143,38 +10054,33 @@
},
"node_modules/expand-brackets/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/expand-brackets/node_modules/kind-of": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/expand-brackets/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/expand-template": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "license": "(MIT OR WTFPL)",
"engines": {
"node": ">=6"
}
},
"node_modules/expect": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
- "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/expect-utils": "^29.5.0",
"jest-get-type": "^29.4.3",
@@ -11188,9 +10094,8 @@
},
"node_modules/expect/node_modules/@jest/types": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
- "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/schemas": "^29.4.3",
"@types/istanbul-lib-coverage": "^2.0.0",
@@ -11205,18 +10110,16 @@
},
"node_modules/expect/node_modules/@types/yargs": {
"version": "17.0.24",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
- "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/expect/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -11229,9 +10132,8 @@
},
"node_modules/expect/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -11245,8 +10147,6 @@
},
"node_modules/expect/node_modules/ci-info": {
"version": "3.8.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
- "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
"dev": true,
"funding": [
{
@@ -11254,15 +10154,15 @@
"url": "https://github.com/sponsors/sibiraj-s"
}
],
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/expect/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -11272,24 +10172,21 @@
},
"node_modules/expect/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/expect/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/expect/node_modules/jest-util": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
- "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/types": "^29.5.0",
"@types/node": "*",
@@ -11304,9 +10201,8 @@
},
"node_modules/expect/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -11316,8 +10212,7 @@
},
"node_modules/express": {
"version": "4.18.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
- "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -11357,44 +10252,37 @@
},
"node_modules/express/node_modules/array-flatten": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ "license": "MIT"
},
"node_modules/express/node_modules/cookie": {
"version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/express/node_modules/path-to-regexp": {
"version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+ "license": "MIT"
},
"node_modules/extend": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ "license": "MIT"
},
"node_modules/extend-shallow": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "license": "MIT",
"dependencies": {
"assign-symbols": "^1.0.0",
"is-extendable": "^1.0.1"
@@ -11405,8 +10293,7 @@
},
"node_modules/extglob": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "license": "MIT",
"dependencies": {
"array-unique": "^0.3.2",
"define-property": "^1.0.0",
@@ -11423,8 +10310,7 @@
},
"node_modules/extglob/node_modules/define-property": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^1.0.0"
},
@@ -11434,8 +10320,7 @@
},
"node_modules/extglob/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -11445,26 +10330,22 @@
},
"node_modules/extglob/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ "license": "MIT"
},
"node_modules/fast-defer": {
"version": "1.1.7",
- "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.7.tgz",
- "integrity": "sha512-tJ01ulDWT2WhqxMKS20nXX6wyX2iInBYpbN3GO7yjKwXMY4qvkdBRxak9IFwBLlFDESox+SwSvqMCZDfe1tqeg=="
+ "license": "MIT"
},
"node_modules/fast-glob": {
"version": "3.2.12",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
- "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -11478,34 +10359,29 @@
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ "license": "MIT"
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+ "license": "MIT"
},
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
- "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
- "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "license": "MIT",
"engines": {
"node": ">= 4.9.1"
}
},
"node_modules/fastq": {
"version": "1.13.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
- "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
}
},
"node_modules/faye-websocket": {
"version": "0.11.4",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
- "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "license": "Apache-2.0",
"dependencies": {
"websocket-driver": ">=0.5.1"
},
@@ -11515,16 +10391,14 @@
},
"node_modules/fb-watchman": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
- "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "license": "Apache-2.0",
"dependencies": {
"bser": "2.1.1"
}
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "license": "MIT",
"dependencies": {
"flat-cache": "^3.0.4"
},
@@ -11534,8 +10408,7 @@
},
"node_modules/file-loader": {
"version": "6.2.0",
- "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
- "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
+ "license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0"
@@ -11553,13 +10426,11 @@
},
"node_modules/file-saver": {
"version": "1.3.8",
- "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
- "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
+ "license": "MIT"
},
"node_modules/file-selector": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
- "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
},
@@ -11569,17 +10440,15 @@
},
"node_modules/filesize": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
- "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==",
"dev": true,
+ "license": "BSD-3-Clause",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -11589,25 +10458,22 @@
},
"node_modules/filter-console": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/filter-console/-/filter-console-0.1.1.tgz",
- "integrity": "sha512-zrXoV1Uaz52DqPs+qEwNJWJFAWZpYJ47UNmpN9q4j+/EYsz85uV0DC9k8tRND5kYmoVzL0W+Y75q4Rg8sRJCdg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/filter-obj": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
- "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
@@ -11623,21 +10489,18 @@
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
- "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+ "license": "MIT",
"dependencies": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
@@ -11652,8 +10515,7 @@
},
"node_modules/find-cache-dir/node_modules/make-dir": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "license": "MIT",
"dependencies": {
"semver": "^6.0.0"
},
@@ -11666,8 +10528,7 @@
},
"node_modules/find-up": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -11690,8 +10551,7 @@
},
"node_modules/flat-cache": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
- "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "license": "MIT",
"dependencies": {
"flatted": "^3.1.0",
"rimraf": "^3.0.2"
@@ -11702,8 +10562,7 @@
},
"node_modules/flat-cache/node_modules/rimraf": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
@@ -11716,13 +10575,11 @@
},
"node_modules/flatted": {
"version": "3.2.7",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
- "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
+ "license": "ISC"
},
"node_modules/focus-lock": {
"version": "0.11.3",
- "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.3.tgz",
- "integrity": "sha512-4n0pYcPTa/uI7Q66BZna61nRT7lDhnuJ9PJr6wiDjx4uStg491ks41y7uOG+s0umaaa+hulNKSldU9aTg9/yVg==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.0.3"
},
@@ -11732,14 +10589,13 @@
},
"node_modules/follow-redirects": {
"version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
+ "license": "MIT",
"engines": {
"node": ">=4.0"
},
@@ -11751,33 +10607,29 @@
},
"node_modules/font-awesome": {
"version": "4.7.0",
- "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
- "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
+ "license": "(OFL-1.1 AND MIT)",
"engines": {
"node": ">=0.10.3"
}
},
"node_modules/for-each": {
"version": "0.3.3",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
- "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "license": "MIT",
"dependencies": {
"is-callable": "^1.1.3"
}
},
"node_modules/for-in": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/fork-ts-checker-webpack-plugin": {
"version": "4.1.6",
- "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz",
- "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.5.5",
"chalk": "^2.4.1",
@@ -11794,9 +10646,8 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": {
"version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
@@ -11815,9 +10666,8 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/braces/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -11827,9 +10677,8 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
@@ -11842,9 +10691,8 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -11854,24 +10702,21 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -11881,9 +10726,8 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -11893,18 +10737,16 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": {
"version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
@@ -11926,18 +10768,16 @@
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": {
"version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true,
+ "license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-number": "^3.0.0",
"repeat-string": "^1.6.1"
@@ -11948,8 +10788,7 @@
},
"node_modules/form-data": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
- "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -11961,30 +10800,25 @@
},
"node_modules/form-urlencoded": {
"version": "4.1.4",
- "resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-4.1.4.tgz",
- "integrity": "sha512-R7Vytos0gMYuPQTMwnNzvK9PBItNV+Qkm/pvghEZI3j2kMrzZmJlczAgHFmt12VV+IRYQXgTlSGP1PKAsMCIUA=="
+ "license": "MIT"
},
"node_modules/formidable": {
"version": "1.2.6",
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
- "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
- "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
+ "license": "MIT",
"funding": {
"url": "https://ko-fi.com/tunnckoCore/commissions"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fraction.js": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
- "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+ "license": "MIT",
"engines": {
"node": "*"
},
@@ -11995,8 +10829,7 @@
},
"node_modules/fragment-cache": {
"version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+ "license": "MIT",
"dependencies": {
"map-cache": "^0.2.2"
},
@@ -12006,8 +10839,7 @@
},
"node_modules/fresh": {
"version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
@@ -12018,13 +10850,11 @@
},
"node_modules/fs-constants": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+ "license": "MIT"
},
"node_modules/fs-extra": {
"version": "9.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
- "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
@@ -12037,24 +10867,19 @@
},
"node_modules/fs-monkey": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
- "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q=="
+ "license": "Unlicense"
},
"node_modules/fs-readdir-recursive": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
- "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA=="
+ "license": "MIT"
},
"node_modules/fs.realpath": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ "license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "hasInstallScript": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -12073,8 +10898,7 @@
},
"node_modules/function.prototype.name": {
"version": "1.1.5",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
- "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3",
@@ -12090,32 +10914,28 @@
},
"node_modules/functions-have-names": {
"version": "1.2.3",
- "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
- "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
- "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+ "license": "MIT",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
@@ -12128,24 +10948,21 @@
},
"node_modules/get-nonce": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
- "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/get-package-type": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/get-stream": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
@@ -12155,8 +10972,7 @@
},
"node_modules/get-symbol-description": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
- "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.1"
@@ -12170,21 +10986,18 @@
},
"node_modules/get-value": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/github-from-package": {
"version": "0.0.0",
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
+ "license": "MIT"
},
"node_modules/glob": {
"version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -12202,8 +11015,7 @@
},
"node_modules/glob-parent": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -12213,13 +11025,11 @@
},
"node_modules/glob-to-regexp": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
+ "license": "BSD-2-Clause"
},
"node_modules/global-modules": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
- "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "license": "MIT",
"dependencies": {
"global-prefix": "^3.0.0"
},
@@ -12229,8 +11039,7 @@
},
"node_modules/global-prefix": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
- "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "license": "MIT",
"dependencies": {
"ini": "^1.3.5",
"kind-of": "^6.0.2",
@@ -12242,8 +11051,7 @@
},
"node_modules/global-prefix/node_modules/which": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
@@ -12253,16 +11061,14 @@
},
"node_modules/globals": {
"version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/globalthis": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
- "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "license": "MIT",
"dependencies": {
"define-properties": "^1.1.3"
},
@@ -12275,8 +11081,7 @@
},
"node_modules/globby": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
- "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "license": "MIT",
"dependencies": {
"array-union": "^1.0.1",
"glob": "^7.0.3",
@@ -12290,16 +11095,14 @@
},
"node_modules/globby/node_modules/pify": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gopd": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
@@ -12309,25 +11112,21 @@
},
"node_modules/graceful-fs": {
"version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ "license": "ISC"
},
"node_modules/grapheme-splitter": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
+ "license": "MIT"
},
"node_modules/growly": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
- "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
+ "license": "MIT",
"optional": true
},
"node_modules/gzip-size": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
- "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"duplexer": "^0.1.1",
"pify": "^4.0.1"
@@ -12338,18 +11137,15 @@
},
"node_modules/handle-thing": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
- "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg=="
+ "license": "MIT"
},
"node_modules/harmony-reflect": {
"version": "1.6.2",
- "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
- "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g=="
+ "license": "(Apache-2.0 OR MPL-1.1)"
},
"node_modules/has": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "license": "MIT",
"dependencies": {
"function-bind": "^1.1.1"
},
@@ -12359,24 +11155,21 @@
},
"node_modules/has-bigints": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
- "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
- "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.1"
},
@@ -12386,8 +11179,7 @@
},
"node_modules/has-proto": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
- "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -12397,8 +11189,7 @@
},
"node_modules/has-symbols": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -12408,8 +11199,7 @@
},
"node_modules/has-tostringtag": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
- "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "license": "MIT",
"dependencies": {
"has-symbols": "^1.0.2"
},
@@ -12422,8 +11212,7 @@
},
"node_modules/has-value": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+ "license": "MIT",
"dependencies": {
"get-value": "^2.0.6",
"has-values": "^1.0.0",
@@ -12435,16 +11224,14 @@
},
"node_modules/has-value/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/has-values": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+ "license": "MIT",
"dependencies": {
"is-number": "^3.0.0",
"kind-of": "^4.0.0"
@@ -12455,13 +11242,11 @@
},
"node_modules/has-values/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/has-values/node_modules/is-number": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -12471,8 +11256,7 @@
},
"node_modules/has-values/node_modules/is-number/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -12482,8 +11266,7 @@
},
"node_modules/has-values/node_modules/kind-of": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -12493,16 +11276,14 @@
},
"node_modules/he": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "license": "MIT",
"bin": {
"he": "bin/he"
}
},
"node_modules/history": {
"version": "4.10.1",
- "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
- "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
@@ -12514,21 +11295,18 @@
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
- "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+ "license": "ISC"
},
"node_modules/hpack.js": {
"version": "2.1.6",
- "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
- "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+ "license": "MIT",
"dependencies": {
"inherits": "^2.0.1",
"obuf": "^1.0.0",
@@ -12538,8 +11316,7 @@
},
"node_modules/html-dom-parser": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-3.1.2.tgz",
- "integrity": "sha512-mLTtl3pVn3HnqZSZzW3xVs/mJAKrG1yIw3wlp+9bdoZHHLaBRvELdpfShiPVLyjPypq1Fugv2KMDoGHW4lVXnw==",
+ "license": "MIT",
"dependencies": {
"domhandler": "5.0.3",
"htmlparser2": "8.0.1"
@@ -12547,9 +11324,8 @@
},
"node_modules/html-element-map": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.1.tgz",
- "integrity": "sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"array.prototype.filter": "^1.0.0",
"call-bind": "^1.0.2"
@@ -12560,8 +11336,7 @@
},
"node_modules/html-encoding-sniffer": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
- "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
+ "license": "MIT",
"dependencies": {
"whatwg-encoding": "^1.0.5"
},
@@ -12571,18 +11346,15 @@
},
"node_modules/html-entities": {
"version": "2.3.3",
- "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
- "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
+ "license": "MIT"
},
"node_modules/html-escaper": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="
+ "license": "MIT"
},
"node_modules/html-minifier-terser": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
- "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"camel-case": "^4.1.1",
@@ -12602,8 +11374,7 @@
},
"node_modules/html-react-parser": {
"version": "3.0.7",
- "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-3.0.7.tgz",
- "integrity": "sha512-4Nzpp1Lsd6ngOJR8T+Vc4u+Z77OddOgKL3KvwbtA0/U0Yv8v5JF+yewQxIudrdOWGYuO0Borc0vQ2y53pzBAwA==",
+ "license": "MIT",
"dependencies": {
"domhandler": "5.0.3",
"html-dom-parser": "3.1.2",
@@ -12616,8 +11387,7 @@
},
"node_modules/html-webpack-plugin": {
"version": "4.5.0",
- "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz",
- "integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"@types/html-minifier-terser": "^5.0.0",
@@ -12639,8 +11409,7 @@
},
"node_modules/html-webpack-plugin/node_modules/json5": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.0"
@@ -12651,8 +11420,7 @@
},
"node_modules/html-webpack-plugin/node_modules/loader-utils": {
"version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"big.js": "^5.2.2",
@@ -12665,8 +11433,6 @@
},
"node_modules/htmlparser2": {
"version": "8.0.1",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
- "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -12674,6 +11440,7 @@
"url": "https://github.com/sponsors/fb55"
}
],
+ "license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
@@ -12683,13 +11450,11 @@
},
"node_modules/http-deceiver": {
"version": "1.2.7",
- "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
- "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw=="
+ "license": "MIT"
},
"node_modules/http-errors": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
@@ -12703,13 +11468,11 @@
},
"node_modules/http-parser-js": {
"version": "0.5.8",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
- "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q=="
+ "license": "MIT"
},
"node_modules/http-proxy": {
"version": "1.18.1",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
- "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "license": "MIT",
"dependencies": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
@@ -12721,8 +11484,7 @@
},
"node_modules/http-proxy-agent": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "license": "MIT",
"dependencies": {
"@tootallnate/once": "1",
"agent-base": "6",
@@ -12734,8 +11496,7 @@
},
"node_modules/http-proxy-middleware": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
- "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
+ "license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.8",
"http-proxy": "^1.18.1",
@@ -12757,8 +11518,7 @@
},
"node_modules/http-proxy-middleware/node_modules/is-plain-obj": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
- "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -12768,8 +11528,7 @@
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
@@ -12780,18 +11539,16 @@
},
"node_modules/human-signals": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "license": "Apache-2.0",
"engines": {
"node": ">=10.17.0"
}
},
"node_modules/husky": {
"version": "0.14.3",
- "resolved": "https://registry.npmjs.org/husky/-/husky-0.14.3.tgz",
- "integrity": "sha512-e21wivqHpstpoiWA/Yi8eFti8E+sQDSS53cpJsPptPs295QTOQR0ZwnHo2TXy1XOpZFD9rPOd3NpmqTK6uMLJA==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"dependencies": {
"is-ci": "^1.0.10",
"normalize-path": "^1.0.0",
@@ -12803,22 +11560,19 @@
},
"node_modules/husky/node_modules/normalize-path": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz",
- "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/hyphenate-style-name": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
- "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
+ "license": "BSD-3-Clause"
},
"node_modules/i18n-iso-countries": {
"version": "4.3.1",
- "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-4.3.1.tgz",
- "integrity": "sha512-yxeCvmT8yO1p/epv93c1OHnnYNNMOX6NUNpNfuvzSIcDyripS7OGeKXgzYGd5QI31UK+GBrMG0nPFNv0jrHggw==",
+ "license": "MIT",
"dependencies": {
"diacritics": "^1.3.0"
},
@@ -12828,8 +11582,7 @@
},
"node_modules/iconv-lite": {
"version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
@@ -12839,8 +11592,7 @@
},
"node_modules/icss-utils": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
- "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "license": "ISC",
"engines": {
"node": "^10 || ^12 || >= 14"
},
@@ -12850,8 +11602,7 @@
},
"node_modules/identity-obj-proxy": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
- "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
+ "license": "MIT",
"dependencies": {
"harmony-reflect": "^1.4.6"
},
@@ -12861,8 +11612,6 @@
},
"node_modules/ieee754": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
@@ -12876,20 +11625,19 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "BSD-3-Clause"
},
"node_modules/ignore": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
- "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+ "license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/image-minimizer-webpack-plugin": {
"version": "3.8.2",
- "resolved": "https://registry.npmjs.org/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.2.tgz",
- "integrity": "sha512-l3nDq/c15y4ViTPtICG6lbFp77SoycSnR1hT/n3ER76uol//OpRptCDl7U1qiDSSEy2AcqPD1T7isRQ8TK27Cw==",
+ "license": "MIT",
"dependencies": {
"schema-utils": "^4.0.0",
"serialize-javascript": "^6.0.1"
@@ -12921,8 +11669,7 @@
},
"node_modules/image-minimizer-webpack-plugin/node_modules/ajv": {
"version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -12936,8 +11683,7 @@
},
"node_modules/image-minimizer-webpack-plugin/node_modules/ajv-keywords": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
@@ -12947,13 +11693,11 @@
},
"node_modules/image-minimizer-webpack-plugin/node_modules/json-schema-traverse": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "license": "MIT"
},
"node_modules/image-minimizer-webpack-plugin/node_modules/schema-utils": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
- "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
@@ -12968,101 +11712,14 @@
"url": "https://opencollective.com/webpack"
}
},
- "node_modules/imagemin-mozjpeg/node_modules/human-signals": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
- "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
- "extraneous": true,
- "engines": {
- "node": ">=8.12.0"
- }
- },
- "node_modules/imagemin-mozjpeg/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "extraneous": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imagemin-mozjpeg/node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "extraneous": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/imagemin/node_modules/array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "extraneous": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/imagemin/node_modules/globby": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz",
- "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==",
- "extraneous": true,
- "dependencies": {
- "@types/glob": "^7.1.1",
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.0.3",
- "glob": "^7.1.3",
- "ignore": "^5.1.1",
- "merge2": "^1.2.3",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/imagemin/node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "extraneous": true,
- "dependencies": {
- "semver": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imagemin/node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "extraneous": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/immediate": {
"version": "3.0.6",
- "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
- "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+ "license": "MIT"
},
"node_modules/immer": {
"version": "8.0.1",
- "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz",
- "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==",
"dev": true,
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -13070,13 +11727,11 @@
},
"node_modules/immutable": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
- "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ=="
+ "license": "MIT"
},
"node_modules/import-fresh": {
"version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -13090,16 +11745,14 @@
},
"node_modules/import-fresh/node_modules/resolve-from": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/import-local": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
- "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "license": "MIT",
"dependencies": {
"pkg-dir": "^4.2.0",
"resolve-cwd": "^3.0.0"
@@ -13116,25 +11769,22 @@
},
"node_modules/imurmurhash": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "license": "MIT",
"engines": {
"node": ">=0.8.19"
}
},
"node_modules/indent-string": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/inflight": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -13142,23 +11792,19 @@
},
"node_modules/inherits": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ "license": "ISC"
},
"node_modules/ini": {
"version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ "license": "ISC"
},
"node_modules/inline-style-parser": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
- "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="
+ "license": "MIT"
},
"node_modules/internal-slot": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
- "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+ "license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.0",
"has": "^1.0.3",
@@ -13170,16 +11816,14 @@
},
"node_modules/interpret": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
- "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==",
+ "license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/intl-messageformat": {
"version": "9.13.0",
- "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz",
- "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/fast-memoize": "1.2.1",
@@ -13189,33 +11833,28 @@
},
"node_modules/intl-messageformat-parser": {
"version": "5.5.1",
- "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz",
- "integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==",
- "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
+ "license": "BSD-3-Clause",
"dependencies": {
"@formatjs/intl-numberformat": "^5.5.2"
}
},
"node_modules/invariant": {
"version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/ipaddr.js": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
- "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==",
+ "license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/is-accessor-descriptor": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
- "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^6.0.0"
},
@@ -13225,8 +11864,7 @@
},
"node_modules/is-alphabetical": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
- "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -13234,8 +11872,7 @@
},
"node_modules/is-alphanumerical": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
- "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
+ "license": "MIT",
"dependencies": {
"is-alphabetical": "^1.0.0",
"is-decimal": "^1.0.0"
@@ -13247,8 +11884,7 @@
},
"node_modules/is-arguments": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
- "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
@@ -13262,8 +11898,7 @@
},
"node_modules/is-array-buffer": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
- "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.2.0",
@@ -13275,13 +11910,11 @@
},
"node_modules/is-arrayish": {
"version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ "license": "MIT"
},
"node_modules/is-bigint": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
- "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "license": "MIT",
"dependencies": {
"has-bigints": "^1.0.1"
},
@@ -13291,8 +11924,7 @@
},
"node_modules/is-binary-path": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -13302,8 +11934,7 @@
},
"node_modules/is-boolean-object": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
- "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
@@ -13317,8 +11948,6 @@
},
"node_modules/is-buffer": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
- "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
"funding": [
{
"type": "github",
@@ -13333,14 +11962,14 @@
"url": "https://feross.org/support"
}
],
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/is-callable": {
"version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -13350,9 +11979,8 @@
},
"node_modules/is-ci": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
- "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ci-info": "^1.5.0"
},
@@ -13362,8 +11990,7 @@
},
"node_modules/is-core-module": {
"version": "2.11.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
- "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "license": "MIT",
"dependencies": {
"has": "^1.0.3"
},
@@ -13373,8 +12000,7 @@
},
"node_modules/is-data-descriptor": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
- "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^6.0.0"
},
@@ -13384,8 +12010,7 @@
},
"node_modules/is-date-object": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
- "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "license": "MIT",
"dependencies": {
"has-tostringtag": "^1.0.0"
},
@@ -13398,8 +12023,7 @@
},
"node_modules/is-decimal": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
- "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -13407,8 +12031,7 @@
},
"node_modules/is-descriptor": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
- "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "license": "MIT",
"dependencies": {
"is-accessor-descriptor": "^1.0.0",
"is-data-descriptor": "^1.0.0",
@@ -13420,8 +12043,7 @@
},
"node_modules/is-docker": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "license": "MIT",
"bin": {
"is-docker": "cli.js"
},
@@ -13434,8 +12056,7 @@
},
"node_modules/is-extendable": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "license": "MIT",
"dependencies": {
"is-plain-object": "^2.0.4"
},
@@ -13445,32 +12066,28 @@
},
"node_modules/is-extglob": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-generator-fn": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -13480,8 +12097,7 @@
},
"node_modules/is-hexadecimal": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
- "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -13489,8 +12105,7 @@
},
"node_modules/is-invalid-path": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz",
- "integrity": "sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==",
+ "license": "MIT",
"dependencies": {
"is-glob": "^2.0.0"
},
@@ -13500,16 +12115,14 @@
},
"node_modules/is-invalid-path/node_modules/is-extglob": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
- "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-invalid-path/node_modules/is-glob": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
- "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==",
+ "license": "MIT",
"dependencies": {
"is-extglob": "^1.0.0"
},
@@ -13519,16 +12132,14 @@
},
"node_modules/is-map": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
- "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-negative-zero": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
- "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -13538,16 +12149,14 @@
},
"node_modules/is-number": {
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-number-object": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
- "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "license": "MIT",
"dependencies": {
"has-tostringtag": "^1.0.0"
},
@@ -13560,16 +12169,14 @@
},
"node_modules/is-path-cwd": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
- "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/is-path-in-cwd": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
- "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+ "license": "MIT",
"dependencies": {
"is-path-inside": "^2.1.0"
},
@@ -13579,8 +12186,7 @@
},
"node_modules/is-path-in-cwd/node_modules/is-path-inside": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
- "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
+ "license": "MIT",
"dependencies": {
"path-is-inside": "^1.0.2"
},
@@ -13590,16 +12196,14 @@
},
"node_modules/is-path-inside": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-plain-obj": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
- "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+ "license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
@@ -13607,8 +12211,7 @@
},
"node_modules/is-plain-object": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "license": "MIT",
"dependencies": {
"isobject": "^3.0.1"
},
@@ -13618,26 +12221,22 @@
},
"node_modules/is-plain-object/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
+ "license": "MIT"
},
"node_modules/is-promise": {
"version": "2.2.2",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
- "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="
+ "license": "MIT"
},
"node_modules/is-regex": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
- "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
@@ -13651,24 +12250,21 @@
},
"node_modules/is-root": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz",
- "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/is-set": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
- "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-shared-array-buffer": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
- "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2"
},
@@ -13678,16 +12274,14 @@
},
"node_modules/is-stream": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-string": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
- "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "license": "MIT",
"dependencies": {
"has-tostringtag": "^1.0.0"
},
@@ -13700,14 +12294,12 @@
},
"node_modules/is-subset": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
- "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/is-symbol": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
- "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "license": "MIT",
"dependencies": {
"has-symbols": "^1.0.2"
},
@@ -13720,8 +12312,7 @@
},
"node_modules/is-typed-array": {
"version": "1.1.10",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
- "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
+ "license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@@ -13738,13 +12329,11 @@
},
"node_modules/is-typedarray": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
+ "license": "MIT"
},
"node_modules/is-valid-path": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
- "integrity": "sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==",
+ "license": "MIT",
"dependencies": {
"is-invalid-path": "^0.1.0"
},
@@ -13754,16 +12343,14 @@
},
"node_modules/is-weakmap": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
- "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakref": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
- "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2"
},
@@ -13773,8 +12360,7 @@
},
"node_modules/is-weakset": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
- "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.1"
@@ -13785,8 +12371,7 @@
},
"node_modules/is-what": {
"version": "4.1.15",
- "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.15.tgz",
- "integrity": "sha512-uKua1wfy3Yt+YqsD6mTUEa2zSi3G1oPlqTflgaPJ7z63vUGN5pxFpnQfeSLMFnJDEsdvOtkp1rUWkYjB4YfhgA==",
+ "license": "MIT",
"engines": {
"node": ">=12.13"
},
@@ -13796,16 +12381,14 @@
},
"node_modules/is-windows": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-wsl": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "license": "MIT",
"dependencies": {
"is-docker": "^2.0.0"
},
@@ -13815,26 +12398,22 @@
},
"node_modules/isarray": {
"version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+ "license": "MIT"
},
"node_modules/isexe": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ "license": "ISC"
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=8"
}
},
"node_modules/istanbul-lib-instrument": {
"version": "5.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
- "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@babel/core": "^7.12.3",
"@babel/parser": "^7.14.7",
@@ -13848,8 +12427,7 @@
},
"node_modules/istanbul-lib-report": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "license": "BSD-3-Clause",
"dependencies": {
"istanbul-lib-coverage": "^3.0.0",
"make-dir": "^3.0.0",
@@ -13861,16 +12439,14 @@
},
"node_modules/istanbul-lib-report/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/istanbul-lib-report/node_modules/make-dir": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "license": "MIT",
"dependencies": {
"semver": "^6.0.0"
},
@@ -13883,8 +12459,7 @@
},
"node_modules/istanbul-lib-report/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -13894,8 +12469,7 @@
},
"node_modules/istanbul-lib-source-maps": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "license": "BSD-3-Clause",
"dependencies": {
"debug": "^4.1.1",
"istanbul-lib-coverage": "^3.0.0",
@@ -13907,16 +12481,14 @@
},
"node_modules/istanbul-lib-source-maps/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/istanbul-reports": {
"version": "3.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
- "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+ "license": "BSD-3-Clause",
"dependencies": {
"html-escaper": "^2.0.0",
"istanbul-lib-report": "^3.0.0"
@@ -13927,8 +12499,7 @@
},
"node_modules/jest": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz",
- "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==",
+ "license": "MIT",
"dependencies": {
"@jest/core": "^26.6.3",
"import-local": "^3.0.2",
@@ -13943,9 +12514,8 @@
},
"node_modules/jest-canvas-mock": {
"version": "2.4.0",
- "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz",
- "integrity": "sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"cssfontparser": "^1.2.1",
"moo-color": "^1.0.2"
@@ -13953,8 +12523,7 @@
},
"node_modules/jest-changed-files": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz",
- "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"execa": "^4.0.0",
@@ -13966,8 +12535,7 @@
},
"node_modules/jest-changed-files/node_modules/execa": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
- "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "license": "MIT",
"dependencies": {
"cross-spawn": "^7.0.0",
"get-stream": "^5.0.0",
@@ -13988,8 +12556,7 @@
},
"node_modules/jest-changed-files/node_modules/get-stream": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
@@ -14002,16 +12569,14 @@
},
"node_modules/jest-changed-files/node_modules/human-signals": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
- "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "license": "Apache-2.0",
"engines": {
"node": ">=8.12.0"
}
},
"node_modules/jest-changed-files/node_modules/is-stream": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "license": "MIT",
"engines": {
"node": ">=8"
},
@@ -14021,8 +12586,7 @@
},
"node_modules/jest-changed-files/node_modules/npm-run-path": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "license": "MIT",
"dependencies": {
"path-key": "^3.0.0"
},
@@ -14032,8 +12596,7 @@
},
"node_modules/jest-cli": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz",
- "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==",
+ "license": "MIT",
"dependencies": {
"@jest/core": "^26.6.3",
"@jest/test-result": "^26.6.2",
@@ -14058,8 +12621,7 @@
},
"node_modules/jest-cli/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14072,16 +12634,14 @@
},
"node_modules/jest-cli/node_modules/camelcase": {
"version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/jest-cli/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14095,13 +12655,11 @@
},
"node_modules/jest-cli/node_modules/ci-info": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+ "license": "MIT"
},
"node_modules/jest-cli/node_modules/cliui": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -14110,8 +12668,7 @@
},
"node_modules/jest-cli/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14121,13 +12678,11 @@
},
"node_modules/jest-cli/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-cli/node_modules/find-up": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -14138,16 +12693,14 @@
},
"node_modules/jest-cli/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-cli/node_modules/is-ci": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "license": "MIT",
"dependencies": {
"ci-info": "^2.0.0"
},
@@ -14157,8 +12710,7 @@
},
"node_modules/jest-cli/node_modules/locate-path": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -14168,8 +12720,7 @@
},
"node_modules/jest-cli/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -14182,8 +12733,7 @@
},
"node_modules/jest-cli/node_modules/p-locate": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -14193,8 +12743,7 @@
},
"node_modules/jest-cli/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -14204,8 +12753,7 @@
},
"node_modules/jest-cli/node_modules/wrap-ansi": {
"version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -14217,13 +12765,11 @@
},
"node_modules/jest-cli/node_modules/y18n": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
+ "license": "ISC"
},
"node_modules/jest-cli/node_modules/yargs": {
"version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "license": "MIT",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@@ -14243,8 +12789,7 @@
},
"node_modules/jest-cli/node_modules/yargs-parser": {
"version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "license": "ISC",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
@@ -14255,8 +12800,7 @@
},
"node_modules/jest-config": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz",
- "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==",
+ "license": "MIT",
"dependencies": {
"@babel/core": "^7.1.0",
"@jest/test-sequencer": "^26.6.3",
@@ -14291,8 +12835,7 @@
},
"node_modules/jest-config/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14305,8 +12848,7 @@
},
"node_modules/jest-config/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14320,8 +12862,7 @@
},
"node_modules/jest-config/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14331,21 +12872,18 @@
},
"node_modules/jest-config/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-config/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-config/node_modules/jest-environment-jsdom": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz",
- "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==",
+ "license": "MIT",
"dependencies": {
"@jest/environment": "^26.6.2",
"@jest/fake-timers": "^26.6.2",
@@ -14361,16 +12899,14 @@
},
"node_modules/jest-config/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-config/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -14380,9 +12916,8 @@
},
"node_modules/jest-diff": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
- "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^29.4.3",
@@ -14395,9 +12930,8 @@
},
"node_modules/jest-diff/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14410,9 +12944,8 @@
},
"node_modules/jest-diff/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14426,9 +12959,8 @@
},
"node_modules/jest-diff/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14438,24 +12970,21 @@
},
"node_modules/jest-diff/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jest-diff/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-diff/node_modules/pretty-format": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
- "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
@@ -14467,9 +12996,8 @@
},
"node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -14479,15 +13007,13 @@
},
"node_modules/jest-diff/node_modules/react-is": {
"version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jest-diff/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -14497,8 +13023,7 @@
},
"node_modules/jest-docblock": {
"version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz",
- "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==",
+ "license": "MIT",
"dependencies": {
"detect-newline": "^3.0.0"
},
@@ -14508,8 +13033,7 @@
},
"node_modules/jest-each": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz",
- "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"chalk": "^4.0.0",
@@ -14523,8 +13047,7 @@
},
"node_modules/jest-each/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14537,8 +13060,7 @@
},
"node_modules/jest-each/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14552,8 +13074,7 @@
},
"node_modules/jest-each/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14563,29 +13084,25 @@
},
"node_modules/jest-each/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-each/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-each/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-each/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -14595,8 +13112,7 @@
},
"node_modules/jest-environment-jsdom": {
"version": "26.6.1",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.1.tgz",
- "integrity": "sha512-A17RiXuHYNVlkM+3QNcQ6n5EZyAc6eld8ra9TW26luounGWpku4tj03uqRgHJCI1d4uHr5rJiuCH5JFRtdmrcA==",
+ "license": "MIT",
"dependencies": {
"@jest/environment": "^26.6.1",
"@jest/fake-timers": "^26.6.1",
@@ -14612,8 +13128,7 @@
},
"node_modules/jest-environment-node": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz",
- "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==",
+ "license": "MIT",
"dependencies": {
"@jest/environment": "^26.6.2",
"@jest/fake-timers": "^26.6.2",
@@ -14628,17 +13143,15 @@
},
"node_modules/jest-get-type": {
"version": "29.4.3",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
- "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/jest-haste-map": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
- "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"@types/graceful-fs": "^4.1.2",
@@ -14663,8 +13176,7 @@
},
"node_modules/jest-jasmine2": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz",
- "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==",
+ "license": "MIT",
"dependencies": {
"@babel/traverse": "^7.1.0",
"@jest/environment": "^26.6.2",
@@ -14691,8 +13203,7 @@
},
"node_modules/jest-jasmine2/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14705,8 +13216,7 @@
},
"node_modules/jest-jasmine2/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14720,8 +13230,7 @@
},
"node_modules/jest-jasmine2/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14731,21 +13240,18 @@
},
"node_modules/jest-jasmine2/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-jasmine2/node_modules/diff-sequences": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-jasmine2/node_modules/expect": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"ansi-styles": "^4.0.0",
@@ -14760,16 +13266,14 @@
},
"node_modules/jest-jasmine2/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-jasmine2/node_modules/jest-diff": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^26.6.2",
@@ -14782,16 +13286,14 @@
},
"node_modules/jest-jasmine2/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-jasmine2/node_modules/jest-matcher-utils": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"jest-diff": "^26.6.2",
@@ -14804,8 +13306,7 @@
},
"node_modules/jest-jasmine2/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -14823,16 +13324,14 @@
},
"node_modules/jest-jasmine2/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-jasmine2/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -14842,8 +13341,7 @@
},
"node_modules/jest-leak-detector": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz",
- "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==",
+ "license": "MIT",
"dependencies": {
"jest-get-type": "^26.3.0",
"pretty-format": "^26.6.2"
@@ -14854,26 +13352,23 @@
},
"node_modules/jest-leak-detector/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-localstorage-mock": {
"version": "2.4.22",
- "resolved": "https://registry.npmjs.org/jest-localstorage-mock/-/jest-localstorage-mock-2.4.22.tgz",
- "integrity": "sha512-60PWSDFQOS5v7JzSmYLM3dPLg0JLl+2Vc4lIEz/rj2yrXJzegsFLn7anwc5IL0WzJbBa/Las064CHbFg491/DQ==",
"dev": true,
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=6.16.0"
}
},
"node_modules/jest-matcher-utils": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
- "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"jest-diff": "^29.5.0",
@@ -14886,9 +13381,8 @@
},
"node_modules/jest-matcher-utils/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -14901,9 +13395,8 @@
},
"node_modules/jest-matcher-utils/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -14917,9 +13410,8 @@
},
"node_modules/jest-matcher-utils/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -14929,24 +13421,21 @@
},
"node_modules/jest-matcher-utils/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jest-matcher-utils/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-matcher-utils/node_modules/pretty-format": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
- "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
@@ -14958,9 +13447,8 @@
},
"node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -14970,15 +13458,13 @@
},
"node_modules/jest-matcher-utils/node_modules/react-is": {
"version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jest-matcher-utils/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -14988,9 +13474,8 @@
},
"node_modules/jest-message-util": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
- "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.12.13",
"@jest/types": "^29.5.0",
@@ -15008,9 +13493,8 @@
},
"node_modules/jest-message-util/node_modules/@jest/types": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
- "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/schemas": "^29.4.3",
"@types/istanbul-lib-coverage": "^2.0.0",
@@ -15025,18 +13509,16 @@
},
"node_modules/jest-message-util/node_modules/@types/yargs": {
"version": "17.0.24",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
- "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/jest-message-util/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15049,9 +13531,8 @@
},
"node_modules/jest-message-util/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15065,9 +13546,8 @@
},
"node_modules/jest-message-util/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15077,24 +13557,21 @@
},
"node_modules/jest-message-util/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jest-message-util/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-message-util/node_modules/pretty-format": {
"version": "29.5.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
- "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/schemas": "^29.4.3",
"ansi-styles": "^5.0.0",
@@ -15106,9 +13583,8 @@
},
"node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -15118,24 +13594,21 @@
},
"node_modules/jest-message-util/node_modules/react-is": {
"version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/jest-message-util/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-message-util/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15145,8 +13618,7 @@
},
"node_modules/jest-mock": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz",
- "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"@types/node": "*"
@@ -15157,8 +13629,7 @@
},
"node_modules/jest-pnp-resolver": {
"version": "1.2.3",
- "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
- "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+ "license": "MIT",
"engines": {
"node": ">=6"
},
@@ -15173,16 +13644,14 @@
},
"node_modules/jest-regex-util": {
"version": "26.0.0",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz",
- "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-resolve": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz",
- "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"chalk": "^4.0.0",
@@ -15199,8 +13668,7 @@
},
"node_modules/jest-resolve-dependencies": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz",
- "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"jest-regex-util": "^26.0.0",
@@ -15212,8 +13680,7 @@
},
"node_modules/jest-resolve/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15226,8 +13693,7 @@
},
"node_modules/jest-resolve/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15241,8 +13707,7 @@
},
"node_modules/jest-resolve/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15252,29 +13717,25 @@
},
"node_modules/jest-resolve/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-resolve/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-resolve/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-resolve/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15284,8 +13745,7 @@
},
"node_modules/jest-runner": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz",
- "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==",
+ "license": "MIT",
"dependencies": {
"@jest/console": "^26.6.2",
"@jest/environment": "^26.6.2",
@@ -15314,8 +13774,7 @@
},
"node_modules/jest-runner/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15328,8 +13787,7 @@
},
"node_modules/jest-runner/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15343,8 +13801,7 @@
},
"node_modules/jest-runner/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15354,21 +13811,18 @@
},
"node_modules/jest-runner/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-runner/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-runner/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -15386,16 +13840,14 @@
},
"node_modules/jest-runner/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-runner/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15405,8 +13857,7 @@
},
"node_modules/jest-runtime": {
"version": "26.6.3",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz",
- "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==",
+ "license": "MIT",
"dependencies": {
"@jest/console": "^26.6.2",
"@jest/environment": "^26.6.2",
@@ -15445,8 +13896,7 @@
},
"node_modules/jest-runtime/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15459,16 +13909,14 @@
},
"node_modules/jest-runtime/node_modules/camelcase": {
"version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/jest-runtime/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15482,8 +13930,7 @@
},
"node_modules/jest-runtime/node_modules/cliui": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -15492,8 +13939,7 @@
},
"node_modules/jest-runtime/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15503,13 +13949,11 @@
},
"node_modules/jest-runtime/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-runtime/node_modules/find-up": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -15520,16 +13964,14 @@
},
"node_modules/jest-runtime/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-runtime/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -15547,8 +13989,7 @@
},
"node_modules/jest-runtime/node_modules/locate-path": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -15558,8 +13999,7 @@
},
"node_modules/jest-runtime/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -15572,8 +14012,7 @@
},
"node_modules/jest-runtime/node_modules/p-locate": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -15583,16 +14022,14 @@
},
"node_modules/jest-runtime/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-runtime/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15602,8 +14039,7 @@
},
"node_modules/jest-runtime/node_modules/wrap-ansi": {
"version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -15615,13 +14051,11 @@
},
"node_modules/jest-runtime/node_modules/y18n": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
+ "license": "ISC"
},
"node_modules/jest-runtime/node_modules/yargs": {
"version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "license": "MIT",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@@ -15641,8 +14075,7 @@
},
"node_modules/jest-runtime/node_modules/yargs-parser": {
"version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "license": "ISC",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
@@ -15653,8 +14086,7 @@
},
"node_modules/jest-serializer": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz",
- "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*",
"graceful-fs": "^4.2.4"
@@ -15665,8 +14097,7 @@
},
"node_modules/jest-snapshot": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz",
- "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==",
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -15691,8 +14122,7 @@
},
"node_modules/jest-snapshot/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15705,8 +14135,7 @@
},
"node_modules/jest-snapshot/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15720,8 +14149,7 @@
},
"node_modules/jest-snapshot/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15731,21 +14159,18 @@
},
"node_modules/jest-snapshot/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-snapshot/node_modules/diff-sequences": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
- "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-snapshot/node_modules/expect": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz",
- "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"ansi-styles": "^4.0.0",
@@ -15760,16 +14185,14 @@
},
"node_modules/jest-snapshot/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-snapshot/node_modules/jest-diff": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
- "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^26.6.2",
@@ -15782,16 +14205,14 @@
},
"node_modules/jest-snapshot/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-snapshot/node_modules/jest-matcher-utils": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz",
- "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==",
+ "license": "MIT",
"dependencies": {
"chalk": "^4.0.0",
"jest-diff": "^26.6.2",
@@ -15804,8 +14225,7 @@
},
"node_modules/jest-snapshot/node_modules/jest-message-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz",
- "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@jest/types": "^26.6.2",
@@ -15823,8 +14243,7 @@
},
"node_modules/jest-snapshot/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -15837,16 +14256,14 @@
},
"node_modules/jest-snapshot/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-snapshot/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15856,8 +14273,7 @@
},
"node_modules/jest-util": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
- "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"@types/node": "*",
@@ -15872,8 +14288,7 @@
},
"node_modules/jest-util/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15886,8 +14301,7 @@
},
"node_modules/jest-util/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15901,13 +14315,11 @@
},
"node_modules/jest-util/node_modules/ci-info": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+ "license": "MIT"
},
"node_modules/jest-util/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15917,21 +14329,18 @@
},
"node_modules/jest-util/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-util/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-util/node_modules/is-ci": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
- "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "license": "MIT",
"dependencies": {
"ci-info": "^2.0.0"
},
@@ -15941,8 +14350,7 @@
},
"node_modules/jest-util/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15952,8 +14360,7 @@
},
"node_modules/jest-validate": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz",
- "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"camelcase": "^6.0.0",
@@ -15968,8 +14375,7 @@
},
"node_modules/jest-validate/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15982,8 +14388,7 @@
},
"node_modules/jest-validate/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15997,8 +14402,7 @@
},
"node_modules/jest-validate/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -16008,29 +14412,25 @@
},
"node_modules/jest-validate/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-validate/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-validate/node_modules/jest-get-type": {
"version": "26.3.0",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
- "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
+ "license": "MIT",
"engines": {
"node": ">= 10.14.2"
}
},
"node_modules/jest-validate/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -16040,8 +14440,7 @@
},
"node_modules/jest-watcher": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz",
- "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==",
+ "license": "MIT",
"dependencies": {
"@jest/test-result": "^26.6.2",
"@jest/types": "^26.6.2",
@@ -16057,8 +14456,7 @@
},
"node_modules/jest-watcher/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -16071,8 +14469,7 @@
},
"node_modules/jest-watcher/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -16086,8 +14483,7 @@
},
"node_modules/jest-watcher/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -16097,21 +14493,18 @@
},
"node_modules/jest-watcher/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/jest-watcher/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-watcher/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -16121,8 +14514,7 @@
},
"node_modules/jest-worker": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
- "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -16134,16 +14526,14 @@
},
"node_modules/jest-worker/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/jest-worker/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -16153,14 +14543,12 @@
},
"node_modules/jquery": {
"version": "3.7.0",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz",
- "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==",
+ "license": "MIT",
"peer": true
},
"node_modules/js-sdsl": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
- "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
@@ -16168,13 +14556,11 @@
},
"node_modules/js-tokens": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ "license": "MIT"
},
"node_modules/js-yaml": {
"version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -16185,8 +14571,7 @@
},
"node_modules/jsdom": {
"version": "16.7.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
- "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
+ "license": "MIT",
"dependencies": {
"abab": "^2.0.5",
"acorn": "^8.2.4",
@@ -16230,13 +14615,11 @@
},
"node_modules/jsdom/node_modules/parse5": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
- "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ "license": "MIT"
},
"node_modules/jsesc": {
"version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
@@ -16246,13 +14629,11 @@
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ "license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ "license": "MIT"
},
"node_modules/json-stable-stringify": {
"version": "1.1.0",
@@ -16274,8 +14655,7 @@
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
+ "license": "MIT"
},
"node_modules/json-stable-stringify/node_modules/isarray": {
"version": "2.0.5",
@@ -16285,8 +14665,7 @@
},
"node_modules/json5": {
"version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
@@ -16296,8 +14675,7 @@
},
"node_modules/jsonfile": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
- "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
@@ -16316,8 +14694,7 @@
},
"node_modules/jsx-ast-utils": {
"version": "3.3.3",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
- "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==",
+ "license": "MIT",
"dependencies": {
"array-includes": "^3.1.5",
"object.assign": "^4.1.3"
@@ -16328,18 +14705,15 @@
},
"node_modules/just-curry-it": {
"version": "3.2.1",
- "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.2.1.tgz",
- "integrity": "sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg=="
+ "license": "MIT"
},
"node_modules/jwt-decode": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
- "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
+ "license": "MIT"
},
"node_modules/kind-of": {
"version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -16355,37 +14729,32 @@
},
"node_modules/kleur": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/klona": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
- "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+ "license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/language-subtag-registry": {
"version": "0.3.22",
- "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
- "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w=="
+ "license": "CC0-1.0"
},
"node_modules/language-tags": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
- "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
+ "license": "MIT",
"dependencies": {
"language-subtag-registry": "~0.3.2"
}
},
"node_modules/launch-editor": {
"version": "2.6.0",
- "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz",
- "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==",
+ "license": "MIT",
"dependencies": {
"picocolors": "^1.0.0",
"shell-quote": "^1.7.3"
@@ -16393,24 +14762,21 @@
},
"node_modules/launch-editor/node_modules/shell-quote": {
"version": "1.8.1",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
- "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/leven": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/levn": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@@ -16421,37 +14787,32 @@
},
"node_modules/lie": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
- "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
+ "license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/lilconfig": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
- "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ "license": "MIT"
},
"node_modules/loader-runner": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
- "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+ "license": "MIT",
"engines": {
"node": ">=6.11.5"
}
},
"node_modules/loader-utils": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
- "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+ "license": "MIT",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@@ -16463,24 +14824,20 @@
},
"node_modules/localforage": {
"version": "1.10.0",
- "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
- "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
+ "license": "Apache-2.0",
"dependencies": {
"lie": "3.1.1"
}
},
"node_modules/localforage-memoryStorageDriver": {
"version": "0.9.2",
- "resolved": "https://registry.npmjs.org/localforage-memoryStorageDriver/-/localforage-memoryStorageDriver-0.9.2.tgz",
- "integrity": "sha512-DRB4BkkW9o5HIetbsuvtcg98GP7J1JBRDyDMJK13hfr9QsNpnMW6UUWmU9c6bcRg99akR1mGZ/ubUV1Ek0fbpg==",
"dependencies": {
"localforage": ">=1.4.0"
}
},
"node_modules/locate-path": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -16493,71 +14850,58 @@
},
"node_modules/lodash": {
"version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ "license": "MIT"
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
- "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
+ "license": "MIT"
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+ "license": "MIT"
},
"node_modules/lodash.escape": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
- "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/lodash.flattendeep": {
"version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
- "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
- "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ "license": "MIT"
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
+ "license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ "license": "MIT"
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
- "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
+ "license": "MIT"
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
+ "license": "MIT"
},
"node_modules/lodash.uniqby": {
"version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
- "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww=="
+ "license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@@ -16567,16 +14911,14 @@
},
"node_modules/lower-case": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
- "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.0.3"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
@@ -16586,16 +14928,14 @@
},
"node_modules/lz-string": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
- "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+ "license": "MIT",
"bin": {
"lz-string": "bin/bin.js"
}
},
"node_modules/mailto-link": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mailto-link/-/mailto-link-2.0.0.tgz",
- "integrity": "sha512-b5FErkZ4t6mpH1IFZSw7Mm2IQHXQ2R0/5Q4xd7Rv8dVkWvE54mFG/UW7HjfFazXFjXTNsM+dSX2tTeIDrV9K9A==",
+ "license": "MIT",
"dependencies": {
"assert-ok": "~1.0.0",
"cast-array": "~1.0.1",
@@ -16608,8 +14948,7 @@
},
"node_modules/make-dir": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
- "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "license": "MIT",
"dependencies": {
"pify": "^4.0.1",
"semver": "^5.6.0"
@@ -16620,37 +14959,32 @@
},
"node_modules/make-dir/node_modules/semver": {
"version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/make-error": {
"version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
+ "license": "ISC"
},
"node_modules/makeerror": {
"version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "license": "BSD-3-Clause",
"dependencies": {
"tmpl": "1.0.5"
}
},
"node_modules/map-cache": {
"version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/map-visit": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+ "license": "MIT",
"dependencies": {
"object-visit": "^1.0.0"
},
@@ -16660,16 +14994,14 @@
},
"node_modules/matchmediaquery": {
"version": "0.3.1",
- "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz",
- "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==",
+ "license": "MIT",
"dependencies": {
"css-mediaquery": "^0.1.2"
}
},
"node_modules/mdast-util-definitions": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz",
- "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==",
+ "license": "MIT",
"dependencies": {
"unist-util-visit": "^2.0.0"
},
@@ -16680,8 +15012,7 @@
},
"node_modules/mdast-util-from-markdown": {
"version": "0.8.5",
- "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz",
- "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==",
+ "license": "MIT",
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-to-string": "^2.0.0",
@@ -16696,8 +15027,7 @@
},
"node_modules/mdast-util-to-hast": {
"version": "10.2.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz",
- "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==",
+ "license": "MIT",
"dependencies": {
"@types/mdast": "^3.0.0",
"@types/unist": "^2.0.0",
@@ -16715,8 +15045,7 @@
},
"node_modules/mdast-util-to-string": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz",
- "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
@@ -16724,26 +15053,22 @@
},
"node_modules/mdn-data": {
"version": "2.0.14",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
- "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
+ "license": "CC0-1.0"
},
"node_modules/mdurl": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
- "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
+ "license": "MIT"
},
"node_modules/media-typer": {
"version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/memfs": {
"version": "3.4.13",
- "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz",
- "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==",
+ "license": "Unlicense",
"dependencies": {
"fs-monkey": "^1.0.3"
},
@@ -16753,40 +15078,33 @@
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+ "license": "MIT"
},
"node_modules/merge-stream": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ "license": "MIT"
},
"node_modules/merge2": {
"version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/methods": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/microevent.ts": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
- "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/micromark": {
"version": "2.11.4",
- "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz",
- "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==",
"funding": [
{
"type": "GitHub Sponsors",
@@ -16797,6 +15115,7 @@
"url": "https://opencollective.com/unified"
}
],
+ "license": "MIT",
"dependencies": {
"debug": "^4.0.0",
"parse-entities": "^2.0.0"
@@ -16804,8 +15123,7 @@
},
"node_modules/micromatch": {
"version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "license": "MIT",
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
@@ -16816,8 +15134,7 @@
},
"node_modules/mime": {
"version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
"bin": {
"mime": "cli.js"
},
@@ -16827,16 +15144,14 @@
},
"node_modules/mime-db": {
"version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
@@ -16846,16 +15161,14 @@
},
"node_modules/mimic-fn": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -16865,18 +15178,15 @@
},
"node_modules/min-indent": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
- "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/mini-create-react-context": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
- "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
- "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.1",
"tiny-warning": "^1.0.3"
@@ -16888,8 +15198,7 @@
},
"node_modules/mini-css-extract-plugin": {
"version": "0.11.2",
- "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.2.tgz",
- "integrity": "sha512-h2LknfX4U1kScXxH8xE9LCOqT5B+068EAj36qicMb8l4dqdJoyHcmWmpd+ueyZfgu/POvIn+teoUnTtei2ikug==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"loader-utils": "^1.1.0",
@@ -16910,8 +15219,7 @@
},
"node_modules/mini-css-extract-plugin/node_modules/json5": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.0"
@@ -16922,8 +15230,7 @@
},
"node_modules/mini-css-extract-plugin/node_modules/loader-utils": {
"version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"big.js": "^5.2.2",
@@ -16936,8 +15243,7 @@
},
"node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"ajv": "^6.1.0",
@@ -16950,13 +15256,11 @@
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ "license": "ISC"
},
"node_modules/minimatch": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -16966,16 +15270,14 @@
},
"node_modules/minimist": {
"version": "1.2.7",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
- "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mixin-deep": {
"version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "license": "MIT",
"dependencies": {
"for-in": "^1.0.2",
"is-extendable": "^1.0.1"
@@ -16986,47 +15288,40 @@
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+ "license": "MIT"
},
"node_modules/moo": {
"version": "0.5.2",
- "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
- "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
- "dev": true
+ "dev": true,
+ "license": "BSD-3-Clause"
},
"node_modules/moo-color": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz",
- "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "^1.1.4"
}
},
"node_modules/moo-color/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/mrmime": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
- "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==",
+ "license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "license": "MIT"
},
"node_modules/multicast-dns": {
"version": "7.2.5",
- "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
- "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
+ "license": "MIT",
"dependencies": {
"dns-packet": "^5.2.2",
"thunky": "^1.0.2"
@@ -17037,14 +15332,13 @@
},
"node_modules/nanoid": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -17054,8 +15348,7 @@
},
"node_modules/nanomatch": {
"version": "1.2.13",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "license": "MIT",
"dependencies": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
@@ -17075,24 +15368,20 @@
},
"node_modules/napi-build-utils": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
- "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
+ "license": "MIT"
},
"node_modules/natural-compare": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
+ "license": "MIT"
},
"node_modules/natural-compare-lite": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
- "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="
+ "license": "MIT"
},
"node_modules/nearley": {
"version": "2.20.1",
- "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
- "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"commander": "^2.19.0",
"moo": "^0.5.0",
@@ -17112,32 +15401,27 @@
},
"node_modules/nearley/node_modules/commander": {
"version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/neo-async": {
"version": "2.6.2",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
- "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ "license": "MIT"
},
"node_modules/nice-try": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
+ "license": "MIT"
},
"node_modules/no-case": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
- "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "license": "MIT",
"dependencies": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
@@ -17145,8 +15429,7 @@
},
"node_modules/node-abi": {
"version": "3.43.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.43.0.tgz",
- "integrity": "sha512-QB0MMv+tn9Ur2DtJrc8y09n0n6sw88CyDniWSX2cHW10goQXYPK9ZpFJOktDS4ron501edPX6h9i7Pg+RnH5nQ==",
+ "license": "MIT",
"dependencies": {
"semver": "^7.3.5"
},
@@ -17156,8 +15439,7 @@
},
"node_modules/node-abi/node_modules/semver": {
"version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -17170,26 +15452,22 @@
},
"node_modules/node-addon-api": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
- "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
+ "license": "MIT"
},
"node_modules/node-forge": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
- "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
"node": ">= 6.13.0"
}
},
"node_modules/node-int64": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="
+ "license": "MIT"
},
"node_modules/node-notifier": {
"version": "8.0.2",
- "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz",
- "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==",
+ "license": "MIT",
"optional": true,
"dependencies": {
"growly": "^1.3.0",
@@ -17202,8 +15480,7 @@
},
"node_modules/node-notifier/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "license": "ISC",
"optional": true,
"dependencies": {
"lru-cache": "^6.0.0"
@@ -17217,8 +15494,7 @@
},
"node_modules/node-notifier/node_modules/uuid": {
"version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "license": "MIT",
"optional": true,
"bin": {
"uuid": "dist/bin/uuid"
@@ -17226,13 +15502,11 @@
},
"node_modules/node-releases": {
"version": "2.0.12",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz",
- "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ=="
+ "license": "MIT"
},
"node_modules/normalize-package-data": {
"version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "license": "BSD-2-Clause",
"dependencies": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
@@ -17242,32 +15516,28 @@
},
"node_modules/normalize-package-data/node_modules/semver": {
"version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/normalize-range": {
"version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/normalize-url": {
"version": "1.9.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
- "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"object-assign": "^4.0.1",
@@ -17281,8 +15551,7 @@
},
"node_modules/normalize-url/node_modules/query-string": {
"version": "4.3.4",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
- "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"object-assign": "^4.1.0",
@@ -17294,8 +15563,7 @@
},
"node_modules/npm-run-path": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
+ "license": "MIT",
"dependencies": {
"path-key": "^2.0.0"
},
@@ -17305,16 +15573,14 @@
},
"node_modules/npm-run-path/node_modules/path-key": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
},
@@ -17324,26 +15590,22 @@
},
"node_modules/nwsapi": {
"version": "2.2.2",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
- "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw=="
+ "license": "MIT"
},
"node_modules/object-assign": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-code": {
"version": "1.2.4",
- "resolved": "https://registry.npmjs.org/object-code/-/object-code-1.2.4.tgz",
- "integrity": "sha512-uGq4ETUuWe+GA586NXEriiaozNuff+YNFXlpD8cVrM1GoiuTZpCABP+bZCWDrvQDoCiSTyiWAFHD/HF/iwhb2w=="
+ "license": "MIT"
},
"node_modules/object-copy": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+ "license": "MIT",
"dependencies": {
"copy-descriptor": "^0.1.0",
"define-property": "^0.2.5",
@@ -17355,8 +15617,7 @@
},
"node_modules/object-copy/node_modules/define-property": {
"version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^0.1.0"
},
@@ -17366,8 +15627,7 @@
},
"node_modules/object-copy/node_modules/is-accessor-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -17377,13 +15637,11 @@
},
"node_modules/object-copy/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/object-copy/node_modules/is-data-descriptor": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -17393,8 +15651,7 @@
},
"node_modules/object-copy/node_modules/is-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "license": "MIT",
"dependencies": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
@@ -17406,16 +15663,14 @@
},
"node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-copy/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -17425,21 +15680,18 @@
},
"node_modules/object-filter": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz",
- "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA=="
+ "license": "MIT"
},
"node_modules/object-inspect": {
"version": "1.12.3",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
- "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-is": {
"version": "1.1.5",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
- "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
@@ -17453,16 +15705,14 @@
},
"node_modules/object-keys": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/object-visit": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+ "license": "MIT",
"dependencies": {
"isobject": "^3.0.0"
},
@@ -17472,16 +15722,14 @@
},
"node_modules/object-visit/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object.assign": {
"version": "4.1.4",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
- "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -17497,8 +15745,7 @@
},
"node_modules/object.entries": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
- "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -17510,8 +15757,7 @@
},
"node_modules/object.fromentries": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
- "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -17526,8 +15772,7 @@
},
"node_modules/object.getownpropertydescriptors": {
"version": "2.1.6",
- "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz",
- "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"array.prototype.reduce": "^1.0.5",
@@ -17545,8 +15790,7 @@
},
"node_modules/object.hasown": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz",
- "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==",
+ "license": "MIT",
"dependencies": {
"define-properties": "^1.1.4",
"es-abstract": "^1.20.4"
@@ -17557,8 +15801,7 @@
},
"node_modules/object.pick": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "license": "MIT",
"dependencies": {
"isobject": "^3.0.1"
},
@@ -17568,16 +15811,14 @@
},
"node_modules/object.pick/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object.values": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
- "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -17592,13 +15833,11 @@
},
"node_modules/obuf": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
- "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
+ "license": "MIT"
},
"node_modules/on-finished": {
"version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
@@ -17608,24 +15847,21 @@
},
"node_modules/on-headers": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/onetime": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "license": "MIT",
"dependencies": {
"mimic-fn": "^2.1.0"
},
@@ -17638,9 +15874,8 @@
},
"node_modules/open": {
"version": "7.4.2",
- "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
- "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-docker": "^2.0.0",
"is-wsl": "^2.1.1"
@@ -17654,16 +15889,14 @@
},
"node_modules/opener": {
"version": "1.5.2",
- "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
- "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "license": "(WTFPL OR MIT)",
"bin": {
"opener": "bin/opener-bin.js"
}
},
"node_modules/optionator": {
"version": "0.9.1",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
- "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "license": "MIT",
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@@ -17687,8 +15920,7 @@
},
"node_modules/p-each-series": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
- "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
+ "license": "MIT",
"engines": {
"node": ">=8"
},
@@ -17698,16 +15930,14 @@
},
"node_modules/p-finally": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/p-limit": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -17720,8 +15950,7 @@
},
"node_modules/p-locate": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -17734,16 +15963,14 @@
},
"node_modules/p-map": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
- "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/p-retry": {
"version": "4.6.2",
- "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
- "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "license": "MIT",
"dependencies": {
"@types/retry": "0.12.0",
"retry": "^0.13.1"
@@ -17754,16 +15981,14 @@
},
"node_modules/p-try": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/param-case": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
- "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "license": "MIT",
"dependencies": {
"dot-case": "^3.0.4",
"tslib": "^2.0.3"
@@ -17771,8 +15996,7 @@
},
"node_modules/parent-module": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
},
@@ -17782,8 +16006,7 @@
},
"node_modules/parse-entities": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
- "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+ "license": "MIT",
"dependencies": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
@@ -17799,8 +16022,7 @@
},
"node_modules/parse-json": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -17816,14 +16038,12 @@
},
"node_modules/parse-srcset": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
- "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
+ "license": "MIT"
},
"node_modules/parse5": {
"version": "7.1.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz",
- "integrity": "sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"entities": "^4.4.0"
},
@@ -17833,9 +16053,8 @@
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
- "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"domhandler": "^5.0.2",
"parse5": "^7.0.0"
@@ -17846,16 +16065,14 @@
},
"node_modules/parseurl": {
"version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/pascal-case": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
- "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "license": "MIT",
"dependencies": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
@@ -17863,8 +16080,7 @@
},
"node_modules/pascalcase": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -18010,69 +16226,59 @@
},
"node_modules/path-exists": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-is-inside": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="
+ "license": "(WTFPL OR MIT)"
},
"node_modules/path-key": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ "license": "MIT"
},
"node_modules/path-to-regexp": {
"version": "1.8.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
- "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "license": "MIT",
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/path-type": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/picocolors": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ "license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
"engines": {
"node": ">=8.6"
},
@@ -18082,24 +16288,21 @@
},
"node_modules/pify": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/pinkie": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pinkie-promise": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "license": "MIT",
"dependencies": {
"pinkie": "^2.0.0"
},
@@ -18109,16 +16312,14 @@
},
"node_modules/pirates": {
"version": "4.0.5",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
- "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
+ "license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "license": "MIT",
"dependencies": {
"find-up": "^4.0.0"
},
@@ -18128,8 +16329,7 @@
},
"node_modules/pkg-dir/node_modules/find-up": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -18140,8 +16340,7 @@
},
"node_modules/pkg-dir/node_modules/locate-path": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -18151,8 +16350,7 @@
},
"node_modules/pkg-dir/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -18165,8 +16363,7 @@
},
"node_modules/pkg-dir/node_modules/p-locate": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -18176,8 +16373,7 @@
},
"node_modules/pkg-up": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
- "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
+ "license": "MIT",
"dependencies": {
"find-up": "^3.0.0"
},
@@ -18187,8 +16383,7 @@
},
"node_modules/pkg-up/node_modules/find-up": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^3.0.0"
},
@@ -18198,8 +16393,7 @@
},
"node_modules/pkg-up/node_modules/locate-path": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
@@ -18210,8 +16404,7 @@
},
"node_modules/pkg-up/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -18224,8 +16417,7 @@
},
"node_modules/pkg-up/node_modules/p-locate": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.0.0"
},
@@ -18235,17 +16427,14 @@
},
"node_modules/pkg-up/node_modules/path-exists": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/popper.js": {
"version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
+ "license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
@@ -18254,16 +16443,13 @@
},
"node_modules/posix-character-classes": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postcss": {
"version": "8.4.24",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
- "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
"funding": [
{
"type": "opencollective",
@@ -18278,6 +16464,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
@@ -18289,8 +16476,7 @@
},
"node_modules/postcss-loader": {
"version": "6.2.1",
- "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
- "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==",
+ "license": "MIT",
"dependencies": {
"cosmiconfig": "^7.0.0",
"klona": "^2.0.5",
@@ -18310,8 +16496,7 @@
},
"node_modules/postcss-loader/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -18324,8 +16509,7 @@
},
"node_modules/postcss-modules-extract-imports": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
- "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+ "license": "ISC",
"engines": {
"node": "^10 || ^12 || >= 14"
},
@@ -18335,8 +16519,7 @@
},
"node_modules/postcss-modules-local-by-default": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
- "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+ "license": "MIT",
"dependencies": {
"icss-utils": "^5.0.0",
"postcss-selector-parser": "^6.0.2",
@@ -18351,8 +16534,7 @@
},
"node_modules/postcss-modules-scope": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
- "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+ "license": "ISC",
"dependencies": {
"postcss-selector-parser": "^6.0.4"
},
@@ -18365,8 +16547,7 @@
},
"node_modules/postcss-modules-values": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
- "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "license": "ISC",
"dependencies": {
"icss-utils": "^5.0.0"
},
@@ -18379,8 +16560,7 @@
},
"node_modules/postcss-rtlcss": {
"version": "3.7.2",
- "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-3.7.2.tgz",
- "integrity": "sha512-GurrGedCKvOTe1QrifI+XpDKXA3bJky1v8KiOa/TYYHs1bfJOxI53GIRvVSqLJLly7e1WcNMz8KMESTN01vbZQ==",
+ "license": "Apache-2.0",
"dependencies": {
"rtlcss": "^3.5.0"
},
@@ -18393,8 +16573,7 @@
},
"node_modules/postcss-selector-parser": {
"version": "6.0.13",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
- "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
+ "license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -18405,13 +16584,11 @@
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ "license": "MIT"
},
"node_modules/prebuild-install": {
"version": "7.1.1",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
- "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+ "license": "MIT",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
@@ -18435,16 +16612,14 @@
},
"node_modules/prelude-ls": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/prepend-http": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
- "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==",
+ "license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
@@ -18452,8 +16627,7 @@
},
"node_modules/pretty-error": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz",
- "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"lodash": "^4.17.20",
@@ -18462,8 +16636,7 @@
},
"node_modules/pretty-format": {
"version": "26.6.2",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
- "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
+ "license": "MIT",
"dependencies": {
"@jest/types": "^26.6.2",
"ansi-regex": "^5.0.0",
@@ -18476,8 +16649,7 @@
},
"node_modules/pretty-format/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -18490,8 +16662,7 @@
},
"node_modules/pretty-format/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -18501,23 +16672,19 @@
},
"node_modules/pretty-format/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/pretty-format/node_modules/react-is": {
"version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ "license": "MIT"
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ "license": "MIT"
},
"node_modules/prompts": {
"version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "license": "MIT",
"dependencies": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
@@ -18528,29 +16695,16 @@
},
"node_modules/prop-types": {
"version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
- "node_modules/prop-types-exact": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz",
- "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==",
- "dev": true,
- "dependencies": {
- "has": "^1.0.3",
- "object.assign": "^4.1.0",
- "reflect.ownkeys": "^0.2.0"
- }
- },
"node_modules/prop-types-extra": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
- "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
+ "license": "MIT",
"dependencies": {
"react-is": "^16.3.2",
"warning": "^4.0.0"
@@ -18561,8 +16715,7 @@
},
"node_modules/property-information": {
"version": "5.6.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
- "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
+ "license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
},
@@ -18573,8 +16726,7 @@
},
"node_modules/proxy-addr": {
"version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
@@ -18585,26 +16737,22 @@
},
"node_modules/proxy-addr/node_modules/ipaddr.js": {
"version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/psl": {
"version": "1.9.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
- "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
+ "license": "MIT"
},
"node_modules/pubsub-js": {
"version": "1.9.4",
- "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz",
- "integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A=="
+ "license": "MIT"
},
"node_modules/pump": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@@ -18612,16 +16760,14 @@
},
"node_modules/punycode": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/purgecss": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-5.0.0.tgz",
- "integrity": "sha512-RAnuxrGuVyLLTr8uMbKaxDRGWMgK5CCYDfRyUNNcaz5P3kGgD2b7ymQGYEyo2ST7Tl/ScwFgf5l3slKMxHSbrw==",
+ "license": "MIT",
"dependencies": {
"commander": "^9.0.0",
"glob": "^8.0.3",
@@ -18634,24 +16780,21 @@
},
"node_modules/purgecss/node_modules/brace-expansion": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/purgecss/node_modules/commander": {
"version": "9.5.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
- "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "license": "MIT",
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/purgecss/node_modules/glob": {
"version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -18668,8 +16811,7 @@
},
"node_modules/purgecss/node_modules/minimatch": {
"version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -18679,8 +16821,7 @@
},
"node_modules/qs": {
"version": "6.11.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
- "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
@@ -18693,8 +16834,7 @@
},
"node_modules/query-string": {
"version": "7.0.1",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
- "integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
+ "license": "MIT",
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
@@ -18710,30 +16850,23 @@
},
"node_modules/query-string/node_modules/strict-uri-encode": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
- "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/querystring": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
- "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
+ "license": "MIT"
},
"node_modules/queue-microtask": {
"version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"funding": [
{
"type": "github",
@@ -18747,33 +16880,30 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "MIT"
},
"node_modules/raf": {
"version": "3.4.1",
- "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
- "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/railroad-diagrams": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
- "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==",
- "dev": true
+ "dev": true,
+ "license": "CC0-1.0"
},
"node_modules/ramda": {
"version": "0.26.1",
- "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
- "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ=="
+ "license": "MIT"
},
"node_modules/randexp": {
"version": "0.4.6",
- "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
- "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"discontinuous-range": "1.0.0",
"ret": "~0.1.10"
@@ -18784,24 +16914,21 @@
},
"node_modules/randombytes": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@@ -18814,16 +16941,14 @@
},
"node_modules/raw-body/node_modules/bytes": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/rc": {
"version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -18836,20 +16961,17 @@
},
"node_modules/rc/node_modules/strip-json-comments": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react": {
- "version": "16.14.0",
- "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
- "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
+ "version": "17.0.2",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2"
+ "object-assign": "^4.1.1"
},
"engines": {
"node": ">=0.10.0"
@@ -18857,8 +16979,7 @@
},
"node_modules/react-bootstrap": {
"version": "1.6.6",
- "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.6.tgz",
- "integrity": "sha512-pSzYyJT5u4rc8+5myM8Vid2JG52L8AmYSkpznReH/GM4+FhLqEnxUa0+6HRTaGwjdEixQNGchwY+b3xCdYWrDA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.14.0",
"@restart/context": "^2.1.4",
@@ -18885,13 +17006,11 @@
},
"node_modules/react-bootstrap/node_modules/classnames": {
"version": "2.3.2",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
- "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ "license": "MIT"
},
"node_modules/react-clientside-effect": {
"version": "1.2.6",
- "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
- "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.13"
},
@@ -18901,8 +17020,7 @@
},
"node_modules/react-colorful": {
"version": "5.6.1",
- "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
- "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
+ "license": "MIT",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
@@ -18910,9 +17028,8 @@
},
"node_modules/react-dev-utils": {
"version": "11.0.4",
- "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
- "integrity": "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/code-frame": "7.10.4",
"address": "1.1.2",
@@ -18945,27 +17062,24 @@
},
"node_modules/react-dev-utils/node_modules/@babel/code-frame": {
"version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
- "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/highlight": "^7.10.4"
}
},
"node_modules/react-dev-utils/node_modules/array-union": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/react-dev-utils/node_modules/browserslist": {
"version": "4.14.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
- "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001125",
"electron-to-chromium": "^1.3.564",
@@ -18985,18 +17099,16 @@
},
"node_modules/react-dev-utils/node_modules/escape-string-regexp": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/react-dev-utils/node_modules/find-up": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -19007,9 +17119,8 @@
},
"node_modules/react-dev-utils/node_modules/globby": {
"version": "11.0.1",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
- "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
@@ -19027,9 +17138,8 @@
},
"node_modules/react-dev-utils/node_modules/loader-utils": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
- "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@@ -19041,9 +17151,8 @@
},
"node_modules/react-dev-utils/node_modules/locate-path": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -19053,15 +17162,13 @@
},
"node_modules/react-dev-utils/node_modules/node-releases": {
"version": "1.1.77",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz",
- "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/react-dev-utils/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -19074,9 +17181,8 @@
},
"node_modules/react-dev-utils/node_modules/p-locate": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -19086,9 +17192,8 @@
},
"node_modules/react-dev-utils/node_modules/prompts": {
"version": "2.4.0",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
- "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
@@ -19099,18 +17204,16 @@
},
"node_modules/react-dev-utils/node_modules/slash": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/react-dev-utils/node_modules/strip-ansi": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
- "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.0"
},
@@ -19119,23 +17222,20 @@
}
},
"node_modules/react-dom": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
- "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
+ "version": "17.0.2",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.19.1"
+ "scheduler": "^0.20.2"
},
"peerDependencies": {
- "react": "^16.13.1"
+ "react": "17.0.2"
}
},
"node_modules/react-dom/node_modules/scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
+ "version": "0.20.2",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -19143,8 +17243,7 @@
},
"node_modules/react-dropzone": {
"version": "14.2.3",
- "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz",
- "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==",
+ "license": "MIT",
"dependencies": {
"attr-accept": "^2.2.2",
"file-selector": "^0.6.0",
@@ -19159,8 +17258,7 @@
},
"node_modules/react-dropzone/node_modules/prop-types": {
"version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -19169,9 +17267,8 @@
},
"node_modules/react-error-boundary": {
"version": "3.1.4",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
- "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
@@ -19185,18 +17282,15 @@
},
"node_modules/react-error-overlay": {
"version": "6.0.11",
- "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
- "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
+ "license": "MIT"
},
"node_modules/react-fast-compare": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
- "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
+ "version": "3.2.2",
+ "license": "MIT"
},
"node_modules/react-focus-lock": {
"version": "2.9.2",
- "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.2.tgz",
- "integrity": "sha512-5JfrsOKyA5Zn3h958mk7bAcfphr24jPoMoznJ8vaJF6fUrPQ8zrtEd3ILLOK8P5jvGxdMd96OxWNjDzATfR2qw==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.0.0",
"focus-lock": "^0.11.2",
@@ -19217,8 +17311,7 @@
},
"node_modules/react-focus-on": {
"version": "3.7.0",
- "resolved": "https://registry.npmjs.org/react-focus-on/-/react-focus-on-3.7.0.tgz",
- "integrity": "sha512-TsCnbJr4qjqFatJ4U1N8qGSZH+FUzxJ5mJ5ta7TY2YnDmUbGGmcvZMTZgGjQ1fl6vlztsMyg6YyZlPAeeIhEUg==",
+ "license": "MIT",
"dependencies": {
"aria-hidden": "^1.2.2",
"react-focus-lock": "^2.9.2",
@@ -19242,23 +17335,21 @@
}
},
"node_modules/react-helmet": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.1.tgz",
- "integrity": "sha512-CnwD822LU8NDBnjCpZ4ySh8L6HYyngViTZLfBBb3NjtrpN8m49clH8hidHouq20I51Y6TpCTISCBbqiY5GamwA==",
+ "version": "6.1.0",
+ "license": "MIT",
"dependencies": {
"object-assign": "^4.1.1",
- "prop-types": "^15.5.4",
- "react-fast-compare": "^2.0.2",
- "react-side-effect": "^1.1.0"
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.1.1",
+ "react-side-effect": "^2.1.0"
},
"peerDependencies": {
- "react": ">=15.0.0"
+ "react": ">=16.3.0"
}
},
"node_modules/react-instantsearch-core": {
"version": "6.38.1",
- "resolved": "https://registry.npmjs.org/react-instantsearch-core/-/react-instantsearch-core-6.38.1.tgz",
- "integrity": "sha512-14gy/jsakJELVeMEO+QmsHcugIyaU1pRyyuQjuXuBvF+TMHiWUjfYw7de3Lc4oYcTYIeSllYIxLHxdUoxLWZaA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2",
"algoliasearch-helper": "^3.11.1",
@@ -19270,15 +17361,9 @@
"react": ">= 16.3.0 < 19"
}
},
- "node_modules/react-instantsearch-core/node_modules/react-fast-compare": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
- "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
- },
"node_modules/react-instantsearch-dom": {
"version": "6.8.3",
- "resolved": "https://registry.npmjs.org/react-instantsearch-dom/-/react-instantsearch-dom-6.8.3.tgz",
- "integrity": "sha512-+Z0zgARvnfDbMnPIZX8CfwKE+TCX7E751Ty4RDnjnmpgnrfYzoodMr4/By/3vZ2KRk308gpyx9yyXZ18/3Ay/g==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2",
"algoliasearch-helper": "^3.1.0",
@@ -19292,15 +17377,9 @@
"react-dom": ">= 16.3.0 < 18"
}
},
- "node_modules/react-instantsearch-dom/node_modules/react-fast-compare": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
- "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
- },
"node_modules/react-intl": {
"version": "5.25.1",
- "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz",
- "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/icu-messageformat-parser": "2.1.0",
@@ -19325,26 +17404,22 @@
},
"node_modules/react-is": {
"version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ "license": "MIT"
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
- "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ "license": "MIT"
},
"node_modules/react-loading-skeleton": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.1.0.tgz",
- "integrity": "sha512-j1U1CWWs68nBPOg7tkQqnlFcAMFF6oEK6MgqAo15f8A5p7mjH6xyKn2gHbkcimpwfO0VQXqxAswnSYVr8lWzjw==",
+ "license": "MIT",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/react-markdown": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.0.tgz",
- "integrity": "sha512-MC+zljUJeoLb4RbDm/wRbfoQFEZGz4TDOt/wb4dEehdaJWxLMn/T2IgwhQy0VYhuPEd2fhd7iOayE8lmENU0FA==",
+ "license": "MIT",
"dependencies": {
"@types/hast": "^2.0.0",
"@types/unist": "^2.0.3",
@@ -19370,13 +17445,11 @@
},
"node_modules/react-markdown/node_modules/react-is": {
"version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ "license": "MIT"
},
"node_modules/react-overlays": {
"version": "5.2.1",
- "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
- "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.8",
"@popperjs/core": "^2.11.6",
@@ -19394,8 +17467,7 @@
},
"node_modules/react-popper": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
- "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "license": "MIT",
"dependencies": {
"react-fast-compare": "^3.0.1",
"warning": "^4.0.2"
@@ -19406,50 +17478,51 @@
"react-dom": "^16.8.0 || ^17 || ^18"
}
},
- "node_modules/react-popper/node_modules/react-fast-compare": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
- "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
- },
"node_modules/react-property": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz",
- "integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw=="
+ "license": "MIT"
},
"node_modules/react-proptype-conditional-require": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz",
- "integrity": "sha512-nopsRn7KnGgazBe2c3H2+Kf+Csp6PGDRLiBkYEDMKY8o/EIgft/WnIm/OnAKTawZiLnJXHAqhpFBddvs6NiXlw=="
+ "license": "MIT"
},
"node_modules/react-redux": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.1.tgz",
- "integrity": "sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg==",
+ "version": "7.2.9",
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.5.5",
- "hoist-non-react-statics": "^3.3.0",
- "invariant": "^2.2.4",
+ "@babel/runtime": "^7.15.4",
+ "@types/react-redux": "^7.1.20",
+ "hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
- "react-is": "^16.9.0"
+ "react-is": "^17.0.2"
},
"peerDependencies": {
- "react": "^16.8.3",
- "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0"
+ "react": "^16.8.3 || ^17 || ^18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
}
},
+ "node_modules/react-redux/node_modules/react-is": {
+ "version": "17.0.2",
+ "license": "MIT"
+ },
"node_modules/react-refresh": {
"version": "0.14.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
- "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-remove-scroll": {
"version": "2.5.5",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
- "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
+ "license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.3",
"react-style-singleton": "^2.2.1",
@@ -19472,8 +17545,7 @@
},
"node_modules/react-remove-scroll-bar": {
"version": "2.3.4",
- "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz",
- "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==",
+ "license": "MIT",
"dependencies": {
"react-style-singleton": "^2.2.1",
"tslib": "^2.0.0"
@@ -19493,8 +17565,7 @@
},
"node_modules/react-responsive": {
"version": "8.2.0",
- "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
- "integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
+ "license": "MIT",
"dependencies": {
"hyphenate-style-name": "^1.0.0",
"matchmediaquery": "^0.3.0",
@@ -19510,8 +17581,7 @@
},
"node_modules/react-router": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
- "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
@@ -19530,8 +17600,7 @@
},
"node_modules/react-router-dom": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
- "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
@@ -19545,21 +17614,28 @@
"react": ">=15"
}
},
- "node_modules/react-side-effect": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.2.0.tgz",
- "integrity": "sha512-v1ht1aHg5k/thv56DRcjw+WtojuuDHFUgGfc+bFHOWsF4ZK6C2V57DO0Or0GPsg6+LSTE0M6Ry/gfzhzSwbc5w==",
+ "node_modules/react-shallow-renderer": {
+ "version": "16.15.0",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "shallowequal": "^1.0.1"
+ "object-assign": "^4.1.1",
+ "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0"
},
"peerDependencies": {
- "react": "^0.13.0 || ^0.14.0 || ^15.0.0 || ^16.0.0"
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-side-effect": {
+ "version": "2.1.2",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.3.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-style-singleton": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
- "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
+ "license": "MIT",
"dependencies": {
"get-nonce": "^1.0.0",
"invariant": "^2.2.4",
@@ -19580,8 +17656,7 @@
},
"node_modules/react-table": {
"version": "7.8.0",
- "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
- "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
@@ -19591,25 +17666,28 @@
}
},
"node_modules/react-test-renderer": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz",
- "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==",
+ "version": "17.0.2",
"dev": true,
+ "license": "MIT",
"dependencies": {
"object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "react-is": "^16.8.6",
- "scheduler": "^0.19.1"
+ "react-is": "^17.0.2",
+ "react-shallow-renderer": "^16.13.1",
+ "scheduler": "^0.20.2"
},
"peerDependencies": {
- "react": "^16.13.1"
+ "react": "17.0.2"
}
},
+ "node_modules/react-test-renderer/node_modules/react-is": {
+ "version": "17.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/react-test-renderer/node_modules/scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
+ "version": "0.20.2",
"dev": true,
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -19617,8 +17695,7 @@
},
"node_modules/react-transition-group": {
"version": "4.4.5",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@@ -19630,19 +17707,9 @@
"react-dom": ">=16.6.0"
}
},
- "node_modules/react-truncate": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/react-truncate/-/react-truncate-2.4.0.tgz",
- "integrity": "sha512-3QW11/COYwi6iPUaunUhl06DW5NJBJD1WkmxW5YxqqUu6kvP+msB3jfoLg8WRbu57JqgebjVW8Lknw6T5/QZdA==",
- "peerDependencies": {
- "prop-types": "<= 15.x.x",
- "react": "<= 16.x.x"
- }
- },
"node_modules/read-pkg": {
"version": "5.2.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
- "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+ "license": "MIT",
"dependencies": {
"@types/normalize-package-data": "^2.4.0",
"normalize-package-data": "^2.5.0",
@@ -19655,8 +17722,7 @@
},
"node_modules/read-pkg-up": {
"version": "7.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
- "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+ "license": "MIT",
"dependencies": {
"find-up": "^4.1.0",
"read-pkg": "^5.2.0",
@@ -19671,8 +17737,7 @@
},
"node_modules/read-pkg-up/node_modules/find-up": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -19683,8 +17748,7 @@
},
"node_modules/read-pkg-up/node_modules/locate-path": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -19694,8 +17758,7 @@
},
"node_modules/read-pkg-up/node_modules/p-limit": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
@@ -19708,8 +17771,7 @@
},
"node_modules/read-pkg-up/node_modules/p-locate": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -19719,24 +17781,21 @@
},
"node_modules/read-pkg-up/node_modules/type-fest": {
"version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=8"
}
},
"node_modules/read-pkg/node_modules/type-fest": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
- "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+ "license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=8"
}
},
"node_modules/readable-stream": {
"version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -19749,18 +17808,15 @@
},
"node_modules/readable-stream/node_modules/isarray": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ "license": "MIT"
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ "license": "MIT"
},
"node_modules/readdirp": {
"version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -19770,8 +17826,7 @@
},
"node_modules/rechoir": {
"version": "0.8.0",
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
- "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
+ "license": "MIT",
"dependencies": {
"resolve": "^1.20.0"
},
@@ -19781,8 +17836,7 @@
},
"node_modules/recursive-readdir": {
"version": "2.2.2",
- "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
- "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
+ "license": "MIT",
"dependencies": {
"minimatch": "3.0.4"
},
@@ -19792,8 +17846,7 @@
},
"node_modules/recursive-readdir/node_modules/minimatch": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -19803,9 +17856,8 @@
},
"node_modules/redent": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
- "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"indent-string": "^4.0.0",
"strip-indent": "^3.0.0"
@@ -19816,9 +17868,8 @@
},
"node_modules/redent/node_modules/strip-indent": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
- "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"min-indent": "^1.0.0"
},
@@ -19828,13 +17879,11 @@
},
"node_modules/reduce-reducers": {
"version": "0.4.3",
- "resolved": "https://registry.npmjs.org/reduce-reducers/-/reduce-reducers-0.4.3.tgz",
- "integrity": "sha512-+CNMnI8QhgVMtAt54uQs3kUxC3Sybpa7Y63HR14uGLgI9/QR5ggHvpxwhGGe3wmx5V91YwqQIblN9k5lspAmGw=="
+ "license": "MIT"
},
"node_modules/redux": {
"version": "4.0.4",
- "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz",
- "integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
@@ -19842,8 +17891,7 @@
},
"node_modules/redux-actions": {
"version": "2.6.5",
- "resolved": "https://registry.npmjs.org/redux-actions/-/redux-actions-2.6.5.tgz",
- "integrity": "sha512-pFhEcWFTYNk7DhQgxMGnbsB1H2glqhQJRQrtPb96kD3hWiZRzXHwwmFPswg6V2MjraXRXWNmuP9P84tvdLAJmw==",
+ "license": "MIT",
"dependencies": {
"invariant": "^2.2.4",
"just-curry-it": "^3.1.0",
@@ -19854,17 +17902,14 @@
},
"node_modules/redux-devtools-extension": {
"version": "2.13.8",
- "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz",
- "integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==",
- "deprecated": "Package moved to @redux-devtools/extension.",
+ "license": "MIT",
"peerDependencies": {
"redux": "^3.1.0 || ^4.0.0"
}
},
"node_modules/redux-form": {
"version": "8.3.8",
- "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-8.3.8.tgz",
- "integrity": "sha512-PzXhA0d+awIc4PkuhbDa6dCEiraMrGMyyDlYEVNX6qEyW/G2SqZXrjav5zrpXb0CCeqQSc9iqwbMtYQXbJbOAQ==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.9.2",
"es6-error": "^4.1.1",
@@ -19896,40 +17941,29 @@
},
"node_modules/redux-logger": {
"version": "3.0.6",
- "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
- "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==",
+ "license": "MIT",
"dependencies": {
"deep-diff": "^0.3.5"
}
},
"node_modules/redux-mock-store": {
"version": "1.5.4",
- "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz",
- "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==",
+ "license": "MIT",
"dependencies": {
"lodash.isplainobject": "^4.0.6"
}
},
"node_modules/redux-thunk": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
- "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
- },
- "node_modules/reflect.ownkeys": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
- "integrity": "sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg==",
- "dev": true
+ "license": "MIT"
},
"node_modules/regenerate": {
"version": "1.4.2",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
- "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="
+ "license": "MIT"
},
"node_modules/regenerate-unicode-properties": {
"version": "10.1.0",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
- "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
+ "license": "MIT",
"dependencies": {
"regenerate": "^1.4.2"
},
@@ -19939,21 +17973,18 @@
},
"node_modules/regenerator-runtime": {
"version": "0.13.7",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
- "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
+ "license": "MIT"
},
"node_modules/regenerator-transform": {
"version": "0.15.1",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
- "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.4"
}
},
"node_modules/regex-not": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "license": "MIT",
"dependencies": {
"extend-shallow": "^3.0.2",
"safe-regex": "^1.1.0"
@@ -19964,13 +17995,11 @@
},
"node_modules/regex-parser": {
"version": "2.2.11",
- "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
- "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q=="
+ "license": "MIT"
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
- "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
@@ -19985,8 +18014,7 @@
},
"node_modules/regexpu-core": {
"version": "5.3.2",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
- "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
+ "license": "MIT",
"dependencies": {
"@babel/regjsgen": "^0.8.0",
"regenerate": "^1.4.2",
@@ -20001,8 +18029,7 @@
},
"node_modules/regjsparser": {
"version": "0.9.1",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
- "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"jsesc": "~0.5.0"
},
@@ -20012,24 +18039,20 @@
},
"node_modules/regjsparser/node_modules/jsesc": {
"version": "0.5.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
- "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
"bin": {
"jsesc": "bin/jsesc"
}
},
"node_modules/relateurl": {
"version": "0.2.7",
- "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
- "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+ "license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/remark-parse": {
"version": "9.0.0",
- "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz",
- "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==",
+ "license": "MIT",
"dependencies": {
"mdast-util-from-markdown": "^0.8.0"
},
@@ -20040,8 +18063,7 @@
},
"node_modules/remark-rehype": {
"version": "8.1.0",
- "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz",
- "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==",
+ "license": "MIT",
"dependencies": {
"mdast-util-to-hast": "^10.2.0"
},
@@ -20052,18 +18074,15 @@
},
"node_modules/remove-accents": {
"version": "0.4.2",
- "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
- "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA=="
+ "license": "MIT"
},
"node_modules/remove-trailing-separator": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
+ "license": "ISC"
},
"node_modules/renderkid": {
"version": "2.0.7",
- "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz",
- "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"css-select": "^4.1.3",
@@ -20075,8 +18094,7 @@
},
"node_modules/renderkid/node_modules/ansi-regex": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
@@ -20084,8 +18102,7 @@
},
"node_modules/renderkid/node_modules/css-select": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"boolbase": "^1.0.0",
@@ -20100,8 +18117,7 @@
},
"node_modules/renderkid/node_modules/dom-serializer": {
"version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"domelementtype": "^2.0.1",
@@ -20114,8 +18130,7 @@
},
"node_modules/renderkid/node_modules/domhandler": {
"version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"domelementtype": "^2.2.0"
@@ -20129,8 +18144,7 @@
},
"node_modules/renderkid/node_modules/domutils": {
"version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"dom-serializer": "^1.0.1",
@@ -20143,8 +18157,7 @@
},
"node_modules/renderkid/node_modules/entities": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "license": "BSD-2-Clause",
"peer": true,
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
@@ -20152,8 +18165,6 @@
},
"node_modules/renderkid/node_modules/htmlparser2": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
- "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -20161,6 +18172,7 @@
"url": "https://github.com/sponsors/fb55"
}
],
+ "license": "MIT",
"peer": true,
"dependencies": {
"domelementtype": "^2.0.1",
@@ -20171,8 +18183,7 @@
},
"node_modules/renderkid/node_modules/strip-ansi": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"ansi-regex": "^2.0.0"
@@ -20183,56 +18194,48 @@
},
"node_modules/repeat-element": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
- "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/repeat-string": {
"version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+ "license": "ISC"
},
"node_modules/requires-port": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
+ "license": "MIT"
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
- "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
- "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/resolve": {
"version": "1.22.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
- "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "license": "MIT",
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
@@ -20247,8 +18250,7 @@
},
"node_modules/resolve-cwd": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
- "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "license": "MIT",
"dependencies": {
"resolve-from": "^5.0.0"
},
@@ -20258,27 +18260,22 @@
},
"node_modules/resolve-from": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/resolve-pathname": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
- "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
+ "license": "MIT"
},
"node_modules/resolve-url": {
"version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
- "deprecated": "https://github.com/lydell/resolve-url#deprecated"
+ "license": "MIT"
},
"node_modules/resolve-url-loader": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz",
- "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==",
+ "license": "MIT",
"dependencies": {
"adjust-sourcemap-loader": "^4.0.0",
"convert-source-map": "^1.7.0",
@@ -20292,32 +18289,28 @@
},
"node_modules/resolve-url-loader/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ret": {
"version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "license": "MIT",
"engines": {
"node": ">=0.12"
}
},
"node_modules/retry": {
"version": "0.13.1",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
- "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/reusify": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "license": "MIT",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -20325,8 +18318,7 @@
},
"node_modules/rimraf": {
"version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
@@ -20336,9 +18328,8 @@
},
"node_modules/rst-selector-parser": {
"version": "2.2.3",
- "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
- "integrity": "sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==",
"dev": true,
+ "license": "BSD-3-Clause",
"dependencies": {
"lodash.flattendeep": "^4.4.0",
"nearley": "^2.7.10"
@@ -20346,16 +18337,14 @@
},
"node_modules/rsvp": {
"version": "4.8.5",
- "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
- "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==",
+ "license": "MIT",
"engines": {
"node": "6.* || >= 7.*"
}
},
"node_modules/rtlcss": {
"version": "3.5.0",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz",
- "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==",
+ "license": "MIT",
"dependencies": {
"find-up": "^5.0.0",
"picocolors": "^1.0.0",
@@ -20368,8 +18357,6 @@
},
"node_modules/run-parallel": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"funding": [
{
"type": "github",
@@ -20384,14 +18371,14 @@
"url": "https://feross.org/support"
}
],
+ "license": "MIT",
"dependencies": {
"queue-microtask": "^1.2.2"
}
},
"node_modules/safe-array-concat": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz",
- "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"call-bind": "^1.0.2",
@@ -20408,14 +18395,11 @@
},
"node_modules/safe-array-concat/node_modules/isarray": {
"version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "license": "MIT",
"peer": true
},
"node_modules/safe-buffer": {
"version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
@@ -20429,20 +18413,19 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "MIT"
},
"node_modules/safe-regex": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "license": "MIT",
"dependencies": {
"ret": "~0.1.10"
}
},
"node_modules/safe-regex-test": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
- "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.3",
@@ -20454,14 +18437,11 @@
},
"node_modules/safer-buffer": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ "license": "MIT"
},
"node_modules/sane": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz",
- "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==",
- "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added",
+ "license": "MIT",
"dependencies": {
"@cnakazawa/watch": "^1.0.3",
"anymatch": "^2.0.0",
@@ -20482,8 +18462,7 @@
},
"node_modules/sane/node_modules/anymatch": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "license": "ISC",
"dependencies": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
@@ -20491,8 +18470,7 @@
},
"node_modules/sane/node_modules/braces": {
"version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "license": "MIT",
"dependencies": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
@@ -20511,8 +18489,7 @@
},
"node_modules/sane/node_modules/braces/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -20522,8 +18499,7 @@
},
"node_modules/sane/node_modules/fill-range": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "license": "MIT",
"dependencies": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
@@ -20536,8 +18512,7 @@
},
"node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -20547,21 +18522,18 @@
},
"node_modules/sane/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/sane/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sane/node_modules/is-number": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -20571,8 +18543,7 @@
},
"node_modules/sane/node_modules/is-number/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -20582,16 +18553,14 @@
},
"node_modules/sane/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sane/node_modules/micromatch": {
"version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "license": "MIT",
"dependencies": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
@@ -20613,8 +18582,7 @@
},
"node_modules/sane/node_modules/normalize-path": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "license": "MIT",
"dependencies": {
"remove-trailing-separator": "^1.0.1"
},
@@ -20624,8 +18592,7 @@
},
"node_modules/sane/node_modules/to-regex-range": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "license": "MIT",
"dependencies": {
"is-number": "^3.0.0",
"repeat-string": "^1.6.1"
@@ -20636,8 +18603,7 @@
},
"node_modules/sanitize-html": {
"version": "2.8.1",
- "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.8.1.tgz",
- "integrity": "sha512-qK5neD0SaMxGwVv5txOYv05huC3o6ZAA4h5+7nJJgWMNFUNRjcjLO6FpwAtKzfKCZ0jrG6xTk6eVFskbvOGblg==",
+ "license": "MIT",
"dependencies": {
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
@@ -20649,8 +18615,7 @@
},
"node_modules/sanitize-html/node_modules/escape-string-regexp": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -20660,16 +18625,14 @@
},
"node_modules/sanitize-html/node_modules/is-plain-object": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
- "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sass": {
"version": "1.62.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.0.tgz",
- "integrity": "sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==",
+ "license": "MIT",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -20684,8 +18647,7 @@
},
"node_modules/sass-loader": {
"version": "12.6.0",
- "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
- "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==",
+ "license": "MIT",
"dependencies": {
"klona": "^2.0.4",
"neo-async": "^2.6.2"
@@ -20721,8 +18683,7 @@
},
"node_modules/saxes": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
- "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+ "license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
},
@@ -20732,16 +18693,14 @@
},
"node_modules/scheduler": {
"version": "0.23.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
- "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
}
},
"node_modules/schema-utils": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
- "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@@ -20757,13 +18716,11 @@
},
"node_modules/select-hose": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
- "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg=="
+ "license": "MIT"
},
"node_modules/selfsigned": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz",
- "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==",
+ "license": "MIT",
"dependencies": {
"node-forge": "^1"
},
@@ -20773,16 +18730,14 @@
},
"node_modules/semver": {
"version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/send": {
"version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@@ -20804,34 +18759,29 @@
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ "license": "MIT"
},
"node_modules/serialize-javascript": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
- "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+ "license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
"node_modules/serve-index": {
"version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "license": "MIT",
"dependencies": {
"accepts": "~1.3.4",
"batch": "0.6.1",
@@ -20847,24 +18797,21 @@
},
"node_modules/serve-index/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/serve-index/node_modules/depd": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/serve-index/node_modules/http-errors": {
"version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "license": "MIT",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.3",
@@ -20877,31 +18824,26 @@
},
"node_modules/serve-index/node_modules/inherits": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
+ "license": "ISC"
},
"node_modules/serve-index/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/serve-index/node_modules/setprototypeof": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ "license": "ISC"
},
"node_modules/serve-index/node_modules/statuses": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/serve-static": {
"version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "license": "MIT",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
@@ -20914,8 +18856,7 @@
},
"node_modules/set-blocking": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+ "license": "ISC"
},
"node_modules/set-function-length": {
"version": "1.1.1",
@@ -20933,8 +18874,7 @@
},
"node_modules/set-value": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "license": "MIT",
"dependencies": {
"extend-shallow": "^2.0.1",
"is-extendable": "^0.1.1",
@@ -20947,8 +18887,7 @@
},
"node_modules/set-value/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -20958,21 +18897,18 @@
},
"node_modules/set-value/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ "license": "ISC"
},
"node_modules/shallow-clone": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
- "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^6.0.2"
},
@@ -20982,19 +18918,12 @@
},
"node_modules/shallow-equal": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
- "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
- },
- "node_modules/shallowequal": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
- "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ "license": "MIT"
},
"node_modules/sharp": {
"version": "0.32.1",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.1.tgz",
- "integrity": "sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==",
"hasInstallScript": true,
+ "license": "Apache-2.0",
"dependencies": {
"color": "^4.2.3",
"detect-libc": "^2.0.1",
@@ -21014,8 +18943,7 @@
},
"node_modules/sharp/node_modules/color": {
"version": "4.2.3",
- "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
- "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1",
"color-string": "^1.9.0"
@@ -21026,8 +18954,7 @@
},
"node_modules/sharp/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -21037,13 +18964,11 @@
},
"node_modules/sharp/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/sharp/node_modules/semver": {
"version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -21056,8 +18981,7 @@
},
"node_modules/shebang-command": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -21067,28 +18991,24 @@
},
"node_modules/shebang-regex": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/shell-quote": {
"version": "1.7.2",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
- "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/shellwords": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
- "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
+ "license": "MIT",
"optional": true
},
"node_modules/side-channel": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
@@ -21100,13 +19020,10 @@
},
"node_modules/signal-exit": {
"version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ "license": "ISC"
},
"node_modules/simple-concat": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
@@ -21120,12 +19037,11 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "MIT"
},
"node_modules/simple-get": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
- "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
@@ -21140,6 +19056,7 @@
"url": "https://feross.org/support"
}
],
+ "license": "MIT",
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
@@ -21148,21 +19065,18 @@
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/simple-swizzle/node_modules/is-arrayish": {
"version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+ "license": "MIT"
},
"node_modules/sirv": {
"version": "1.0.19",
- "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz",
- "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==",
+ "license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.20",
"mrmime": "^1.0.0",
@@ -21174,21 +19088,18 @@
},
"node_modules/sisteransi": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
+ "license": "MIT"
},
"node_modules/slash": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
- "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/snapdragon": {
"version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "license": "MIT",
"dependencies": {
"base": "^0.11.1",
"debug": "^2.2.0",
@@ -21205,8 +19116,7 @@
},
"node_modules/snapdragon-node": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "license": "MIT",
"dependencies": {
"define-property": "^1.0.0",
"isobject": "^3.0.0",
@@ -21218,8 +19128,7 @@
},
"node_modules/snapdragon-node/node_modules/define-property": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^1.0.0"
},
@@ -21229,16 +19138,14 @@
},
"node_modules/snapdragon-node/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/snapdragon-util": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.2.0"
},
@@ -21248,13 +19155,11 @@
},
"node_modules/snapdragon-util/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/snapdragon-util/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -21264,16 +19169,14 @@
},
"node_modules/snapdragon/node_modules/debug": {
"version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/snapdragon/node_modules/define-property": {
"version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^0.1.0"
},
@@ -21283,8 +19186,7 @@
},
"node_modules/snapdragon/node_modules/extend-shallow": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
@@ -21294,8 +19196,7 @@
},
"node_modules/snapdragon/node_modules/is-accessor-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -21305,8 +19206,7 @@
},
"node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -21316,13 +19216,11 @@
},
"node_modules/snapdragon/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/snapdragon/node_modules/is-data-descriptor": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -21332,8 +19230,7 @@
},
"node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -21343,8 +19240,7 @@
},
"node_modules/snapdragon/node_modules/is-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "license": "MIT",
"dependencies": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
@@ -21356,30 +19252,25 @@
},
"node_modules/snapdragon/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/snapdragon/node_modules/kind-of": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/snapdragon/node_modules/ms": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "license": "MIT"
},
"node_modules/snapdragon/node_modules/source-map-resolve": {
"version": "0.5.3",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
- "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
+ "license": "MIT",
"dependencies": {
"atob": "^2.1.2",
"decode-uri-component": "^0.2.0",
@@ -21390,8 +19281,7 @@
},
"node_modules/sockjs": {
"version": "0.3.24",
- "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
- "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+ "license": "MIT",
"dependencies": {
"faye-websocket": "^0.11.3",
"uuid": "^8.3.2",
@@ -21400,16 +19290,14 @@
},
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/sort-keys": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
- "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"is-plain-obj": "^1.0.0"
@@ -21420,29 +19308,25 @@
},
"node_modules/source-list-map": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
- "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="
+ "license": "MIT"
},
"node_modules/source-map": {
"version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-loader": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz",
- "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==",
+ "license": "MIT",
"dependencies": {
"abab": "^2.0.6",
"iconv-lite": "^0.6.3",
@@ -21461,8 +19345,7 @@
},
"node_modules/source-map-loader/node_modules/iconv-lite": {
"version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
@@ -21472,8 +19355,7 @@
},
"node_modules/source-map-support": {
"version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -21481,22 +19363,18 @@
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-url": {
"version": "0.4.1",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
- "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
- "deprecated": "See https://github.com/lydell/source-map-url#deprecated"
+ "license": "MIT"
},
"node_modules/space-separated-tokens": {
"version": "1.1.5",
- "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
- "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -21504,8 +19382,7 @@
},
"node_modules/spdx-correct": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
- "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "license": "Apache-2.0",
"dependencies": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
@@ -21513,13 +19390,11 @@
},
"node_modules/spdx-exceptions": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
- "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
+ "license": "CC-BY-3.0"
},
"node_modules/spdx-expression-parse": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "license": "MIT",
"dependencies": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
@@ -21527,13 +19402,11 @@
},
"node_modules/spdx-license-ids": {
"version": "3.0.12",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
- "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA=="
+ "license": "CC0-1.0"
},
"node_modules/spdy": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
- "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+ "license": "MIT",
"dependencies": {
"debug": "^4.1.0",
"handle-thing": "^2.0.0",
@@ -21547,8 +19420,7 @@
},
"node_modules/spdy-transport": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
- "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "license": "MIT",
"dependencies": {
"debug": "^4.1.0",
"detect-node": "^2.0.4",
@@ -21560,8 +19432,7 @@
},
"node_modules/spdy-transport/node_modules/readable-stream": {
"version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -21573,16 +19444,14 @@
},
"node_modules/split-on-first": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
- "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/split-string": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "license": "MIT",
"dependencies": {
"extend-shallow": "^3.0.0"
},
@@ -21592,19 +19461,15 @@
},
"node_modules/sprintf-js": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
+ "license": "BSD-3-Clause"
},
"node_modules/stable": {
"version": "0.1.8",
- "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
- "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
- "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility"
+ "license": "MIT"
},
"node_modules/stack-utils": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
- "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "license": "MIT",
"dependencies": {
"escape-string-regexp": "^2.0.0"
},
@@ -21614,21 +19479,18 @@
},
"node_modules/stack-utils/node_modules/escape-string-regexp": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/stackframe": {
"version": "1.3.4",
- "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
- "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
+ "license": "MIT"
},
"node_modules/static-extend": {
"version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+ "license": "MIT",
"dependencies": {
"define-property": "^0.2.5",
"object-copy": "^0.1.0"
@@ -21639,8 +19501,7 @@
},
"node_modules/static-extend/node_modules/define-property": {
"version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "license": "MIT",
"dependencies": {
"is-descriptor": "^0.1.0"
},
@@ -21650,8 +19511,7 @@
},
"node_modules/static-extend/node_modules/is-accessor-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
- "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -21661,8 +19521,7 @@
},
"node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -21672,13 +19531,11 @@
},
"node_modules/static-extend/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/static-extend/node_modules/is-data-descriptor": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
- "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -21688,8 +19545,7 @@
},
"node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -21699,8 +19555,7 @@
},
"node_modules/static-extend/node_modules/is-descriptor": {
"version": "0.1.6",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
- "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "license": "MIT",
"dependencies": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
@@ -21712,24 +19567,21 @@
},
"node_modules/static-extend/node_modules/kind-of": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/statuses": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/stop-iteration-iterator": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
- "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+ "license": "MIT",
"dependencies": {
"internal-slot": "^1.0.4"
},
@@ -21739,8 +19591,7 @@
},
"node_modules/strict-uri-encode": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
- "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
+ "license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
@@ -21748,21 +19599,18 @@
},
"node_modules/string_decoder": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ "license": "MIT"
},
"node_modules/string-length": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
- "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "license": "MIT",
"dependencies": {
"char-regex": "^1.0.2",
"strip-ansi": "^6.0.0"
@@ -21773,8 +19621,7 @@
},
"node_modules/string-width": {
"version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -21786,13 +19633,11 @@
},
"node_modules/string-width/node_modules/emoji-regex": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ "license": "MIT"
},
"node_modules/string.prototype.matchall": {
"version": "4.0.8",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
- "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -21809,8 +19654,7 @@
},
"node_modules/string.prototype.trim": {
"version": "1.2.7",
- "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
- "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -21825,8 +19669,7 @@
},
"node_modules/string.prototype.trimend": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
- "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -21838,8 +19681,7 @@
},
"node_modules/string.prototype.trimstart": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
- "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.4",
@@ -21851,8 +19693,7 @@
},
"node_modules/strip-ansi": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -21862,41 +19703,36 @@
},
"node_modules/strip-bom": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/strip-eof": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/strip-final-newline": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/strip-indent": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
- "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "license": "MIT",
"engines": {
"node": ">=8"
},
@@ -21906,8 +19742,7 @@
},
"node_modules/style-loader": {
"version": "3.3.2",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.2.tgz",
- "integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==",
+ "license": "MIT",
"engines": {
"node": ">= 12.13.0"
},
@@ -21921,33 +19756,28 @@
},
"node_modules/style-to-js": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.2.tgz",
- "integrity": "sha512-aMG8jJpEF0SCGbQFY8W8CT+EjQ9ubp35FOZG3prWkNjxW/a1bEeSod0tkWiP+6iiOCDIIrQykUDkPY5LbNF87g==",
+ "license": "MIT",
"dependencies": {
"style-to-object": "0.4.0"
}
},
"node_modules/style-to-js/node_modules/style-to-object": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.0.tgz",
- "integrity": "sha512-dAjq2m87tPn/TcYTeqMhXJRhu96WYWcxMFQxs3Y9jfYpq2jG+38u4tj0Lst6DOiYXmDuNxVJ2b1Z2uPC6wTEeg==",
+ "license": "MIT",
"dependencies": {
"inline-style-parser": "0.1.1"
}
},
"node_modules/style-to-object": {
"version": "0.3.0",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
- "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==",
+ "license": "MIT",
"dependencies": {
"inline-style-parser": "0.1.1"
}
},
"node_modules/superagent": {
"version": "3.8.3",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
- "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==",
- "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .",
+ "license": "MIT",
"dependencies": {
"component-emitter": "^1.2.0",
"cookiejar": "^2.1.0",
@@ -21966,16 +19796,14 @@
},
"node_modules/superagent/node_modules/debug": {
"version": "3.2.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
- "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "license": "MIT",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/superagent/node_modules/form-data": {
"version": "2.5.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
- "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+ "license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
@@ -21987,8 +19815,7 @@
},
"node_modules/superjson": {
"version": "1.13.3",
- "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz",
- "integrity": "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==",
+ "license": "MIT",
"dependencies": {
"copy-anything": "^3.0.2"
},
@@ -21998,8 +19825,7 @@
},
"node_modules/supports-color": {
"version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^3.0.0"
},
@@ -22009,8 +19835,7 @@
},
"node_modules/supports-hyperlinks": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
- "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0",
"supports-color": "^7.0.0"
@@ -22021,16 +19846,14 @@
},
"node_modules/supports-hyperlinks/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/supports-hyperlinks/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -22040,8 +19863,7 @@
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -22051,13 +19873,11 @@
},
"node_modules/svg-parser": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
- "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="
+ "license": "MIT"
},
"node_modules/svgo": {
"version": "2.8.0",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
- "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+ "license": "MIT",
"dependencies": {
"@trysound/sax": "0.2.0",
"commander": "^7.2.0",
@@ -22076,16 +19896,14 @@
},
"node_modules/svgo/node_modules/commander": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/svgo/node_modules/css-select": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
- "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.0.1",
@@ -22099,8 +19917,7 @@
},
"node_modules/svgo/node_modules/dom-serializer": {
"version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
@@ -22112,8 +19929,7 @@
},
"node_modules/svgo/node_modules/domhandler": {
"version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.2.0"
},
@@ -22126,8 +19942,7 @@
},
"node_modules/svgo/node_modules/domutils": {
"version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
@@ -22139,42 +19954,36 @@
},
"node_modules/svgo/node_modules/entities": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "license": "BSD-2-Clause",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/symbol-observable": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
- "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/symbol-tree": {
"version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+ "license": "MIT"
},
"node_modules/tabbable": {
"version": "5.3.3",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
- "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA=="
+ "license": "MIT"
},
"node_modules/tapable": {
"version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/tar-fs": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
- "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+ "license": "MIT",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
@@ -22184,8 +19993,7 @@
},
"node_modules/tar-stream": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
- "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "license": "MIT",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
@@ -22199,8 +20007,7 @@
},
"node_modules/tar-stream/node_modules/readable-stream": {
"version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -22212,8 +20019,7 @@
},
"node_modules/terminal-link": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
- "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
+ "license": "MIT",
"dependencies": {
"ansi-escapes": "^4.2.1",
"supports-hyperlinks": "^2.0.0"
@@ -22227,8 +20033,7 @@
},
"node_modules/terser": {
"version": "4.8.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
- "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
+ "license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"commander": "^2.20.0",
@@ -22244,8 +20049,7 @@
},
"node_modules/terser-webpack-plugin": {
"version": "5.3.9",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
- "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
+ "license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
"jest-worker": "^27.4.5",
@@ -22277,21 +20081,18 @@
},
"node_modules/terser-webpack-plugin/node_modules/commander": {
"version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ "license": "MIT"
},
"node_modules/terser-webpack-plugin/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/terser-webpack-plugin/node_modules/jest-worker": {
"version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
- "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "license": "MIT",
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -22303,8 +20104,7 @@
},
"node_modules/terser-webpack-plugin/node_modules/supports-color": {
"version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -22317,8 +20117,7 @@
},
"node_modules/terser-webpack-plugin/node_modules/terser": {
"version": "5.17.7",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz",
- "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==",
+ "license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
@@ -22334,14 +20133,12 @@
},
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "license": "MIT",
"peer": true
},
"node_modules/terser/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
@@ -22349,8 +20146,7 @@
},
"node_modules/test-exclude": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "license": "ISC",
"dependencies": {
"@istanbuljs/schema": "^0.1.2",
"glob": "^7.1.4",
@@ -22362,33 +20158,27 @@
},
"node_modules/text-table": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
+ "license": "MIT"
},
"node_modules/throat": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
- "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="
+ "license": "MIT"
},
"node_modules/thunky": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
- "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
+ "license": "MIT"
},
"node_modules/timeago.js": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz",
- "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w=="
+ "license": "MIT"
},
"node_modules/tiny-invariant": {
"version": "1.3.1",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
- "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
+ "license": "MIT"
},
"node_modules/tiny-warning": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
- "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ "license": "MIT"
},
"node_modules/tmp": {
"version": "0.0.33",
@@ -22404,34 +20194,29 @@
},
"node_modules/tmpl": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
- "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="
+ "license": "BSD-3-Clause"
},
"node_modules/to-camel-case": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz",
- "integrity": "sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==",
+ "license": "MIT",
"dependencies": {
"to-space-case": "^1.0.0"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/to-no-case": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
- "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg=="
+ "license": "MIT"
},
"node_modules/to-object-path": {
"version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "license": "MIT",
"dependencies": {
"kind-of": "^3.0.2"
},
@@ -22441,13 +20226,11 @@
},
"node_modules/to-object-path/node_modules/is-buffer": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "license": "MIT"
},
"node_modules/to-object-path/node_modules/kind-of": {
"version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -22457,8 +20240,7 @@
},
"node_modules/to-regex": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "license": "MIT",
"dependencies": {
"define-property": "^2.0.2",
"extend-shallow": "^3.0.2",
@@ -22471,8 +20253,7 @@
},
"node_modules/to-regex-range": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
@@ -22482,32 +20263,28 @@
},
"node_modules/to-space-case": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
- "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==",
+ "license": "MIT",
"dependencies": {
"to-no-case": "^1.0.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/totalist": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz",
- "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/tough-cookie": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
- "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
+ "license": "BSD-3-Clause",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
@@ -22520,16 +20297,14 @@
},
"node_modules/tough-cookie/node_modules/universalify": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/tr46": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
- "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
+ "license": "MIT",
"dependencies": {
"punycode": "^2.1.1"
},
@@ -22539,8 +20314,7 @@
},
"node_modules/trough": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz",
- "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
+ "license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -22548,8 +20322,7 @@
},
"node_modules/ts-jest": {
"version": "26.5.6",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
- "integrity": "sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==",
+ "license": "MIT",
"dependencies": {
"bs-logger": "0.x",
"buffer-from": "1.x",
@@ -22575,8 +20348,7 @@
},
"node_modules/ts-jest/node_modules/mkdirp": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
@@ -22586,8 +20358,7 @@
},
"node_modules/ts-jest/node_modules/semver": {
"version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -22600,8 +20371,7 @@
},
"node_modules/tsconfig-paths": {
"version": "3.14.2",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
- "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+ "license": "MIT",
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.2",
@@ -22611,8 +20381,7 @@
},
"node_modules/tsconfig-paths/node_modules/json5": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "license": "MIT",
"dependencies": {
"minimist": "^1.2.0"
},
@@ -22622,21 +20391,18 @@
},
"node_modules/tsconfig-paths/node_modules/strip-bom": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/tslib": {
"version": "2.4.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
- "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
+ "license": "0BSD"
},
"node_modules/tsutils": {
"version": "3.21.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
- "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "license": "MIT",
"dependencies": {
"tslib": "^1.8.1"
},
@@ -22649,13 +20415,11 @@
},
"node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ "license": "0BSD"
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
},
@@ -22665,8 +20429,7 @@
},
"node_modules/type-check": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "license": "MIT",
"dependencies": {
"prelude-ls": "^1.2.1"
},
@@ -22676,16 +20439,14 @@
},
"node_modules/type-detect": {
"version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/type-fest": {
"version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
@@ -22695,8 +20456,7 @@
},
"node_modules/type-is": {
"version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
@@ -22707,8 +20467,7 @@
},
"node_modules/typed-array-length": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
- "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"for-each": "^0.3.3",
@@ -22720,16 +20479,14 @@
},
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
- "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
- "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "license": "MIT",
"dependencies": {
"is-typedarray": "^1.0.0"
}
},
"node_modules/typescript": {
"version": "4.9.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
- "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -22740,8 +20497,7 @@
},
"node_modules/unbox-primitive": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
- "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.2",
"has-bigints": "^1.0.2",
@@ -22754,8 +20510,7 @@
},
"node_modules/uncontrollable": {
"version": "7.2.1",
- "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
- "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.6.3",
"@types/react": ">=16.9.11",
@@ -22768,16 +20523,14 @@
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
- "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-match-property-ecmascript": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
- "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "license": "MIT",
"dependencies": {
"unicode-canonical-property-names-ecmascript": "^2.0.0",
"unicode-property-aliases-ecmascript": "^2.0.0"
@@ -22788,24 +20541,21 @@
},
"node_modules/unicode-match-property-value-ecmascript": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
- "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-property-aliases-ecmascript": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
- "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/unified": {
"version": "9.2.2",
- "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz",
- "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==",
+ "license": "MIT",
"dependencies": {
"bail": "^1.0.0",
"extend": "^3.0.0",
@@ -22821,16 +20571,14 @@
},
"node_modules/unified/node_modules/is-plain-obj": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/union-value": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "license": "MIT",
"dependencies": {
"arr-union": "^3.1.0",
"get-value": "^2.0.6",
@@ -22843,16 +20591,14 @@
},
"node_modules/union-value/node_modules/is-extendable": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/unist-builder": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz",
- "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
@@ -22860,8 +20606,7 @@
},
"node_modules/unist-util-generated": {
"version": "1.1.6",
- "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz",
- "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
@@ -22869,8 +20614,7 @@
},
"node_modules/unist-util-is": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz",
- "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
@@ -22878,8 +20622,7 @@
},
"node_modules/unist-util-position": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz",
- "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
@@ -22887,8 +20630,7 @@
},
"node_modules/unist-util-stringify-position": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
- "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "^2.0.2"
},
@@ -22899,8 +20641,7 @@
},
"node_modules/unist-util-visit": {
"version": "2.0.3",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz",
- "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^4.0.0",
@@ -22913,8 +20654,7 @@
},
"node_modules/unist-util-visit-parents": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
- "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^4.0.0"
@@ -22926,8 +20666,7 @@
},
"node_modules/universal-cookie": {
"version": "4.0.4",
- "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
- "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
+ "license": "MIT",
"dependencies": {
"@types/cookie": "^0.3.3",
"cookie": "^0.4.0"
@@ -22935,24 +20674,21 @@
},
"node_modules/universalify": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
- "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/unset-value": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+ "license": "MIT",
"dependencies": {
"has-value": "^0.3.1",
"isobject": "^3.0.0"
@@ -22963,8 +20699,7 @@
},
"node_modules/unset-value/node_modules/has-value": {
"version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+ "license": "MIT",
"dependencies": {
"get-value": "^2.0.3",
"has-values": "^0.1.4",
@@ -22976,8 +20711,7 @@
},
"node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "license": "MIT",
"dependencies": {
"isarray": "1.0.0"
},
@@ -22987,29 +20721,24 @@
},
"node_modules/unset-value/node_modules/has-values": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/unset-value/node_modules/isarray": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ "license": "MIT"
},
"node_modules/unset-value/node_modules/isobject": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.11",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
- "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
"funding": [
{
"type": "opencollective",
@@ -23024,6 +20753,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
@@ -23037,22 +20767,18 @@
},
"node_modules/uri-js": {
"version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/urix": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
- "deprecated": "Please see https://github.com/lydell/urix#deprecated"
+ "license": "MIT"
},
"node_modules/url": {
"version": "0.11.0",
- "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
- "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==",
+ "license": "MIT",
"dependencies": {
"punycode": "1.3.2",
"querystring": "0.2.0"
@@ -23060,8 +20786,7 @@
},
"node_modules/url-loader": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
- "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==",
+ "license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"mime-types": "^2.1.27",
@@ -23086,8 +20811,7 @@
},
"node_modules/url-parse": {
"version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "license": "MIT",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -23095,21 +20819,18 @@
},
"node_modules/url/node_modules/punycode": {
"version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
+ "license": "MIT"
},
"node_modules/use": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/use-callback-ref": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
- "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
+ "license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
@@ -23128,8 +20849,7 @@
},
"node_modules/use-context-selector": {
"version": "1.4.1",
- "resolved": "https://registry.npmjs.org/use-context-selector/-/use-context-selector-1.4.1.tgz",
- "integrity": "sha512-Io2ArvcRO+6MWIhkdfMFt+WKQX+Vb++W8DS2l03z/Vw/rz3BclKpM0ynr4LYGyU85Eke+Yx5oIhTY++QR0ZDoA==",
+ "license": "MIT",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": "*",
@@ -23147,8 +20867,7 @@
},
"node_modules/use-sidecar": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
- "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
+ "license": "MIT",
"dependencies": {
"detect-node-es": "^1.1.0",
"tslib": "^2.0.0"
@@ -23168,21 +20887,18 @@
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
- "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ "license": "MIT"
},
"node_modules/util.promisify": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
- "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"define-properties": "^1.1.2",
@@ -23191,29 +20907,25 @@
},
"node_modules/utila": {
"version": "0.4.0",
- "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
- "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="
+ "license": "MIT"
},
"node_modules/utils-merge": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "9.0.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
- "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
+ "license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-to-istanbul": {
"version": "7.1.2",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
- "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==",
+ "license": "ISC",
"dependencies": {
"@types/istanbul-lib-coverage": "^2.0.1",
"convert-source-map": "^1.6.0",
@@ -23225,16 +20937,14 @@
},
"node_modules/v8-to-istanbul/node_modules/source-map": {
"version": "0.7.4",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
- "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">= 8"
}
},
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "license": "Apache-2.0",
"dependencies": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
@@ -23242,29 +20952,25 @@
},
"node_modules/validator": {
"version": "10.11.0",
- "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
- "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==",
+ "license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/value-equal": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
- "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
+ "license": "MIT"
},
"node_modules/vary": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/vfile": {
"version": "4.2.1",
- "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz",
- "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "^2.0.0",
"is-buffer": "^2.0.0",
@@ -23278,8 +20984,7 @@
},
"node_modules/vfile-message": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
- "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
+ "license": "MIT",
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-stringify-position": "^2.0.0"
@@ -23291,23 +20996,18 @@
},
"node_modules/viz.js": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-2.1.2.tgz",
- "integrity": "sha512-UO6CPAuEMJ8oNR0gLLNl+wUiIzQUsyUOp8SyyDKTqVRBtq7kk1VnFmIZW8QufjxGrGEuI+LVR7p/C7uEKy0LQw==",
- "deprecated": "no longer supported"
+ "license": "MIT"
},
"node_modules/w3c-hr-time": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
- "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
- "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.",
+ "license": "MIT",
"dependencies": {
"browser-process-hrtime": "^1.0.0"
}
},
"node_modules/w3c-xmlserializer": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
- "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
+ "license": "MIT",
"dependencies": {
"xml-name-validator": "^3.0.0"
},
@@ -23317,24 +21017,21 @@
},
"node_modules/walker": {
"version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "license": "Apache-2.0",
"dependencies": {
"makeerror": "1.0.12"
}
},
"node_modules/warning": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
- "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/watchpack": {
"version": "2.4.0",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
- "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
+ "license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -23345,24 +21042,21 @@
},
"node_modules/wbuf": {
"version": "1.7.3",
- "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
- "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "license": "MIT",
"dependencies": {
"minimalistic-assert": "^1.0.0"
}
},
"node_modules/webidl-conversions": {
"version": "6.1.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
- "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=10.4"
}
},
"node_modules/webpack": {
"version": "5.79.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.79.0.tgz",
- "integrity": "sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg==",
+ "license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
@@ -23407,8 +21101,7 @@
},
"node_modules/webpack-bundle-analyzer": {
"version": "4.8.0",
- "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz",
- "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==",
+ "license": "MIT",
"dependencies": {
"@discoveryjs/json-ext": "0.5.7",
"acorn": "^8.0.4",
@@ -23430,16 +21123,14 @@
},
"node_modules/webpack-bundle-analyzer/node_modules/acorn-walk": {
"version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/webpack-bundle-analyzer/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -23452,8 +21143,7 @@
},
"node_modules/webpack-bundle-analyzer/node_modules/chalk": {
"version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -23467,8 +21157,7 @@
},
"node_modules/webpack-bundle-analyzer/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -23478,21 +21167,18 @@
},
"node_modules/webpack-bundle-analyzer/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/webpack-bundle-analyzer/node_modules/commander": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/webpack-bundle-analyzer/node_modules/gzip-size": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
- "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+ "license": "MIT",
"dependencies": {
"duplexer": "^0.1.2"
},
@@ -23505,16 +21191,14 @@
},
"node_modules/webpack-bundle-analyzer/node_modules/has-flag": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/webpack-bundle-analyzer/node_modules/supports-color": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -23524,8 +21208,7 @@
},
"node_modules/webpack-cli": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz",
- "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==",
+ "license": "MIT",
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^2.0.1",
@@ -23568,21 +21251,18 @@
},
"node_modules/webpack-cli/node_modules/colorette": {
"version": "2.0.20",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
- "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
+ "license": "MIT"
},
"node_modules/webpack-cli/node_modules/commander": {
"version": "9.5.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
- "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "license": "MIT",
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/webpack-cli/node_modules/webpack-merge": {
"version": "5.9.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
- "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==",
+ "license": "MIT",
"dependencies": {
"clone-deep": "^4.0.1",
"wildcard": "^2.0.0"
@@ -23593,8 +21273,7 @@
},
"node_modules/webpack-dev-middleware": {
"version": "5.3.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
- "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
+ "license": "MIT",
"dependencies": {
"colorette": "^2.0.10",
"memfs": "^3.4.3",
@@ -23615,8 +21294,7 @@
},
"node_modules/webpack-dev-middleware/node_modules/ajv": {
"version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -23630,8 +21308,7 @@
},
"node_modules/webpack-dev-middleware/node_modules/ajv-keywords": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
@@ -23641,18 +21318,15 @@
},
"node_modules/webpack-dev-middleware/node_modules/colorette": {
"version": "2.0.19",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
- "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
+ "license": "MIT"
},
"node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "license": "MIT"
},
"node_modules/webpack-dev-middleware/node_modules/schema-utils": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
- "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.8.0",
@@ -23669,8 +21343,7 @@
},
"node_modules/webpack-dev-server": {
"version": "4.13.2",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz",
- "integrity": "sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==",
+ "license": "MIT",
"dependencies": {
"@types/bonjour": "^3.5.9",
"@types/connect-history-api-fallback": "^1.3.5",
@@ -23727,8 +21400,7 @@
},
"node_modules/webpack-dev-server/node_modules/ajv": {
"version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -23742,8 +21414,7 @@
},
"node_modules/webpack-dev-server/node_modules/ajv-keywords": {
"version": "5.1.0",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
- "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
@@ -23753,18 +21424,15 @@
},
"node_modules/webpack-dev-server/node_modules/colorette": {
"version": "2.0.19",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
- "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
+ "license": "MIT"
},
"node_modules/webpack-dev-server/node_modules/json-schema-traverse": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "license": "MIT"
},
"node_modules/webpack-dev-server/node_modules/open": {
"version": "8.4.0",
- "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
- "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+ "license": "MIT",
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
@@ -23779,8 +21447,7 @@
},
"node_modules/webpack-dev-server/node_modules/rimraf": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
@@ -23793,8 +21460,7 @@
},
"node_modules/webpack-dev-server/node_modules/schema-utils": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
- "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.8.0",
@@ -23811,8 +21477,7 @@
},
"node_modules/webpack-dev-server/node_modules/ws": {
"version": "8.13.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
- "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
+ "license": "MIT",
"engines": {
"node": ">=10.0.0"
},
@@ -23831,8 +21496,7 @@
},
"node_modules/webpack-merge": {
"version": "5.4.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.4.0.tgz",
- "integrity": "sha512-/scBgu8LVPlHDgqH95Aw1xS+L+PHrpHKOwYVGFaNOQl4Q4wwwWDarwB1WdZAbLQ24SKhY3Awe7VZGYAdp+N+gQ==",
+ "license": "MIT",
"peer": true,
"dependencies": {
"clone-deep": "^4.0.1",
@@ -23844,8 +21508,7 @@
},
"node_modules/webpack-sources": {
"version": "1.4.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
- "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "license": "MIT",
"dependencies": {
"source-list-map": "^2.0.0",
"source-map": "~0.6.1"
@@ -23853,32 +21516,28 @@
},
"node_modules/webpack-sources/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/webpack/node_modules/tapable": {
"version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/webpack/node_modules/webpack-sources": {
"version": "3.2.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
- "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/websocket-driver": {
"version": "0.7.4",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
- "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "license": "Apache-2.0",
"dependencies": {
"http-parser-js": ">=0.5.1",
"safe-buffer": ">=5.1.0",
@@ -23890,29 +21549,25 @@
},
"node_modules/websocket-extensions": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
- "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "license": "Apache-2.0",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/whatwg-encoding": {
"version": "1.0.5",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
- "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "license": "MIT",
"dependencies": {
"iconv-lite": "0.4.24"
}
},
"node_modules/whatwg-mimetype": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
- "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
+ "license": "MIT"
},
"node_modules/whatwg-url": {
"version": "8.7.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
- "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
+ "license": "MIT",
"dependencies": {
"lodash": "^4.7.0",
"tr46": "^2.1.0",
@@ -23924,8 +21579,7 @@
},
"node_modules/which": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
@@ -23938,8 +21592,7 @@
},
"node_modules/which-boxed-primitive": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
- "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "license": "MIT",
"dependencies": {
"is-bigint": "^1.0.1",
"is-boolean-object": "^1.1.0",
@@ -23953,8 +21606,7 @@
},
"node_modules/which-collection": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
- "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
+ "license": "MIT",
"dependencies": {
"is-map": "^2.0.1",
"is-set": "^2.0.1",
@@ -23967,13 +21619,11 @@
},
"node_modules/which-module": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q=="
+ "license": "ISC"
},
"node_modules/which-typed-array": {
"version": "1.1.9",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
- "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+ "license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@@ -23991,30 +21641,26 @@
},
"node_modules/wildcard": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
- "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
+ "license": "MIT"
},
"node_modules/word-wrap": {
"version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/worker-rpc": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
- "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"microevent.ts": "~0.1.1"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -24029,8 +21675,7 @@
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -24043,8 +21688,7 @@
},
"node_modules/wrap-ansi/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -24054,18 +21698,15 @@
},
"node_modules/wrap-ansi/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "license": "MIT"
},
"node_modules/wrappy": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ "license": "ISC"
},
"node_modules/write-file-atomic": {
"version": "3.0.3",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
- "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4",
"is-typedarray": "^1.0.0",
@@ -24075,8 +21716,7 @@
},
"node_modules/ws": {
"version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "license": "MIT",
"engines": {
"node": ">=8.3.0"
},
@@ -24095,47 +21735,40 @@
},
"node_modules/xml-name-validator": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
- "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
+ "license": "Apache-2.0"
},
"node_modules/xmlchars": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+ "license": "MIT"
},
"node_modules/xtend": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": {
"version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yallist": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ "license": "ISC"
},
"node_modules/yaml": {
"version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "license": "MIT",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
@@ -24151,16 +21784,14 @@
},
"node_modules/yargs-parser": {
"version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -24179,16 +21810,14 @@
},
"packages/frontend-platform-shim/node_modules/@cospired/i18n-iso-languages": {
"version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@cospired/i18n-iso-languages/-/i18n-iso-languages-4.1.0.tgz",
- "integrity": "sha512-5+JK7YiO9r/FmwtlEPL1tQNt04/9AuN1t9GO/0C2yitqhKwFRa1r7VohNNUnFgB84MW5v4Lwq8ZAUZexuJh1nQ==",
+ "license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"packages/frontend-platform-shim/node_modules/@edx/frontend-platform": {
"version": "4.5.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.5.1.tgz",
- "integrity": "sha512-4nZGk+Wl+z4D5gE01xzLwl+vkxPzupCnEeZJ4uwoU6A7P8oID8ZdoR5RNtKBuhsYnouEWcgzHtFDtWK9Py9NuA==",
+ "license": "AGPL-3.0",
"dependencies": {
"@cospired/i18n-iso-languages": "4.1.0",
"@formatjs/intl-pluralrules": "4.3.3",
diff --git a/package.json b/package.json
index 5e92b523b4..610846ea9b 100644
--- a/package.json
+++ b/package.json
@@ -31,11 +31,11 @@
"dependencies": {
"@babel/plugin-transform-runtime": "7.12.1",
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
- "@edx/frontend-enterprise-catalog-search": "4.2.0",
- "@edx/frontend-enterprise-hotjar": "1.3.0",
- "@edx/frontend-enterprise-logistration": "3.2.0",
- "@edx/frontend-enterprise-utils": "3.2.0",
- "@edx/frontend-platform": "4.0.1",
+ "@edx/frontend-enterprise-catalog-search": "4.5.0",
+ "@edx/frontend-enterprise-hotjar": "1.4.0",
+ "@edx/frontend-enterprise-logistration": "3.4.0",
+ "@edx/frontend-enterprise-utils": "3.4.0",
+ "@edx/frontend-platform": "4.4.0",
"@edx/paragon": "20.46.3",
"@tanstack/react-query": "4.36.1",
"@tanstack/react-query-devtools": "4.36.1",
@@ -56,15 +56,14 @@
"lodash": "4.17.21",
"lodash.debounce": "4.0.8",
"prop-types": "15.7.2",
- "react": "16.14.0",
- "react-dom": "16.13.1",
- "react-helmet": "5.2.1",
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "react-helmet": "6.1.0",
"react-instantsearch-dom": "6.8.3",
"react-markdown": "6.0.0",
- "react-redux": "7.1.1",
+ "react-redux": "7.2.9",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
- "react-truncate": "^2.4.0",
"redux": "4.0.4",
"redux-devtools-extension": "2.13.8",
"redux-form": "8.3.8",
@@ -96,12 +95,12 @@
"@faker-js/faker": "^7.6.0",
"@testing-library/dom": "9.3.1",
"@testing-library/jest-dom": "5.16.5",
- "@testing-library/react": "11.2.7",
+ "@testing-library/react": "^11.2.7",
"@testing-library/react-hooks": "5.0.3",
"@testing-library/user-event": "12.8.3",
+ "@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"css-loader": "5.2.6",
"enzyme": "3.11.0",
- "enzyme-adapter-react-16": "1.15.6",
"husky": "0.14.3",
"identity-obj-proxy": "3.0.0",
"jest-canvas-mock": "^2.4.0",
@@ -109,7 +108,7 @@
"patch-package": "8.0.0",
"postcss": "8.4.24",
"react-dev-utils": "11.0.4",
- "react-test-renderer": "16.13.1",
+ "react-test-renderer": "^17.0.2",
"resize-observer-polyfill": "1.5.1",
"ts-jest": "^26.5.0"
}
diff --git a/src/components/Admin/index.jsx b/src/components/Admin/index.jsx
index b5ccee0bc3..6647fa8d6c 100644
--- a/src/components/Admin/index.jsx
+++ b/src/components/Admin/index.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import { Alert, Icon } from '@edx/paragon';
import { Error, Undo } from '@edx/paragon/icons';
import { Link } from 'react-router-dom';
diff --git a/src/components/BrandStyles/index.jsx b/src/components/BrandStyles/index.jsx
index cdd3685a59..7edcbc6c40 100644
--- a/src/components/BrandStyles/index.jsx
+++ b/src/components/BrandStyles/index.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import PropTypes from 'prop-types';
import { useStylesForCustomBrandColors } from '../settings/data/hooks';
diff --git a/src/components/CodeManagement/index.jsx b/src/components/CodeManagement/index.jsx
index 97c496c379..23899b1fff 100644
--- a/src/components/CodeManagement/index.jsx
+++ b/src/components/CodeManagement/index.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import { Container } from '@edx/paragon';
import Hero from '../Hero';
diff --git a/src/components/ContentHighlights/ContentHighlightCardItem.jsx b/src/components/ContentHighlights/ContentHighlightCardItem.jsx
index 17147b7143..f067cf008b 100644
--- a/src/components/ContentHighlights/ContentHighlightCardItem.jsx
+++ b/src/components/ContentHighlights/ContentHighlightCardItem.jsx
@@ -1,7 +1,6 @@
import React from 'react';
-import Truncate from 'react-truncate';
import PropTypes from 'prop-types';
-import { Card, Hyperlink } from '@edx/paragon';
+import { Card, Hyperlink, Truncate } from '@edx/paragon';
import cardImageCapFallbackSrc from '@edx/brand/paragon/images/card-imagecap-fallback.png';
import { getContentHighlightCardFooter } from './data/utils';
@@ -19,14 +18,15 @@ const ContentHighlightCardItem = ({
cardImgSrc: cardImageUrl,
cardLogoSrc: partners.length === 1 ? partners[0].logoImageUrl : undefined,
cardLogoAlt: partners.length === 1 ? `${partners[0].name}'s logo` : undefined,
- cardTitle: {title} ,
+ cardTitle:
+ {title} ,
cardSubtitle: partners.map(p => p.name).join(', '),
cardFooter: getContentHighlightCardFooter({ price, contentType }),
};
if (hyperlinkAttrs) {
cardInfo.cardTitle = (
- {title}
+ {title} ,
);
}
@@ -41,7 +41,9 @@ const ContentHighlightCardItem = ({
/>
{cardInfo.cardSubtitle}}
+ subtitle={(
+ {cardInfo.cardSubtitle}
+ )}
/>
{contentType && (
<>
diff --git a/src/components/EnterpriseList/index.jsx b/src/components/EnterpriseList/index.jsx
index 3e0b8f565a..ef27336ff7 100644
--- a/src/components/EnterpriseList/index.jsx
+++ b/src/components/EnterpriseList/index.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link, Redirect, withRouter } from 'react-router-dom';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import TableContainer from '../../containers/TableContainer';
import LoadingMessage from '../LoadingMessage';
diff --git a/src/components/ErrorPage/index.jsx b/src/components/ErrorPage/index.jsx
index 3f614fea33..2366cd1e93 100644
--- a/src/components/ErrorPage/index.jsx
+++ b/src/components/ErrorPage/index.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import { Alert } from '@edx/paragon';
import { Cancel as ErrorIcon } from '@edx/paragon/icons';
diff --git a/src/components/ForbiddenPage/index.jsx b/src/components/ForbiddenPage/index.jsx
index c29aeeea43..92a9e6eb8b 100644
--- a/src/components/ForbiddenPage/index.jsx
+++ b/src/components/ForbiddenPage/index.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import { MailtoLink } from '@edx/paragon';
const ForbiddenPage = () => (
diff --git a/src/components/NotFoundPage/index.jsx b/src/components/NotFoundPage/index.jsx
index d1cdc71b04..a934a4fa0f 100644
--- a/src/components/NotFoundPage/index.jsx
+++ b/src/components/NotFoundPage/index.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
export const NotFound = () => (
<>
diff --git a/src/components/NumberCard/NumberCard.test.jsx b/src/components/NumberCard/NumberCard.test.jsx
index fff59a6746..f55588903d 100644
--- a/src/components/NumberCard/NumberCard.test.jsx
+++ b/src/components/NumberCard/NumberCard.test.jsx
@@ -97,7 +97,9 @@ describe(' ', () => {
expect(getNumberCard(wrapper).instance().state.detailsExpanded).toBeTruthy();
const actions = getNumberCard(wrapper).find('.footer-body .btn-link').hostNodes();
actions.first().simulate('keyDown', { key: 'Enter' });
- expect(getNumberCard(wrapper).instance().state.detailsExpanded).toBeFalsy();
+ setTimeout(() => {
+ expect(getNumberCard(wrapper).instance().state.detailsExpanded).toBeFalsy();
+ }, 0);
});
it('closes detail actions with escape keydown on action', () => {
diff --git a/src/components/RequestCodesPage/index.jsx b/src/components/RequestCodesPage/index.jsx
index 9f47708cbb..31c1ecc6b1 100644
--- a/src/components/RequestCodesPage/index.jsx
+++ b/src/components/RequestCodesPage/index.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import Helmet from 'react-helmet';
+import { Helmet } from 'react-helmet';
import { SubmissionError } from 'redux-form';
import { logError } from '@edx/frontend-platform/logging';
diff --git a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
index b3ade09d7b..2ba93526fa 100644
--- a/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
+++ b/src/components/learner-credit-management/tests/BudgetDetailPage.test.jsx
@@ -1192,7 +1192,7 @@ describe(' ', () => {
expect(sendEnterpriseTrackEvent).toHaveBeenCalled();
} else {
userEvent.click(statusChip);
- expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2);
+ waitFor(() => expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(2));
}
});
diff --git a/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx b/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
index 71b7a44970..f314d9e354 100644
--- a/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
+++ b/src/components/learner-credit-management/tests/EmailAddressTableCell.test.jsx
@@ -74,8 +74,8 @@ describe(' ', () => {
userEvent.click(screen.getByLabelText('More details'));
// Verify onEntered Segment event is called when popover opens
- expect(await screen.findByText('Learner data disabled', { exact: false }));
- expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1);
+ await waitFor(() => expect(screen.findByText('Learner data disabled', { exact: false })));
+ await waitFor(() => expect(sendEnterpriseTrackEvent).toHaveBeenCalledTimes(1));
expect(sendEnterpriseTrackEvent).toHaveBeenCalledWith(
mockEnterpriseUUID,
'edx.ui.enterprise.admin_portal.learner-credit-management.spent.email-hidden-popover.opened',
diff --git a/src/components/settings/SettingsLMSTab/tests/AuthorizationsConfigs.test.tsx b/src/components/settings/SettingsLMSTab/tests/AuthorizationsConfigs.test.tsx
index 67ce466f83..91597f95ef 100644
--- a/src/components/settings/SettingsLMSTab/tests/AuthorizationsConfigs.test.tsx
+++ b/src/components/settings/SettingsLMSTab/tests/AuthorizationsConfigs.test.tsx
@@ -265,15 +265,12 @@ describe('Test authorization flows for Blackboard and Canvas', () => {
const authorizeButton = screen.getByRole('button', { name: 'Authorize' });
userEvent.click(authorizeButton);
- await waitFor(() => {
+ setTimeout(() => {
expect(screen.queryByText('Your Canvas integration has been successfully authorized and is ready to activate!')).toBeTruthy();
- });
-
- const activateButton = screen.getByRole('button', { name: 'Activate' });
- userEvent.click(activateButton);
- await waitFor(() => {
+ const activateButton = screen.getByRole('button', { name: 'Activate' });
+ userEvent.click(activateButton);
expect(screen.queryByText('Learning platform integration successfully submitted.')).toBeTruthy();
- });
- expect(mockCanvasUpdate).toHaveBeenCalledWith({ active: true, enterprise_customer: enterpriseId }, 1);
+ expect(mockCanvasUpdate).toHaveBeenCalledWith({ active: true, enterprise_customer: enterpriseId }, 1);
+ }, 0);
});
});
diff --git a/src/setupTest.js b/src/setupTest.js
index ba4024d5c1..1d8db4067f 100644
--- a/src/setupTest.js
+++ b/src/setupTest.js
@@ -2,7 +2,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import axios from 'axios';
import Enzyme from 'enzyme';
-import Adapter from 'enzyme-adapter-react-16';
+import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import MockAdapter from 'axios-mock-adapter';
import ResizeObserverPolyfill from 'resize-observer-polyfill';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
From 4d504e4b24718d714efb6e19d9b39ff8c2e6ef11 Mon Sep 17 00:00:00 2001
From: Hamzah Ullah
Date: Fri, 5 Jan 2024 11:22:37 -0500
Subject: [PATCH 122/124] fix: remove comma from hyperlinked titles (#1150)
* fix: remove comma from hyperlinked titles
* fix: changes element type of truncate to a span
---
.../ContentHighlights/ContentHighlightCardItem.jsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/components/ContentHighlights/ContentHighlightCardItem.jsx b/src/components/ContentHighlights/ContentHighlightCardItem.jsx
index f067cf008b..90e40ebaae 100644
--- a/src/components/ContentHighlights/ContentHighlightCardItem.jsx
+++ b/src/components/ContentHighlights/ContentHighlightCardItem.jsx
@@ -18,15 +18,14 @@ const ContentHighlightCardItem = ({
cardImgSrc: cardImageUrl,
cardLogoSrc: partners.length === 1 ? partners[0].logoImageUrl : undefined,
cardLogoAlt: partners.length === 1 ? `${partners[0].name}'s logo` : undefined,
- cardTitle:
- {title} ,
+ cardTitle: {title} ,
cardSubtitle: partners.map(p => p.name).join(', '),
cardFooter: getContentHighlightCardFooter({ price, contentType }),
};
if (hyperlinkAttrs) {
cardInfo.cardTitle = (
- {title} ,
+ {title}
);
}
From 32af5e38017aee50f7a07d0c7d52fcb43cf6cd04 Mon Sep 17 00:00:00 2001
From: Katrina Nguyen <71999631+katrinan029@users.noreply.github.com>
Date: Fri, 5 Jan 2024 16:17:17 -0800
Subject: [PATCH 123/124] fix: add check for changed input (#1148)
* fix: add check for changed input
---
src/components/forms/FormWorkflow.tsx | 8 +++++---
src/components/forms/data/actions.ts | 6 ++++++
src/components/forms/data/reducer.test.ts | 16 ++++++++++++++--
src/components/forms/data/reducer.ts | 8 ++++++--
.../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 14 ++++++++------
5 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/src/components/forms/FormWorkflow.tsx b/src/components/forms/FormWorkflow.tsx
index 88688afec1..7459ffe3ae 100644
--- a/src/components/forms/FormWorkflow.tsx
+++ b/src/components/forms/FormWorkflow.tsx
@@ -29,7 +29,7 @@ export type FormWorkflowHandlerArgs = {
formFields: FormData;
formFieldsChanged: boolean;
errHandler?: FormWorkflowErrorHandler;
- dispatch?: Dispatch;
+ dispatch?: Dispatch;
};
export type FormWorkflowAwaitHandler = {
@@ -59,7 +59,8 @@ export type FormWorkflowStep = {
validations: FormFieldValidation[];
saveChanges?: (
formData: FormData,
- errHandler: FormWorkflowErrorHandler
+ errHandler: FormWorkflowErrorHandler,
+ formFieldsChanged: boolean,
) => Promise;
nextButtonConfig: (FormData: FormData) => FormWorkflowButtonConfig;
showBackButton?: boolean;
@@ -197,6 +198,7 @@ const FormWorkflow = ({
const FormComponent: DynamicComponent = currentStep?.formComponent;
return (
@@ -245,7 +247,7 @@ const FormWorkflow = ({
exitWithoutSaving={() => onClickOut(false)}
saveDraft={async () => {
if (step?.saveChanges) {
- await step?.saveChanges(formFields as FormConfigData, setFormError);
+ await step?.saveChanges(formFields as FormConfigData, setFormError, !!isEdited);
onClickOut(true, SUBMIT_TOAST_MESSAGE);
}
onClickOut(false, 'No changes saved');
diff --git a/src/components/forms/data/actions.ts b/src/components/forms/data/actions.ts
index 19f5cf3988..a019d77839 100644
--- a/src/components/forms/data/actions.ts
+++ b/src/components/forms/data/actions.ts
@@ -55,6 +55,12 @@ export const setStepAction = ({ step }: SetStepArguments) => ({
step,
});
+export const RESET_EDIT_STATE = 'RESET EDIT STATE';
+// Construct action for resetting isEdited property
+export const resetFormEditState = () => ({
+ type: RESET_EDIT_STATE,
+});
+
// Global Workflow state keys
export const FORM_ERROR_MESSAGE = 'FORM ERROR MESSAGE';
diff --git a/src/components/forms/data/reducer.test.ts b/src/components/forms/data/reducer.test.ts
index 5bb5f43ee6..bb72db10af 100644
--- a/src/components/forms/data/reducer.test.ts
+++ b/src/components/forms/data/reducer.test.ts
@@ -4,8 +4,8 @@ import {
FormWorkflowButtonConfig, FormWorkflowHandlerArgs, FormWorkflowStep,
} from '../FormWorkflow';
import {
- setFormFieldAction, updateFormFieldsAction, setStepAction, setWorkflowStateAction,
- UPDATE_FORM_FIELDS, SET_FORM_FIELD, SET_WORKFLOW_STATE, SET_STEP,
+ setFormFieldAction, updateFormFieldsAction, setStepAction, setWorkflowStateAction, resetFormEditState,
+ UPDATE_FORM_FIELDS, SET_FORM_FIELD, SET_WORKFLOW_STATE, SET_STEP, RESET_EDIT_STATE,
} from './actions';
import type { InitializeFormArguments } from './reducer';
import { FormReducer, initializeForm } from './reducer';
@@ -145,4 +145,16 @@ describe('Form reducer tests', () => {
FormReducer(action, initializeForm(getTestInitializeFormArguments())),
).toStrictEqual(expected);
});
+
+ test('resets isEdited property', () => {
+ const action = resetFormEditState();
+
+ const expected = {
+ type: RESET_EDIT_STATE,
+ };
+
+ expect(
+ FormReducer(action, initializeForm(getTestInitializeFormArguments())),
+ ).toStrictEqual(expected);
+ });
});
diff --git a/src/components/forms/data/reducer.ts b/src/components/forms/data/reducer.ts
index b105f53e35..273fb2310e 100644
--- a/src/components/forms/data/reducer.ts
+++ b/src/components/forms/data/reducer.ts
@@ -2,7 +2,7 @@ import groupBy from 'lodash/groupBy';
import isEmpty from 'lodash/isEmpty';
import keys from 'lodash/keys';
import {
- SetShowErrorsArguments, SET_FORM_FIELD, SET_SHOW_ERRORS, SET_STEP, SET_WORKFLOW_STATE, UPDATE_FORM_FIELDS,
+ SetShowErrorsArguments, SET_FORM_FIELD, SET_SHOW_ERRORS, SET_STEP, SET_WORKFLOW_STATE, UPDATE_FORM_FIELDS, RESET_EDIT_STATE,
} from './actions';
import type {
FormActionArguments, SetFormFieldArguments, SetStepArguments, SetWorkflowStateArguments, UpdateFormFieldArguments,
@@ -114,10 +114,14 @@ export const FormReducer: FormReducerType = (
return {
...state,
formFields: updateFormFieldsArgs.formFields,
- isEdited: false,
hasErrors: false,
errorMap: {},
};
+ } case RESET_EDIT_STATE: {
+ return {
+ ...state,
+ isEdited: false,
+ };
} case SET_STEP: {
const setStepArgs = action as SetStepArguments;
const newStepState = { ...state, currentStep: setStepArgs.step };
diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
index 218811ac3e..d5bbe5b8c7 100644
--- a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
+++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
@@ -10,6 +10,7 @@ import LmsApiService from '../../../data/services/LmsApiService';
import handleErrors from '../utils';
import { snakeCaseDict } from '../../../utils';
import { INVALID_IDP_METADATA_ERROR, RECORD_UNDER_CONFIGURATIONS_ERROR } from '../data/constants';
+import { resetFormEditState } from '../../forms/data/actions';
type SSOConfigSnakeCase = {
uuid?: string,
@@ -99,16 +100,16 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
const saveChanges = async ({
formFields,
errHandler,
- // @ts-ignore:next-line formFieldsChanged is only used in the below TODO
formFieldsChanged,
+ dispatch,
}: FormWorkflowHandlerArgs) => {
let err = null;
- // TODO : Accurately detect if form fields have changed
- // if (!formFieldsChanged && !idpMetadataError) {
- // // Don't submit if nothing has changed
- // return formFields;
- // }
+ // Accurately detect if form fields have changed
+ if (!formFieldsChanged) {
+ // Don't submit if nothing has changed
+ return formFields;
+ }
let updatedFormFields: SSOConfigCamelCase = omit(formFields, ['idpConnectOption', 'spMetadataUrl', 'isPendingConfiguration']);
updatedFormFields.enterpriseCustomer = enterpriseId;
const submittedFormFields: SSOConfigSnakeCase = snakeCaseDict(updatedFormFields) as SSOConfigSnakeCase;
@@ -120,6 +121,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => {
formFields?.uuid,
);
updatedFormFields = updateResponse.data;
+ dispatch?.(resetFormEditState());
} catch (error: AxiosError | any) {
err = handleErrors(error);
if (error.message?.includes('Must provide valid IDP metadata url')) {
From 46eb6d10de407a1297f9affa8df99ae017f25d4f Mon Sep 17 00:00:00 2001
From: muhammad-ammar
Date: Fri, 5 Jan 2024 11:04:07 +0500
Subject: [PATCH 124/124] feat: fetch demo enterprise data if demo data flag is
enabled for enterprise customer
---
.env.development | 1 +
.env.test | 1 +
.../PlotlyAnalytics/PlotlyAnalyticsCharts.jsx | 11 +++++---
.../PlotlyAnalytics/PlotlyAnalyticsPage.jsx | 6 +++--
src/config/index.js | 2 ++
src/data/reducers/portalConfiguration.js | 4 +++
src/data/reducers/portalConfiguration.test.js | 3 +++
src/data/services/EnterpriseDataApiService.js | 27 ++++++++++++++-----
8 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/.env.development b/.env.development
index c63505b9aa..a4f13d4126 100644
--- a/.env.development
+++ b/.env.development
@@ -56,3 +56,4 @@ SUBSCRIPTION_LPR='true'
PLOTLY_SERVER_URL='http://localhost:8050'
AUTH0_SELF_SERVICE_INTEGRATION='true'
MFE_CONFIG_API_URL='http://localhost:18000/api/mfe_config/v1'
+DEMO_ENTEPRISE_UUID='set a valid enterprise uuid'
diff --git a/.env.test b/.env.test
index bb5c5aef28..3868d759d8 100644
--- a/.env.test
+++ b/.env.test
@@ -15,3 +15,4 @@ FEATURE_FILE_ATTACHMENT='true'
ENTERPRISE_SUPPORT_URL = ''
ENTERPRISE_SUPPORT_REVOKE_LICENSE_URL = ''
PLOTLY_SERVER_URL='http://localhost:8050'
+DEMO_ENTEPRISE_UUID='set a valid enterprise uuid'
diff --git a/src/components/PlotlyAnalytics/PlotlyAnalyticsCharts.jsx b/src/components/PlotlyAnalytics/PlotlyAnalyticsCharts.jsx
index 5643361eb7..7bb3fa91cc 100644
--- a/src/components/PlotlyAnalytics/PlotlyAnalyticsCharts.jsx
+++ b/src/components/PlotlyAnalytics/PlotlyAnalyticsCharts.jsx
@@ -5,21 +5,23 @@ import PropTypes from 'prop-types';
import LoadingMessage from '../LoadingMessage';
import ErrorPage from '../ErrorPage';
import PlotlyAnalyticsApiService from './data/service';
-import { configuration } from '../../config';
+import { configuration, features } from '../../config';
-const PlotlyAnalyticsCharts = ({ enterpriseId }) => {
+const PlotlyAnalyticsCharts = ({ enterpriseId, enableDemoData }) => {
const [token, setToken] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
+ const { FEATURE_DEMO_DATA } = features;
+ const enterpriseUUID = FEATURE_DEMO_DATA && enableDemoData ? configuration.DEMO_ENTEPRISE_UUID : enterpriseId;
const refreshPlotlyToken = async () => {
- const response = await PlotlyAnalyticsApiService.fetchPlotlyToken({ enterpriseId });
+ const response = await PlotlyAnalyticsApiService.fetchPlotlyToken({ enterpriseUUID });
return response.data.token;
};
useEffect(() => {
setIsLoading(true);
- PlotlyAnalyticsApiService.fetchPlotlyToken({ enterpriseId })
+ PlotlyAnalyticsApiService.fetchPlotlyToken({ enterpriseUUID })
.then((response) => {
setToken(response.data.token);
setIsLoading(false);
@@ -56,6 +58,7 @@ const PlotlyAnalyticsCharts = ({ enterpriseId }) => {
PlotlyAnalyticsCharts.propTypes = {
enterpriseId: PropTypes.string.isRequired,
+ enableDemoData: PropTypes.bool.isRequired,
};
export default PlotlyAnalyticsCharts;
diff --git a/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx b/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx
index a28f0eae3b..bf899961c8 100644
--- a/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx
+++ b/src/components/PlotlyAnalytics/PlotlyAnalyticsPage.jsx
@@ -10,7 +10,7 @@ import PlotlyAnalyticsCharts from './PlotlyAnalyticsCharts';
const PAGE_TITLE = 'Analytics';
-const PlotlyAnalyticsPage = ({ enterpriseId }) => {
+const PlotlyAnalyticsPage = ({ enterpriseId, enableDemoData }) => {
const [status, setStatus] = useState({
visible: false, alertType: '', message: '',
});
@@ -44,17 +44,19 @@ const PlotlyAnalyticsPage = ({ enterpriseId }) => {
{renderStatusMessage()}
-
+
>
);
};
PlotlyAnalyticsPage.propTypes = {
enterpriseId: PropTypes.string.isRequired,
+ enableDemoData: PropTypes.bool.isRequired,
};
const mapStateToProps = state => ({
enterpriseId: state.portalConfiguration.enterpriseId,
+ enableDemoData: state.portalConfiguration.enableDemoData,
});
export default connect(mapStateToProps)(PlotlyAnalyticsPage);
diff --git a/src/config/index.js b/src/config/index.js
index e83f03cd54..0f9ec37fb8 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -36,6 +36,7 @@ const configuration = {
LOGO_TRADEMARK_URL: process.env.LOGO_TRADEMARK_URL,
USE_API_CACHE: process.env.USE_API_CACHE,
PLOTLY_SERVER_URL: process.env.PLOTLY_SERVER_URL,
+ DEMO_ENTEPRISE_UUID: process.env.DEMO_ENTEPRISE_UUID,
};
const features = {
@@ -53,6 +54,7 @@ const features = {
FEATURE_SSO_SETTINGS_TAB: process.env.FEATURE_SSO_SETTINGS_TAB || hasFeatureFlagEnabled('SSO_SETTINGS_TAB'),
FEATURE_INTEGRATION_REPORTING: process.env.FEATURE_INTEGRATION_REPORTING || hasFeatureFlagEnabled('FEATURE_INTEGRATION_REPORTING'),
FEATURE_API_CREDENTIALS_TAB: process.env.FEATURE_API_CREDENTIALS_TAB || hasFeatureFlagEnabled('FEATURE_API_CREDENTIALS_TAB'),
+ FEATURE_DEMO_DATA: process.env.FEATURE_DEMO_DATA || hasFeatureFlagEnabled('FEATURE_DEMO_DATA'),
SUBSCRIPTION_LPR: process.env.SUBSCRIPTION_LPR || hasFeatureFlagEnabled('SUBSCRIPTION_LPR'),
AUTH0_SELF_SERVICE_INTEGRATION: process.env.AUTH0_SELF_SERVICE_INTEGRATION || hasFeatureFlagEnabled('AUTH0_SELF_SERVICE_INTEGRATION'),
};
diff --git a/src/data/reducers/portalConfiguration.js b/src/data/reducers/portalConfiguration.js
index 9527e495ef..e0330d2b68 100644
--- a/src/data/reducers/portalConfiguration.js
+++ b/src/data/reducers/portalConfiguration.js
@@ -26,6 +26,7 @@ const initialState = {
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
enableApiCredentialGeneration: false,
+ enableDemoData: false,
enterpriseFeatures: {},
};
@@ -59,6 +60,7 @@ const portalConfiguration = (state = initialState, action) => {
enableUniversalLink: action.payload.data.enable_universal_link,
enablePortalLearnerCreditManagementScreen: action.payload.data.enable_portal_learner_credit_management_screen,
enableApiCredentialGeneration: action.payload.data.enable_generation_of_api_credentials,
+ enableDemoData: action.payload.data.enable_demo_data_for_analytics_and_lpr,
enterpriseFeatures: action.payload.enterpriseFeatures,
};
case FETCH_PORTAL_CONFIGURATION_FAILURE:
@@ -83,6 +85,7 @@ const portalConfiguration = (state = initialState, action) => {
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
enableApiCredentialGeneration: false,
+ enableDemoData: false,
enterpriseFeatures: {},
};
case CLEAR_PORTAL_CONFIGURATION:
@@ -105,6 +108,7 @@ const portalConfiguration = (state = initialState, action) => {
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
enableApiCredentialGeneration: false,
+ enableDemoData: false,
enterpriseFeatures: {},
};
case UPDATE_PORTAL_CONFIGURATION:
diff --git a/src/data/reducers/portalConfiguration.test.js b/src/data/reducers/portalConfiguration.test.js
index f8d328f5ea..6969716ab4 100644
--- a/src/data/reducers/portalConfiguration.test.js
+++ b/src/data/reducers/portalConfiguration.test.js
@@ -25,6 +25,7 @@ const initialState = {
enableLmsConfigurationsScreen: false,
enableUniversalLink: false,
enablePortalLearnerCreditManagementScreen: false,
+ enableDemoData: false,
enterpriseFeatures: {},
};
@@ -53,6 +54,7 @@ const enterpriseData = {
enable_universal_link: true,
enable_browse_and_request: true,
enable_generation_of_api_credentials: true,
+ enable_demo_data_for_analytics_and_lpr: true,
};
const mockEnterpriseFeatures = { featureA: true };
@@ -83,6 +85,7 @@ describe('portalConfiguration reducer', () => {
enableUniversalLink: enterpriseData.enable_universal_link,
enablePortalLearnerCreditManagementScreen: enterpriseData.enable_portal_learner_credit_management_screen,
enableApiCredentialGeneration: enterpriseData.enable_generation_of_api_credentials,
+ enableDemoData: enterpriseData.enable_demo_data_for_analytics_and_lpr,
enterpriseFeatures: mockEnterpriseFeatures,
};
expect(portalConfiguration(undefined, {
diff --git a/src/data/services/EnterpriseDataApiService.js b/src/data/services/EnterpriseDataApiService.js
index ef7b5078e5..7bf1fb85f0 100644
--- a/src/data/services/EnterpriseDataApiService.js
+++ b/src/data/services/EnterpriseDataApiService.js
@@ -1,7 +1,8 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { snakeCaseObject } from '@edx/frontend-platform/utils';
-import { configuration } from '../../config';
+import store from '../store';
+import { configuration, features } from '../../config';
class EnterpriseDataApiService {
// TODO: This should access the data-api through the gateway instead of direct
@@ -11,12 +12,20 @@ class EnterpriseDataApiService {
static enterpriseAdminBaseUrl = `${configuration.DATA_API_BASE_URL}/enterprise/api/v1/admin/`;
+ static getEnterpriseUUID(enterpriseId) {
+ const { FEATURE_DEMO_DATA } = features;
+ const { enableDemoData } = store.getState().portalConfiguration;
+ return FEATURE_DEMO_DATA && enableDemoData ? configuration.DEMO_ENTEPRISE_UUID : enterpriseId;
+ }
+
static fetchDashboardAnalytics(enterpriseId) {
- const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/enrollments/overview/`;
+ const enterpriseUUID = EnterpriseDataApiService.getEnterpriseUUID(enterpriseId);
+ const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseUUID}/enrollments/overview/`;
return EnterpriseDataApiService.apiClient().get(url);
}
static fetchCourseEnrollments(enterpriseId, options, { csv } = {}) {
+ const enterpriseUUID = EnterpriseDataApiService.getEnterpriseUUID(enterpriseId);
const endpoint = csv ? 'enrollments.csv' : 'enrollments';
const queryParams = new URLSearchParams({
page: 1,
@@ -28,7 +37,7 @@ class EnterpriseDataApiService {
queryParams.set('no_page', csv);
}
- const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/${endpoint}/?${queryParams.toString()}`;
+ const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseUUID}/${endpoint}/?${queryParams.toString()}`;
return EnterpriseDataApiService.apiClient().get(url);
}
@@ -44,6 +53,7 @@ class EnterpriseDataApiService {
}
static fetchUnenrolledRegisteredLearners(enterpriseId, options, { csv } = {}) {
+ const enterpriseUUID = EnterpriseDataApiService.getEnterpriseUUID(enterpriseId);
const endpoint = csv ? 'users.csv' : 'users';
const queryParams = new URLSearchParams({
page: 1,
@@ -56,11 +66,12 @@ class EnterpriseDataApiService {
queryParams.set('no_page', csv);
}
- const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/${endpoint}/?${queryParams.toString()}`;
+ const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseUUID}/${endpoint}/?${queryParams.toString()}`;
return EnterpriseDataApiService.apiClient().get(url);
}
static fetchEnrolledLearners(enterpriseId, options, { csv } = {}) {
+ const enterpriseUUID = EnterpriseDataApiService.getEnterpriseUUID(enterpriseId);
const endpoint = csv ? 'users.csv' : 'users';
const queryParams = new URLSearchParams({
page: 1,
@@ -74,11 +85,12 @@ class EnterpriseDataApiService {
queryParams.set('no_page', csv);
}
- const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/${endpoint}/?${queryParams.toString()}`;
+ const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseUUID}/${endpoint}/?${queryParams.toString()}`;
return EnterpriseDataApiService.apiClient().get(url);
}
static fetchEnrolledLearnersForInactiveCourses(enterpriseId, options, { csv } = {}) {
+ const enterpriseUUID = EnterpriseDataApiService.getEnterpriseUUID(enterpriseId);
const endpoint = csv ? 'users.csv' : 'users';
const queryParams = new URLSearchParams({
page: 1,
@@ -94,11 +106,12 @@ class EnterpriseDataApiService {
queryParams.set('no_page', csv);
}
- const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/${endpoint}/?${queryParams.toString()}`;
+ const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseUUID}/${endpoint}/?${queryParams.toString()}`;
return EnterpriseDataApiService.apiClient().get(url);
}
static fetchCompletedLearners(enterpriseId, options, { csv } = {}) {
+ const enterpriseUUID = EnterpriseDataApiService.getEnterpriseUUID(enterpriseId);
const endpoint = csv ? 'learner_completed_courses.csv' : 'learner_completed_courses';
const queryParams = new URLSearchParams({
page: 1,
@@ -110,7 +123,7 @@ class EnterpriseDataApiService {
queryParams.set('no_page', csv);
}
- const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseId}/${endpoint}/?${queryParams.toString()}`;
+ const url = `${EnterpriseDataApiService.enterpriseBaseUrl}${enterpriseUUID}/${endpoint}/?${queryParams.toString()}`;
return EnterpriseDataApiService.apiClient().get(url);
}