Skip to content

Commit

Permalink
Merge pull request #1560 from BLSQ/IA-2871-mappings-add-filters
Browse files Browse the repository at this point in the history
IA-2871 mappings add filters
  • Loading branch information
mestachs authored Aug 23, 2024
2 parents da65326 + 7d2266b commit a40dae1
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 60 deletions.
10 changes: 9 additions & 1 deletion hat/assets/js/apps/Iaso/constants/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,15 @@ export const baseRouteConfigs: Record<string, RouteConfig> = {
},
mappings: {
url: 'forms/mappings',
params: ['accountId', 'formId', ...paginationPathParams],
params: [
'accountId',
'formId',
'mappingTypes',
'orgUnitTypeIds',
'projectsIds',
'search',
...paginationPathParams
],
},
mappingDetail: {
url: 'forms/mapping',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,9 @@ import ConfirmCancelDialogComponent from '../../../components/dialogs/ConfirmCan
import IasoSearchComponent from './IasoSearchComponent';
import Dhis2Search from './Dhis2SearchComponent';
import MESSAGES from '../messages';
import { mappingTypeOptions } from './MappingTypeOptions'
import { useDataSources, useCreateMappingMutation } from '../hooks.js';

const mappingTypeOptions = [
{
value: 'AGGREGATE',
label: MESSAGES.aggregate,
},
{
value: 'EVENT',
label: MESSAGES.event,
},
{
value: 'EVENT_TRACKER',
label: MESSAGES.eventTracker,
},
];

const CreateMappingVersionDialogComponent = () => {
const createMappingRequest = useCreateMappingMutation();
const dataSourcesRequest = useDataSources();
Expand Down
143 changes: 143 additions & 0 deletions hat/assets/js/apps/Iaso/domains/mappings/components/Filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React, { useState, FunctionComponent, useCallback } from 'react';

import { Grid, Button, Box, useMediaQuery, useTheme } from '@mui/material';
import { makeStyles } from '@mui/styles';
import SearchIcon from '@mui/icons-material/Search';
import { useSafeIntl } from 'bluesquare-components';

import InputComponent from '../../../components/forms/InputComponent';
import { useFilterState } from '../../../hooks/useFilterState';
import { mappingTypeOptions } from './MappingTypeOptions';

import MESSAGES from '../messages';

import { baseUrl } from '../config';
import { useGetFormsDropdownOptions } from '../hooks/requests/useGetFormsDropdownOptions';
import { useGetProjectsDropdownOptions } from '../../projects/hooks/requests';
import { useGetOrgUnitTypes } from '../../orgUnits/hooks/requests/useGetOrgUnitTypes';

const useStyles = makeStyles(theme => ({}));

type Params = {
pageSize: string;
order: string;
page: string;
search?: string;
formId?: string;
mappingType?: string;
projectsIds?: string;
};

type Props = {
params: Params;
};

const Filters: FunctionComponent<Props> = ({ params }) => {
const classes: Record<string, string> = useStyles();
const { formatMessage } = useSafeIntl();
const { filters, handleSearch, handleChange, filtersUpdated } =
useFilterState({ baseUrl, params, withPagination: false });
const [textSearchError, setTextSearchError] = useState<boolean>(false);
const { data: orgUnitTypes, isFetching: isFetchingOuTypes } =
useGetOrgUnitTypes();
const { data: allProjects, isFetching: isFetchingProjects } =
useGetProjectsDropdownOptions();

const { data: allForms, isFetching: isFetchingForms } =
useGetFormsDropdownOptions({});

const theme = useTheme();
const isLargeLayout = useMediaQuery(theme.breakpoints.up('md'));

return (
<Grid container spacing={2}>
<Grid item xs={12} md={3}>
<InputComponent
keyValue="search"
onChange={handleChange}
value={filters.search}
type="search"
label={MESSAGES.search}
blockForbiddenChars
onEnterPressed={handleSearch}
onErrorChange={setTextSearchError}
/>
</Grid>

<Grid item xs={12} md={3}>
<InputComponent
keyValue="formId"
onChange={handleChange}
value={filters.formId}
type="select"
options={allForms ?? []}
label={MESSAGES.forms}
loading={isFetchingForms}
onEnterPressed={handleSearch}
clearable
multi
/>
</Grid>

<Grid item xs={12} md={3}>
<InputComponent
type="select"
onChange={handleChange}
keyValue="mappingTypes"
multi
label={MESSAGES.mappingType}
value={filters.mappingTypes}
loading={false}
options={mappingTypeOptions ?? []}
/>
</Grid>

<Grid item xs={12} md={3}>
<InputComponent
type="select"
onChange={handleChange}
keyValue="orgUnitTypeIds"
multi
label={MESSAGES.orgUnitsTypes}
value={filters.orgUnitTypeIds}
loading={isFetchingOuTypes}
options={orgUnitTypes ?? []}
/>
</Grid>
<Grid item xs={12} md={3}>
<InputComponent
keyValue="projectsIds"
onChange={handleChange}
value={filters.projectsIds}
type="select"
options={allProjects}
label={MESSAGES.projects}
loading={isFetchingProjects}
onEnterPressed={handleSearch}
clearable
multi
/>
</Grid>

<Grid container item xs={12} md={12} justifyContent="flex-end">
<Box mt={isLargeLayout ? 3 : 0}>
<Button
data-test="search-button"
disabled={
(!filtersUpdated) || textSearchError
}
variant="contained"
className={classes.button}
color="primary"
onClick={() => handleSearch()}
>
<SearchIcon className={classes.buttonIcon} />
{formatMessage(MESSAGES.search)}
</Button>
</Box>
</Grid>
</Grid>
);
};

export { Filters };
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import MESSAGES from '../messages';

export const mappingTypeOptions = [
{
value: 'AGGREGATE',
label: MESSAGES.aggregate,
},
{
value: 'EVENT',
label: MESSAGES.event,
},
{
value: 'EVENT_TRACKER',
label: MESSAGES.eventTracker,
},
];


5 changes: 3 additions & 2 deletions hat/assets/js/apps/Iaso/domains/mappings/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import MESSAGES from './messages';
import { baseUrls } from '../../constants/urls';
import { DateTimeCell } from '../../components/Cells/DateTimeCell.tsx';

export const baseUrl = baseUrls.mappings;

const safePercent = (a, b) => {
if (b === 0) {
return '-';
}
return (100 * (a / b)).toFixed(2);
};

const mappingsTableColumns = formatMessage => [
export const mappingsTableColumns = formatMessage => [
{
Header: formatMessage(MESSAGES.actions),
accessor: 'actions',
Expand Down Expand Up @@ -73,4 +75,3 @@ const mappingsTableColumns = formatMessage => [
Cell: DateTimeCell,
},
];
export default mappingsTableColumns;
18 changes: 6 additions & 12 deletions hat/assets/js/apps/Iaso/domains/mappings/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,19 @@ import { getRequest, patchRequest, postRequest } from 'Iaso/libs/Api';
import { useRedirectToReplace } from 'bluesquare-components';
import MESSAGES from './messages';
import { baseUrls } from '../../constants/urls.ts';
import { useApiParams } from '../../hooks/useApiParams';

const defaultTimes = {
staleTime: 1000 * 60 * 15, // in MS
cacheTime: 1000 * 60 * 5,
};

const defaultQueryParams = {
};
export const useGetMappingVersions = params => {
const queryParams = {
order: params.order,
limit: params.pageSize,
page: params.page,
};

if (params.formId) {
queryParams['form_id'] = params.formId;
params['form_id'] = params.formId;
}

const queryString = new URLSearchParams(queryParams);

const queryString = new URLSearchParams(useApiParams(params, defaultQueryParams));
return useSnackQuery({
queryKey: ['mappingversions', params],
queryFn: () =>
Expand Down Expand Up @@ -79,7 +73,7 @@ export const useGetMappingVersionDetail = mappingVersionId => {

export const useApplyUpdate = () => {
return useSnackMutation({
mutationFn: ({mappingVersionId, payload}) => {
mutationFn: ({ mappingVersionId, payload }) => {
return patchRequest(
`/api/mappingversions/${mappingVersionId}/`,
payload,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { UseQueryResult } from 'react-query';
import { getRequest } from '../../../../libs/Api';
import { useSnackQuery } from '../../../../libs/apiHooks';
import { Form } from '../../../forms/types/forms';
import { useApiParams } from '../../../../hooks/useApiParams';

const getForms = params => {
const queryString = new URLSearchParams(params).toString();
return getRequest(`/api/forms/?${queryString}`).then((value) => {
return value.forms.map(f => {
return {
value: f.id,
label: f.name
}
})
});
};

export const tableDefaults = {
order: 'updated_at',
limit: 50,
page: 1,
};

type ValueLabel = {
value: string;
label: string;
};
export const useGetFormsDropdownOptions = (
params,
defaults = tableDefaults,
): UseQueryResult<ValueLabel[], Error> => {
const safeParams = useApiParams(params, defaults);
if (safeParams?.accountId) {
delete safeParams.accountId;
}
return useSnackQuery({
queryKey: ['form', 'options', safeParams],
queryFn: () => getForms({ ...safeParams }),
options: {
staleTime: 60000,
cacheTime: 60000,
keepPreviousData: true,
},
});
};
62 changes: 37 additions & 25 deletions hat/assets/js/apps/Iaso/domains/mappings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import {
useSafeIntl,
} from 'bluesquare-components';
import TopBar from '../../components/nav/TopBarComponent';
import mappingsTableColumns from './config';
import { mappingsTableColumns } from './config';
import CreateMappingVersionDialogComponent from './components/CreateMappingVersionDialogComponent';
import { baseUrls } from '../../constants/urls.ts';
import { useGetMappingVersions } from './hooks.js';
import MESSAGES from './messages';
import { useParamsObject } from '../../routing/hooks/useParamsObject';
import * as Permission from '../../utils/permissions.ts';
import { DisplayIfUserHasPerm } from '../../components/DisplayIfUserHasPerm';
import { Filters } from './components/Filter';

const baseUrl = baseUrls.mappings;

Expand All @@ -35,32 +38,41 @@ const Mappings = props => {
return (
<>
{mappingVersionsResults.isFetching && <LoadingSpinner />}

<TopBar title={formatMessage(MESSAGES.dhis2Mappings)} />
<Box className={classes.containerFullHeightNoTabPadded}>
<Table
data={mappingVersionsResults?.data?.mapping_versions || []}
pages={pages}
defaultSorted={[
{ id: 'form_version__form__name', desc: true },
{ id: 'form_version__version_id', desc: true },
{ id: 'mapping__mapping_type', desc: true },
]}
columns={mappingsTableColumns(formatMessage)}
count={count}
baseUrl={baseUrl}
params={params}
redirectTo={(_key, newParams) => {
redirectToReplace(baseUrl, newParams);
}}
/>
<Grid
container
justifyContent="flex-end"
alignItems="center"
className={classes.marginTop}
>
<CreateMappingVersionDialogComponent />
</Grid>
<Filters params={params} />
<Box mt={4}>
<Grid container spacing={2} justifyContent="flex-end">
<CreateMappingVersionDialogComponent />
</Grid>
</Box>
<Box className={classes.containerFullHeightNoTabPadded}>
<Table
data={
mappingVersionsResults?.data?.mapping_versions || []
}
pages={pages}
defaultSorted={[
{ id: 'form_version__form__name', desc: true },
{ id: 'form_version__version_id', desc: true },
{ id: 'mapping__mapping_type', desc: true },
]}
columns={mappingsTableColumns(formatMessage)}
count={count}
baseUrl={baseUrl}
params={params}
redirectTo={(_key, newParams) => {
redirectToReplace(baseUrl, newParams);
}}
/>
<Grid
container
justifyContent="flex-end"
alignItems="center"
className={classes.marginTop}
></Grid>
</Box>
</Box>
</>
);
Expand Down
Loading

0 comments on commit a40dae1

Please sign in to comment.