Skip to content

Commit

Permalink
Merge pull request #722 from opensrp/712-add-pagination-to-practitioners
Browse files Browse the repository at this point in the history
712 add pagination to practitioners
  • Loading branch information
machariamuguku authored Jun 29, 2021
2 parents 84f8f7b + f3ea509 commit c74e7e3
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 38 deletions.
3 changes: 2 additions & 1 deletion app/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ REACT_APP_PROJECT_LANGUAGE_CODE=eusm
REACT_APP_SUPPORTED_LANGUAGES=en,fr
REACT_APP_ENABLE_LANGUAGE_SWITCHER=true
# user roles
REACT_APP_OPENSRP_ROLES={"USERS": "ROLE_EDIT_KEYCLOAK_USERS", "PLANS": "ROLE_VIEW_KEYCLOAK_USERS", "LOCATIONS": "ROLE_VIEW_KEYCLOAK_USERS", "CARD_SUPPORT": "ROLE_VIEW_KEYCLOAK_USERS", "INVENTORY": "ROLE_VIEW_KEYCLOAK_USERS", "TEAMS": "ROLE_VIEW_KEYCLOAK_USERS", "PRODUCT_CATALOGUE": "ROLE_VIEW_KEYCLOAK_USERS", "FORM_CONFIGURATION": "ROLE_VIEW_KEYCLOAK_USERS"}
REACT_APP_OPENSRP_ROLES="{"USERS": "ROLE_EDIT_KEYCLOAK_USERS", "PLANS": "ROLE_VIEW_KEYCLOAK_USERS", "LOCATIONS": "ROLE_VIEW_KEYCLOAK_USERS", "CARD_SUPPORT": "ROLE_VIEW_KEYCLOAK_USERS", "INVENTORY": "ROLE_VIEW_KEYCLOAK_USERS", "TEAMS": "ROLE_VIEW_KEYCLOAK_USERS", "PRODUCT_CATALOGUE": "ROLE_VIEW_KEYCLOAK_USERS", "FORM_CONFIGURATION": "ROLE_VIEW_KEYCLOAK_USERS"}"
REACT_APP_DISABLE_TEAM_MEMBER_REASSIGNMENT=false
REACT_APP_USER_FORM_HIDDEN_FIELDS=""
REACT_APP_USER_FORM_RENDER_FIELDS=""
REACT_APP_PAGINATION_SIZE=1000
1 change: 1 addition & 0 deletions app/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ const App: React.FC = () => {
exact
path={URL_TEAMS_ADD}
component={TeamsAddEdit}
{...teamManagementProps}
/>
<PrivateComponent
redirectPath={APP_CALLBACK_URL}
Expand Down
4 changes: 3 additions & 1 deletion app/src/App/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import {
TASK_GENERATION_STATUS,
DEFAULT_PLAN_ID,
FILTER_BY_PARENT_ID,
KEYCLOAK_USERS_PAGE_SIZE,
KEYCLOAK_USERS_PAGE_SIZE,
DISABLE_TEAM_MEMBER_REASSIGNMENT,
USER_FORM_HIDDEN_FIELDS,
USER_FORM_RENDER_FIELDS,
PAGINATION_SIZE,
} from '../configs/env';

export const BaseProps = {
Expand All @@ -38,6 +39,7 @@ export const teamAssignmentProps = {

export const teamManagementProps = {
disableTeamMemberReassignment: DISABLE_TEAM_MEMBER_REASSIGNMENT,
paginationSize: PAGINATION_SIZE,
};

export const locationUnitProps = {
Expand Down
2 changes: 2 additions & 0 deletions app/src/configs/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,5 @@ export const DISABLE_TEAM_MEMBER_REASSIGNMENT =
export const USER_FORM_HIDDEN_FIELDS = setEnv('REACT_APP_USER_FORM_HIDDEN_FIELDS', '').split(',');

export const USER_FORM_RENDER_FIELDS = setEnv('REACT_APP_USER_FORM_RENDER_FIELDS', '').split(',');

export const PAGINATION_SIZE = Number(setEnv('REACT_APP_PAGINATION_SIZE', 1000));
87 changes: 82 additions & 5 deletions packages/team-management/src/components/TeamsAddEdit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
TEAMS_GET,
TEAM_PRACTITIONERS,
PRACTITIONER_ROLE,
PRACTITIONER_COUNT,
} from '../../constants';
import { OpenSRPService } from '@opensrp/react-utils';
import { sendErrorNotification } from '@opensrp/notifications';
Expand Down Expand Up @@ -83,19 +84,91 @@ function setupInitialValue(
export interface Props {
opensrpBaseURL: string;
disableTeamMemberReassignment: boolean;
paginationSize: number;
}

/** default component props */
export const defaultProps = {
opensrpBaseURL: '',
};

/**
* function to fetch a paginated practitioner object
*
* @param opensrpBaseURL - OpenSRP API base URL
* @param practitionersEndpoint - OpenSRP practitioners endpoint
* @param pageNumber - paginated page number
* @param pageSize - number of practitioners in each page
* @param errorNotification - the error message to show for a failed request
* @returns {Promise<Practitioner[]>} an array of practitioners
*/
async function fetchPractitioners(
opensrpBaseURL: string,
practitionersEndpoint: string,
pageNumber: number,
pageSize: number,
errorNotification: string = lang.ERROR_OCCURRED
): Promise<Practitioner[]> {
const paginationParams = {
pageNumber,
pageSize,
};
const serve = new OpenSRPService(practitionersEndpoint, opensrpBaseURL);
try {
const practitioners: Practitioner[] = await serve.list(paginationParams);
return practitioners;
} catch (_) {
sendErrorNotification(errorNotification);
return [];
}
}

/**
* function to fetch paginated practitioners resource recursively
*
* @param opensrpBaseURL - OpenSRP API base URL
* @param practitionersCountEndpoint - OpenSRP practitioners count endpoint
* @param practitionersEndpoint - OpenSRP practitioners endpoint
* @param pageSize - number of practitioners in each page
* @returns {Promise<Practitioner[]>} - an array of all practitioners in a paginated endpoint
*/
async function fetchPractitionersRecursively(
opensrpBaseURL: string,
practitionersCountEndpoint: string,
practitionersEndpoint: string,
pageSize: number
): Promise<Practitioner[]> {
// get the total number of practitioners
const serve = new OpenSRPService(practitionersCountEndpoint, opensrpBaseURL);
const practitionerCount: number = await serve.list();

// get the maximum possible page numbers
const maxPageNo = Math.ceil(practitionerCount / pageSize);

// compose a promise array to resolve in parallel
const promises: Promise<Practitioner[]>[] = [];
for (let pageNumber = 1; pageNumber <= maxPageNo; pageNumber++) {
promises.push(fetchPractitioners(opensrpBaseURL, practitionersEndpoint, pageNumber, pageSize));
}

// fetch practitioners recursively according to page numbers
return Promise.all(promises)
.then((practitioners: Practitioner[][]) => {
// flatten 2D array - [[][]]
const flatPractitionersArray = practitioners.flat();
return flatPractitionersArray;
})
.catch((err) => {
throw err;
});
}

export const TeamsAddEdit: React.FC<Props> = (props: Props) => {
const params: { id: string } = useParams();
const [initialValue, setInitialValue] = useState<FormField | null>(null);
const [practitioners, setPractitioners] = useState<Practitioner[] | null>(null);
const [practitionersRole, setPractitionersRole] = useState<PractitionerPOST[] | null>(null);
const { opensrpBaseURL, disableTeamMemberReassignment } = props;
const { opensrpBaseURL, disableTeamMemberReassignment, paginationSize } = props;

useEffect(() => {
if (params.id) setupInitialValue(params.id, opensrpBaseURL, setInitialValue);
Expand Down Expand Up @@ -124,9 +197,13 @@ export const TeamsAddEdit: React.FC<Props> = (props: Props) => {
*/
useEffect(() => {
if ((disableTeamMemberReassignment && practitionersRole) || !disableTeamMemberReassignment) {
const serve = new OpenSRPService(PRACTITIONER_GET, opensrpBaseURL);
serve
.list()
// fetch practitioners recursively from a paginated endpoint
fetchPractitionersRecursively(
opensrpBaseURL,
PRACTITIONER_COUNT,
PRACTITIONER_GET,
paginationSize
)
.then((response: Practitioner[]) => {
// filter out inactive practitioners
const activePractitioners = response.filter((practitioner) => practitioner.active);
Expand Down Expand Up @@ -162,7 +239,7 @@ export const TeamsAddEdit: React.FC<Props> = (props: Props) => {
})
.catch(() => sendErrorNotification(lang.ERROR_OCCURRED));
}
}, [disableTeamMemberReassignment, opensrpBaseURL, practitionersRole]);
}, [disableTeamMemberReassignment, opensrpBaseURL, paginationSize, practitionersRole]);

if (!practitioners || (params.id && !initialValue)) return <Spin size={'large'} />;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {

expect(fetch.mock.calls).toMatchObject([
[
'https://opensrp-stage.smartregister.org/opensrp/rest/organization',
'https://some.opensrp.url/organization',
{
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expand Down Expand Up @@ -245,7 +245,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=0&organization=b0c20f20-c1c0-4ea3-b855-4fcb23f6ae2a',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=0&organization=b0c20f20-c1c0-4ea3-b855-4fcb23f6ae2a',
{
headers: {
accept: 'application/json',
Expand All @@ -256,7 +256,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=1&organization=b0c20f20-c1c0-4ea3-b855-4fcb23f6ae2a',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=1&organization=b0c20f20-c1c0-4ea3-b855-4fcb23f6ae2a',
{
headers: {
accept: 'application/json',
Expand All @@ -267,7 +267,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=2&organization=b0c20f20-c1c0-4ea3-b855-4fcb23f6ae2a',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=2&organization=b0c20f20-c1c0-4ea3-b855-4fcb23f6ae2a',
{
headers: {
accept: 'application/json',
Expand Down Expand Up @@ -301,7 +301,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {

expect(fetch.mock.calls).toMatchObject([
[
'https://opensrp-stage.smartregister.org/opensrp/rest/organization/258b4dec-79d3-546d-9c5c-f172aa7e03b0',
'https://some.opensrp.url/organization/258b4dec-79d3-546d-9c5c-f172aa7e03b0',
{
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expand Down Expand Up @@ -329,7 +329,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=0&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=0&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
{
headers: {
accept: 'application/json',
Expand All @@ -340,7 +340,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=1&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=1&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
{
headers: {
accept: 'application/json',
Expand All @@ -351,7 +351,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/add/',
'https://some.opensrp.url/practitionerRole/add/',
{
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expand Down Expand Up @@ -390,7 +390,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {

expect(fetch.mock.calls).toMatchObject([
[
'https://opensrp-stage.smartregister.org/opensrp/rest/organization/258b4dec-79d3-546d-9c5c-f172aa7e03b0',
'https://some.opensrp.url/organization/258b4dec-79d3-546d-9c5c-f172aa7e03b0',
{
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expand Down Expand Up @@ -418,7 +418,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=0&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=0&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
{
headers: {
accept: 'application/json',
Expand All @@ -429,7 +429,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/deleteByPractitioner?practitioner=1&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
'https://some.opensrp.url/practitionerRole/deleteByPractitioner?practitioner=1&organization=258b4dec-79d3-546d-9c5c-f172aa7e03b0',
{
headers: {
accept: 'application/json',
Expand All @@ -440,7 +440,7 @@ describe('Team-management/TeamsAddEdit/Form', () => {
},
],
[
'https://opensrp-stage.smartregister.org/opensrp/rest/practitionerRole/add/',
'https://some.opensrp.url/practitionerRole/add/',
{
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Practitioner, PractitionerPOST } from '../../../ducks/practitioners';
import { FormField } from '../Form';

export const accessToken = 'token';
export const opensrpBaseURL = 'https://opensrp-stage.smartregister.org/opensrp/rest/';
export const opensrpBaseURL = 'https://some.opensrp.url/';
export const id = '258b4dec-79d3-546d-9c5c-f172aa7e03b0';

export const team: Organization = {
Expand Down
Loading

0 comments on commit c74e7e3

Please sign in to comment.