diff --git a/package-lock.json b/package-lock.json index 3816075a04..816d7c99b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7205,8 +7205,9 @@ } }, "node_modules/axios": { - "version": "1.7.3", - "license": "MIT", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "peer": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -8680,8 +8681,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "license": "MIT", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -12427,8 +12429,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "license": "MIT", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", diff --git a/src/components/Admin/Admin.test.jsx b/src/components/Admin/Admin.test.jsx index c0a1105c74..0cd7685cf4 100644 --- a/src/components/Admin/Admin.test.jsx +++ b/src/components/Admin/Admin.test.jsx @@ -54,6 +54,10 @@ const store = mockStore({ loading: null, insights: null, }, + enterpriseBudgets: { + loading: null, + budgets: null, + }, }); const AdminWrapper = props => ( @@ -78,8 +82,14 @@ const AdminWrapper = props => ( pathname: '/', }} {...props} + budgets={[{ + subsidy_access_policy_uuid: '8d6503dd-e40d-42b8-442b-37dd4c5450e3', + subsidy_access_policy_display_name: 'Everything', + }]} fetchDashboardInsights={() => {}} clearDashboardInsights={() => {}} + fetchEnterpriseBudgets={() => {}} + clearEnterpriseBudgets={() => {}} /> @@ -97,6 +107,7 @@ describe('', () => { lastUpdatedDate: '2018-07-31T23:14:35Z', numberOfUsers: 3, insights: null, + budgets: [], }; describe('renders correctly', () => { @@ -370,6 +381,26 @@ describe('', () => { expect(tree).toMatchSnapshot(); }); }); + + describe('with enterprise budgets data', () => { + it('renders budgets correctly', () => { + const budgetUUID = '8d6503dd-e40d-42b8-442b-37dd4c5450e3'; + const budgets = [{ + subsidy_access_policy_uuid: budgetUUID, + subsidy_access_policy_display_name: 'Everything', + }]; + const tree = renderer + .create(( + + )) + .toJSON(); + + expect(tree).toMatchSnapshot(); + }); + }); }); describe('handle changes to enterpriseId prop', () => { diff --git a/src/components/Admin/AdminSearchForm.jsx b/src/components/Admin/AdminSearchForm.jsx index a2624cef84..e93626f166 100644 --- a/src/components/Admin/AdminSearchForm.jsx +++ b/src/components/Admin/AdminSearchForm.jsx @@ -1,6 +1,7 @@ /* eslint-disable camelcase */ import React from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { Form } from '@openedx/paragon'; import { Info } from '@openedx/paragon/icons'; @@ -14,17 +15,22 @@ import { withLocation, withNavigate } from '../../hoc'; class AdminSearchForm extends React.Component { componentDidUpdate(prevProps) { - const { searchParams: { searchQuery, searchCourseQuery, searchDateQuery } } = this.props; + const { + searchParams: { + searchQuery, searchCourseQuery, searchDateQuery, searchBudgetQuery, + }, + } = this.props; const { searchParams: { searchQuery: prevSearchQuery, searchCourseQuery: prevSearchCourseQuery, searchDateQuery: prevSearchDateQuery, + searchBudgetQuery: prevSearchBudgetQuery, }, } = prevProps; if (searchQuery !== prevSearchQuery || searchCourseQuery !== prevSearchCourseQuery - || searchDateQuery !== prevSearchDateQuery) { + || searchDateQuery !== prevSearchDateQuery || searchBudgetQuery !== prevSearchBudgetQuery) { this.handleSearch(); } } @@ -45,14 +51,27 @@ class AdminSearchForm extends React.Component { updateUrl(navigate, location.pathname, updateParams); } + onBudgetSelect(event) { + const { navigate, location } = this.props; + const updateParams = { + budget_uuid: event.target.value, + page: 1, + }; + updateUrl(navigate, location.pathname, updateParams); + } + render() { const { intl, tableData, - searchParams: { searchCourseQuery, searchDateQuery, searchQuery }, + budgets, + searchParams: { + searchCourseQuery, searchDateQuery, searchQuery, searchBudgetQuery, + }, } = this.props; const courseTitles = Array.from(new Set(tableData.map(en => en.course_title).sort())); const courseDates = Array.from(new Set(tableData.map(en => en.course_start_date).sort().reverse())); + const columnWidth = budgets?.length ? 'col-md-3' : 'col-md-6'; return (
@@ -151,7 +170,7 @@ class AdminSearchForm extends React.Component {
-
+
+ {budgets?.length && ( +
+ + + + + this.onBudgetSelect(e)} + > + + {budgets.map(budget => ( + + ))} + + +
+ )}
@@ -193,8 +247,10 @@ AdminSearchForm.propTypes = { searchQuery: PropTypes.string, searchCourseQuery: PropTypes.string, searchDateQuery: PropTypes.string, + searchBudgetQuery: PropTypes.string, }).isRequired, tableData: PropTypes.arrayOf(PropTypes.shape({})), + budgets: PropTypes.arrayOf(PropTypes.shape({})), navigate: PropTypes.func, location: PropTypes.shape({ pathname: PropTypes.string, diff --git a/src/components/Admin/AdminSearchForm.test.jsx b/src/components/Admin/AdminSearchForm.test.jsx index 7a0d944ebf..dada76b682 100644 --- a/src/components/Admin/AdminSearchForm.test.jsx +++ b/src/components/Admin/AdminSearchForm.test.jsx @@ -5,6 +5,7 @@ import { IntlProvider } from '@edx/frontend-platform/i18n'; import AdminSearchForm from './AdminSearchForm'; import SearchBar from '../SearchBar'; +import { updateUrl } from '../../utils'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -12,6 +13,10 @@ jest.mock('react-router-dom', () => ({ useNavigate: jest.fn(), })); +jest.mock('../../utils', () => ({ + updateUrl: jest.fn(), +})); + const DEFAULT_PROPS = { searchEnrollmentsList: () => {}, searchParams: {}, @@ -48,4 +53,32 @@ describe('', () => { expect(spy).toHaveBeenCalledTimes(1); }); }); + + it('select the correct budget', () => { + const budgetUUID = '8d6503dd-e40d-42b8-442b-37dd4c5450e3'; + const budgets = [{ + subsidy_access_policy_uuid: budgetUUID, + subsidy_access_policy_display_name: 'Everything', + }]; + const props = { + ...DEFAULT_PROPS, + budgets, + location: { pathname: '/admin/learners' }, + }; + const wrapper = mount( + , + ); + const selectElement = wrapper.find('.budgets-dropdown select'); + + selectElement.simulate('change', { target: { value: budgetUUID } }); + expect(updateUrl).toHaveBeenCalled(); + expect(updateUrl).toHaveBeenCalledWith( + undefined, + '/admin/learners', + { + budget_uuid: budgetUUID, + page: 1, + }, + ); + }); }); diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap index 234d842110..5b2458ef53 100644 --- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap +++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap @@ -1665,7 +1665,7 @@ exports[` renders correctly with dashboard analytics data renders # cou > @@ -1674,7 +1674,7 @@ exports[` renders correctly with dashboard analytics data renders # cou > renders correctly with dashboard analytics data renders # cou
+
+
+ +
+ +
+
+
@@ -1855,7 +1891,7 @@ exports[` renders correctly with dashboard analytics data renders # cou >