Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add pagination for reporting configs list #1361

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 69 additions & 20 deletions src/components/ReportingConfig/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Collapsible, Icon } from '@openedx/paragon';
import { Collapsible, Icon, Pagination } from '@openedx/paragon';
import { Check, Close } from '@openedx/paragon/icons';
import { camelCaseObject } from '@edx/frontend-platform';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
Expand All @@ -12,6 +12,7 @@
import ErrorPage from '../ErrorPage';

const STATUS_FULFILLED = 'fulfilled';
const DEFAULT_PAGE_SIZE = 10;

class ReportingConfig extends React.Component {
// eslint-disable-next-line react/state-in-constructor
Expand All @@ -33,7 +34,15 @@
LMSApiService.fetchReportingConfigTypes(this.props.enterpriseId),
])
.then((responses) => {
let totalPages = responses[0].status === STATUS_FULFILLED ? responses[0].value.data.num_pages : 1;
if (!totalPages) {
totalPages = 1;
}

this.setState({
totalPages,
currentPage: 1,
totalRecords: responses[0].status === STATUS_FULFILLED ? responses[0].value.data.count : 0,
reportingConfigs: responses[0].status === STATUS_FULFILLED ? responses[0].value.data.results : undefined,
availableCatalogs: responses[1].status === STATUS_FULFILLED ? responses[1].value.data.results : undefined,
reportingConfigTypes: responses[2].status === STATUS_FULFILLED ? responses[2].value.data : undefined,
Expand All @@ -55,13 +64,15 @@
// snake_case the data before sending it to the backend
const transformedData = snakeCaseFormData(formData);
try {
const response = await LMSApiService.postNewReportingConfig(transformedData);
this.setState(prevState => ({
reportingConfigs: [
...prevState.reportingConfigs,
response.data,
],
}));
await LMSApiService.postNewReportingConfig(transformedData);

Check warning on line 67 in src/components/ReportingConfig/index.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/ReportingConfig/index.jsx#L67

Added line #L67 was not covered by tests

const lastPageHaveSpace = this.state.totalRecords % this.state.totalPages > 0;

Check warning on line 69 in src/components/ReportingConfig/index.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/ReportingConfig/index.jsx#L69

Added line #L69 was not covered by tests

if (lastPageHaveSpace || this.state.reportingConfigs.length < DEFAULT_PAGE_SIZE) {
this.handlePageSelect(this.state.totalPages);
} else {
this.handlePageSelect(this.state.totalPages + 1);

Check warning on line 74 in src/components/ReportingConfig/index.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/ReportingConfig/index.jsx#L72-L74

Added lines #L72 - L74 were not covered by tests
}
this.newConfigFormRef.current.close();
return undefined;
} catch (error) {
Expand All @@ -72,18 +83,17 @@
deleteConfig = async (uuid) => {
try {
await LMSApiService.deleteReportingConfig(uuid);
const deletedIndex = this.state.reportingConfigs
.findIndex(config => config.uuid === uuid);

this.setState((state) => {
// Copy the existing, needs to be updated, list of reporting configs
const newReportingConfig = [...state.reportingConfigs];
// Splice out the one that's being deleted
newReportingConfig.splice(deletedIndex, 1);
return {
reportingConfigs: newReportingConfig,
};
});

const isLastPage = this.state.currentPage === this.state.totalPages;
const hasOneRecord = this.state.reportingConfigs.length === 1;
const isOnlyRecordOnLastPage = hasOneRecord && isLastPage;

if (isOnlyRecordOnLastPage && this.state.currentPage > 1) {
this.handlePageSelect(this.state.totalPages - 1);

Check warning on line 92 in src/components/ReportingConfig/index.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/ReportingConfig/index.jsx#L92

Added line #L92 was not covered by tests
} else {
this.handlePageSelect(this.state.currentPage);
}

return undefined;
} catch (error) {
return error;
Expand Down Expand Up @@ -111,13 +121,41 @@
}
};

/**
* Handles page select event and fetches the data for the selected page
* @param {number} page - The page number to fetch data for
*/
handlePageSelect = async (page) => {
this.setState({
loading: true,
});

try {
const response = await LMSApiService.fetchReportingConfigs(this.props.enterpriseId, page);
this.setState({
totalPages: response.data.num_pages || 1,
totalRecords: response.data.count,
currentPage: page,
reportingConfigs: response.data.results,
loading: false,
});
} catch (error) {
this.setState({

Check warning on line 143 in src/components/ReportingConfig/index.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/ReportingConfig/index.jsx#L143

Added line #L143 was not covered by tests
loading: false,
error,
});
}
};

render() {
const {
reportingConfigs,
loading,
error,
availableCatalogs,
reportingConfigTypes,
currentPage,
totalPages,
} = this.state;
const { intl } = this.props;
if (loading) {
Expand Down Expand Up @@ -200,6 +238,17 @@
</Collapsible>
</div>
))}

{reportingConfigs && reportingConfigs.length > 0 && (
<Pagination
variant="reduced"
onPageSelect={this.handlePageSelect}
pageCount={totalPages}
currentPage={currentPage}
paginationLabel="reporting configurations pagination"
/>
)}

<Collapsible
styling="basic"
title={intl.formatMessage({
Expand Down
59 changes: 58 additions & 1 deletion src/components/ReportingConfig/index.test.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';
import { mount } from 'enzyme';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { act } from 'react-dom/test-utils';

import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Pagination } from '@openedx/paragon';

import ReportingConfig from './index';
import LmsApiService from '../../data/services/LmsApiService';

Expand Down Expand Up @@ -170,4 +173,58 @@ describe('<ReportingConfig />', () => {
const afterClickingDeleteButton = wrapper.find('button[data-testid="deleteConfigButton"]');
expect(afterClickingDeleteButton.exists()).toBe(false);
});
it('should not render Pagination when reportingConfigs is empty', async () => {
LmsApiService.fetchReportingConfigs.mockResolvedValue({
data: {
results: [],
count: 0,
num_pages: 0,
},
});

let wrapper;
await act(async () => {
wrapper = mount(
<IntlProvider locale="en">
<ReportingConfig {...defaultProps} intl={mockIntl} />
</IntlProvider>,
);
});

wrapper.update();

// Check that Pagination component is not rendered when no configs
const paginationComponent = wrapper.find(Pagination);
expect(paginationComponent.exists()).toBe(false);
});
it('should render Pagination when reportingConfigs has items', async () => {
let wrapper;

LmsApiService.fetchReportingConfigs.mockResolvedValue({
data: {
results: [{
enterpriseCustomerId: 'test-customer-uuid',
active: true,
delivery_method: 'email',
uuid: 'test-config-uuid',
}],
count: 1,
num_pages: 1,
},
});

await act(async () => {
wrapper = mount(
<IntlProvider locale="en">
<ReportingConfig {...defaultProps} intl={mockIntl} />
</IntlProvider>,
);
});

wrapper.update();

// Check that Pagination component is rendered when configs exist
const paginationComponent = wrapper.find(Pagination);
expect(paginationComponent.exists()).toBe(true);
});
});
9 changes: 7 additions & 2 deletions src/data/services/LmsApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,13 @@
return LmsApiService.apiClient().post(requestCodesUrl, postParams);
}

static fetchReportingConfigs(uuid) {
return LmsApiService.apiClient().get(`${LmsApiService.reportingConfigUrl}?enterprise_customer=${uuid}`);
static fetchReportingConfigs(uuid, pageNumber) {
let url = `${LmsApiService.reportingConfigUrl}?enterprise_customer=${uuid}`;

Check warning on line 123 in src/data/services/LmsApiService.js

View check run for this annotation

Codecov / codecov/patch

src/data/services/LmsApiService.js#L122-L123

Added lines #L122 - L123 were not covered by tests
if (pageNumber) {
url += `&page=${pageNumber}`;

Check warning on line 125 in src/data/services/LmsApiService.js

View check run for this annotation

Codecov / codecov/patch

src/data/services/LmsApiService.js#L125

Added line #L125 was not covered by tests
}

return LmsApiService.apiClient().get(url);

Check warning on line 128 in src/data/services/LmsApiService.js

View check run for this annotation

Codecov / codecov/patch

src/data/services/LmsApiService.js#L128

Added line #L128 was not covered by tests
}

static fetchReportingConfigTypes(uuid) {
Expand Down
Loading