Skip to content

Commit

Permalink
Merge pull request #32 from openimis/feature/CM-381
Browse files Browse the repository at this point in the history
CM-381: Create change log tab for individual
  • Loading branch information
jdolkowski authored Dec 29, 2023
2 parents a89928a + a220dad commit 387742e
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const INDIVIDUAL_FULL_PROJECTION = [
'lastName',
'dob',
'jsonExt',
'version',
];

const GROUP_INDIVIDUAL_FULL_PROJECTION = [
Expand Down Expand Up @@ -62,6 +63,11 @@ export function fetchIndividual(params) {
return graphql(payload, ACTION_TYPE.GET_INDIVIDUAL);
}

export function fetchIndividualHistory(params) {
const payload = formatPageQueryWithCount('individualHistory', params, INDIVIDUAL_FULL_PROJECTION);
return graphql(payload, ACTION_TYPE.SEARCH_INDIVIDUAL_HISTORY);
}

export function fetchGroup(params) {
const payload = formatPageQuery('group', params, GROUP_FULL_PROJECTION);
return graphql(payload, ACTION_TYPE.GET_GROUP);
Expand Down
38 changes: 38 additions & 0 deletions src/components/IndividualChangelogTab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Tab } from '@material-ui/core';
import { formatMessage, PublishedComponent } from '@openimis/fe-core';
import { INDIVIDUAL_CHANGELOG_TAB_VALUE } from '../constants';

function IndividalChangelogTabLabel({
intl, onChange, tabStyle, isSelected,
}) {
return (
<Tab
onChange={onChange}
className={tabStyle(INDIVIDUAL_CHANGELOG_TAB_VALUE)}
selected={isSelected(INDIVIDUAL_CHANGELOG_TAB_VALUE)}
value={INDIVIDUAL_CHANGELOG_TAB_VALUE}
label={formatMessage(intl, 'individual', 'individualChangelog.label')}
/>
);
}

function IndividalChangelogTabPanel({
value, individual,
}) {
return (
<PublishedComponent
pubRef="policyHolder.TabPanel"
module="individual"
index={INDIVIDUAL_CHANGELOG_TAB_VALUE}
value={value}
>
<PublishedComponent
pubRef="individual.IndividualHistorySearcher"
individualId={individual?.id}
/>
</PublishedComponent>
);
}

export { IndividalChangelogTabLabel, IndividalChangelogTabPanel };
76 changes: 76 additions & 0 deletions src/components/IndividualHistoryFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { injectIntl } from 'react-intl';
import { TextInput, PublishedComponent } from '@openimis/fe-core';
import { Grid } from '@material-ui/core';
import { withTheme, withStyles } from '@material-ui/core/styles';
import _debounce from 'lodash/debounce';
import { CONTAINS_LOOKUP, DEFAULT_DEBOUNCE_TIME, EMPTY_STRING } from '../constants';
import { defaultFilterStyles } from '../util/styles';

function IndividualHistoryFilter({
classes, filters, onChangeFilters,
}) {
const debouncedOnChangeFilters = _debounce(onChangeFilters, DEFAULT_DEBOUNCE_TIME);

const filterValue = (filterName) => filters?.[filterName]?.value;

const filterTextFieldValue = (filterName) => filters?.[filterName]?.value ?? EMPTY_STRING;

const onChangeStringFilter = (filterName, lookup = null) => (value) => {
if (lookup) {
debouncedOnChangeFilters([
{
id: filterName,
value,
filter: `${filterName}_${lookup}: "${value}"`,
},
]);
} else {
onChangeFilters([
{
id: filterName,
value,
filter: `${filterName}: "${value}"`,
},
]);
}
};

return (
<Grid container className={classes.form}>
<Grid item xs={2} className={classes.item}>
<TextInput
module="individual"
label="individualHistory.firstName"
value={filterTextFieldValue('firstName')}
onChange={onChangeStringFilter('firstName', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={2} className={classes.item}>
<TextInput
module="individual"
label="individualHistory.lastName"
value={filterTextFieldValue('lastName')}
onChange={onChangeStringFilter('lastName', CONTAINS_LOOKUP)}
/>
</Grid>
<Grid item xs={2} className={classes.item}>
<PublishedComponent
pubRef="core.DatePicker"
module="individual"
label="individualHistory.dob"
value={filterValue('dob')}
onChange={(v) => onChangeFilters([
{
id: 'dob',
value: v,
filter: `dob: "${v}"`,
},
])}
/>
</Grid>
</Grid>
);
}

export default injectIntl(withTheme(withStyles(defaultFilterStyles)(IndividualHistoryFilter)));
144 changes: 144 additions & 0 deletions src/components/IndividualHistorySearcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React from 'react';
import { injectIntl } from 'react-intl';
import {
withModulesManager,
formatMessageWithValues,
Searcher,
formatDateFromISO,
withHistory,
} from '@openimis/fe-core';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { fetchIndividualHistory } from '../actions';
import {
DEFAULT_PAGE_SIZE,
ROWS_PER_PAGE_OPTIONS,
EMPTY_STRING,
} from '../constants';
import IndividualHistoryFilter from './IndividualHistoryFilter';

function IndividualHistorySearcher({
intl,
modulesManager,
individualHistory,
fetchIndividualHistory,
individualHistoryPageInfo,
fetchingIndividualHistory,
fetchedIndividualHistory,
errorIndividualHistory,
individualHistoryTotalCount,
individualId,
}) {
const fetch = (params) => fetchIndividualHistory(params);

const headers = () => {
const headers = [
'individualHistory.firstName',
'individualHistory.lastName',
'individualHistory.dob',
'individualHistory.dateUpdated',
'individualHistory.version',
'individualHistory.jsonExt',
];
return headers;
};

const itemFormatters = () => {
const formatters = [
(individualHistory) => individualHistory.firstName,
(individualHistory) => individualHistory.lastName,
(individualHistory) => (individualHistory.dob
? formatDateFromISO(modulesManager, intl, individualHistory.dob) : EMPTY_STRING
),
(individualHistory) => (individualHistory.dateUpdated
? formatDateFromISO(modulesManager, intl, individualHistory.dateUpdated) : EMPTY_STRING
),
(individualHistory) => individualHistory.version,
(individualHistory) => individualHistory.jsonExt,
];
return formatters;
};

const rowIdentifier = (individualHistory) => individualHistory.id;

const sorts = () => [
['firstName', true],
['lastName', true],
['dob', true],
['dateUpdated', true],
['version', true],
];

const defaultFilters = () => {
const filters = {
isDeleted: {
value: false,
filter: 'isDeleted: false',
},
};
if (individualId !== null && individualId !== undefined) {
filters.individualId = {
value: individualId,
filter: `id: "${individualId}"`,
};
}
return filters;
};

const individualHistoryFilter = (props) => (
<IndividualHistoryFilter
intl={props.intl}
classes={props.classes}
filters={props.filters}
onChangeFilters={props.onChangeFilters}
/>
);

return (
<div>
<Searcher
module="individual"
FilterPane={individualHistoryFilter}
fetch={fetch}
items={individualHistory}
itemsPageInfo={individualHistoryPageInfo}
fetchingItems={fetchingIndividualHistory}
fetchedItems={fetchedIndividualHistory}
errorItems={errorIndividualHistory}
tableTitle={formatMessageWithValues(intl, 'individual', 'individualHistoryList.searcherResultsTitle', {
individualHistoryTotalCount,
})}
headers={headers}
itemFormatters={itemFormatters}
sorts={sorts}
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
defaultPageSize={DEFAULT_PAGE_SIZE}
defaultOrderBy="lastName"
rowIdentifier={rowIdentifier}
defaultFilters={defaultFilters()}
cacheFiltersKey="individualHistoryFilterChache"
resetFiltersOnUnmount
/>
</div>
);
}

const mapStateToProps = (state) => ({
fetchingIndividualHistory: state.individual.fetchingIndividualHistory,
fetchedIndividualHistory: state.individual.fetchedIndividualHistory,
errorIndividualHistory: state.individual.errorIndividualHistory,
individualHistory: state.individual.individualHistory,
individualHistoryPageInfo: state.individual.individualHistoryPageInfo,
individualHistoryTotalCount: state.individual.individualHistoryTotalCount,
});

const mapDispatchToProps = (dispatch) => bindActionCreators(
{
fetchIndividualHistory,
},
dispatch,
);

export default withHistory(
withModulesManager(injectIntl(connect(mapStateToProps, mapDispatchToProps)(IndividualHistorySearcher))),
);
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const RIGHT_GROUP_DELETE = 180004;

export const BENEFIT_PLANS_LIST_TAB_VALUE = 'BenefitPlansListTab';
export const INDIVIDUALS_LIST_TAB_VALUE = 'IndividualsListTab';
export const INDIVIDUAL_CHANGELOG_TAB_VALUE = 'IndividualChangelogTab';
export const GROUP_CHANGELOG_TAB_VALUE = 'GroupChangelogTab';
export const INDIVIDUAL_TABS_LABEL_CONTRIBUTION_KEY = 'individual.TabPanel.label';
export const INDIVIDUAL_TABS_PANEL_CONTRIBUTION_KEY = 'individual.TabPanel.panel';
Expand Down
10 changes: 8 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ import IndividualPage from './pages/IndividualPage';
import GroupsPage from './pages/GroupsPage';
import GroupPage from './pages/GroupPage';
import { IndividualsListTabLabel, IndividualsListTabPanel } from './components/IndividualsListTab';
import {
IndividalChangelogTabLabel,
IndividalChangelogTabPanel,
} from './components/IndividualChangelogTab';
import getBenefitPlansListTab from './contributions/getBenefitPlansListTab';
import GroupIndividualSearcher from './components/GroupIndividualSearcher';
import { clearIndividualExport, downloadIndividuals, fetchIndividuals } from './actions';
import IndividualHistorySearcher from './components/IndividualHistorySearcher';
import {
IndividualUpdateTaskItemFormatters,
IndividualUpdateTaskTableHeaders,
Expand Down Expand Up @@ -50,17 +55,18 @@ const DEFAULT_CONFIG = {
{ key: 'individual.actions.fetchIndividuals', ref: fetchIndividuals },
{ key: 'individual.actions.downloadIndividuals', ref: downloadIndividuals },
{ key: 'individual.actions.clearIndividualExport', ref: clearIndividualExport },
{ key: 'individual.IndividualHistorySearcher', ref: IndividualHistorySearcher },
{ key: 'individual.GroupHistorySearcher', ref: GroupHistorySearcher },
],
'individual.TabPanel.label': [
IndividualsListTabLabel,
BenefitPlansListTabLabel,
GroupChangelogTabLabel,
IndividalChangelogTabLabel,
],
'individual.TabPanel.panel': [
IndividualsListTabPanel,
BenefitPlansListTabPanel,
GroupChangelogTabPanel,
IndividalChangelogTabPanel,
],
'individual.BenefitPlansListTabLabel': [BENEFIT_PLAN_TABS_LABEL_REF_KEY],
'individual.BenefitPlansListTabPanel': [BENEFIT_PLAN_TABS_PANEL_REF_KEY],
Expand Down
37 changes: 37 additions & 0 deletions src/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const ACTION_TYPE = {
GROUP_EXPORT: 'GROUP_EXPORT',
INDIVIDUAL_EXPORT: 'INDIVIDUAL_EXPORT',
GROUP_INDIVIDUAL_EXPORT: 'GROUP_INDIVIDUAL_EXPORT',
SEARCH_INDIVIDUAL_HISTORY: 'SEARCH_INDIVIDUAL_HISTORY',
SEARCH_GROUP_HISTORY: 'SEARCH_GROUP_HISTORY',
};

Expand Down Expand Up @@ -79,6 +80,12 @@ function reducer(
groupIndividualsExport: null,
groupIndividualsExportPageInfo: {},
errorGroupIndividualsExport: null,
fetchingIndividualHistory: false,
errorIndividualHistory: null,
fetchedIndividualHistory: false,
individualHistory: [],
individualHistoryPageInfo: {},
individualHistoryTotalCount: 0,
fetchingGroupHistory: false,
errorGroupHistory: null,
fetchedGroupHistory: false,
Expand All @@ -99,6 +106,16 @@ function reducer(
individualsTotalCount: 0,
errorIndividuals: null,
};
case REQUEST(ACTION_TYPE.SEARCH_INDIVIDUAL_HISTORY):
return {
...state,
fetchingIndividualHistory: true,
fetchedIndividualHistory: false,
individualHistory: [],
individualHistoryPageInfo: {},
individualHistoryTotalCount: 0,
errorIndividualHistory: null,
};
case REQUEST(ACTION_TYPE.SEARCH_GROUP_INDIVIDUALS):
return {
...state,
Expand Down Expand Up @@ -158,6 +175,20 @@ function reducer(
individualsTotalCount: action.payload.data.individual ? action.payload.data.individual.totalCount : null,
errorIndividuals: formatGraphQLError(action.payload),
};
case SUCCESS(ACTION_TYPE.SEARCH_INDIVIDUAL_HISTORY):
return {
...state,
fetchingIndividualHistory: false,
fetchedIndividualHistory: true,
individualHistory: parseData(action.payload.data.individualHistory)?.map((individualHistory) => ({
...individualHistory,
id: decodeId(individualHistory.id),
})),
individualHistoryPageInfo: pageInfo(action.payload.data.individualHistory),
individualHistoryTotalCount: action.payload.data.individualHistory
? action.payload.data.individualHistory.totalCount : null,
errorIndividualHistory: formatGraphQLError(action.payload),
};
case SUCCESS(ACTION_TYPE.SEARCH_GROUP_INDIVIDUALS):
return {
...state,
Expand Down Expand Up @@ -243,6 +274,12 @@ function reducer(
fetchingIndividuals: false,
errorIndividuals: formatServerError(action.payload),
};
case ERROR(ACTION_TYPE.SEARCH_INDIVIDUAL_HISTORY):
return {
...state,
fetchingIndividualHistory: false,
errorIndividualHistory: formatServerError(action.payload),
};
case ERROR(ACTION_TYPE.SEARCH_GROUP_INDIVIDUALS):
return {
...state,
Expand Down
Loading

0 comments on commit 387742e

Please sign in to comment.