diff --git a/src/actions.js b/src/actions.js
index f43591c..ed4457e 100644
--- a/src/actions.js
+++ b/src/actions.js
@@ -39,6 +39,7 @@ const GROUP_FULL_PROJECTION = [
'dateCreated',
'dateUpdated',
'jsonExt',
+ 'version',
];
export function fetchIndividuals(params) {
@@ -66,6 +67,11 @@ export function fetchGroup(params) {
return graphql(payload, ACTION_TYPE.GET_GROUP);
}
+export function fetchGroupHistory(params) {
+ const payload = formatPageQueryWithCount('groupHistory', params, GROUP_FULL_PROJECTION);
+ return graphql(payload, ACTION_TYPE.SEARCH_GROUP_HISTORY);
+}
+
export function deleteIndividual(individual, clientMutationLabel) {
const individualUuids = `ids: ["${individual?.id}"]`;
const mutation = formatMutation('deleteIndividual', individualUuids, clientMutationLabel);
diff --git a/src/components/GroupChangelogTab.js b/src/components/GroupChangelogTab.js
new file mode 100644
index 0000000..4a87817
--- /dev/null
+++ b/src/components/GroupChangelogTab.js
@@ -0,0 +1,38 @@
+import React from 'react';
+import { Tab } from '@material-ui/core';
+import { formatMessage, PublishedComponent } from '@openimis/fe-core';
+import { GROUP_CHANGELOG_TAB_VALUE } from '../constants';
+
+function GroupChangelogTabLabel({
+ intl, onChange, tabStyle, isSelected,
+}) {
+ return (
+
+ );
+}
+
+function GroupChangelogTabPanel({
+ value, group,
+}) {
+ return (
+
+
+
+ );
+}
+
+export { GroupChangelogTabLabel, GroupChangelogTabPanel };
diff --git a/src/components/GroupHistoryFilter.js b/src/components/GroupHistoryFilter.js
new file mode 100644
index 0000000..d814855
--- /dev/null
+++ b/src/components/GroupHistoryFilter.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import { injectIntl } from 'react-intl';
+import { TextInput } 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 GroupHistoryFilter({
+ classes, filters, onChangeFilters,
+}) {
+ const debouncedOnChangeFilters = _debounce(onChangeFilters, DEFAULT_DEBOUNCE_TIME);
+ 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 (
+
+
+
+
+
+
+
+
+ );
+}
+
+export default injectIntl(withTheme(withStyles(defaultFilterStyles)(GroupHistoryFilter)));
diff --git a/src/components/GroupHistorySearcher.js b/src/components/GroupHistorySearcher.js
new file mode 100644
index 0000000..9afe3e2
--- /dev/null
+++ b/src/components/GroupHistorySearcher.js
@@ -0,0 +1,113 @@
+import React from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { injectIntl } from 'react-intl';
+import {
+ useModulesManager,
+ useTranslations,
+ Searcher,
+ withHistory,
+ withModulesManager,
+} from '@openimis/fe-core';
+import { DEFAULT_PAGE_SIZE, EMPTY_STRING, ROWS_PER_PAGE_OPTIONS } from '../constants';
+import GroupHistoryFilter from './GroupHistoryFilter';
+import { fetchGroupHistory } from '../actions';
+
+function GroupHistorySearcher({
+ groupId,
+}) {
+ const modulesManager = useModulesManager();
+ const dispatch = useDispatch();
+ const { formatDateFromISO, formatMessageWithValues } = useTranslations('individual', modulesManager);
+
+ const fetchingGroupHistory = useSelector((state) => state.individual.fetchingGroupHistory);
+ const fetchedGroupHistory = useSelector((state) => state.individual.fetchedGroupHistory);
+ const errorGroupHistory = useSelector((state) => state.individual.errorGroupHistory);
+ const groupHistory = useSelector((state) => state.individual.groupHistory);
+ const groupHistoryPageInfo = useSelector((state) => state.individual.groupHistoryPageInfo);
+ const groupHistoryTotalCount = useSelector((state) => state.individual.groupHistoryTotalCount);
+ const fetch = (params) => dispatch(fetchGroupHistory(params));
+
+ const headers = () => [
+ 'groupHistory.id',
+ 'groupHistory.head',
+ 'groupHistory.dateUpdated',
+ 'groupHistory.version',
+ 'groupHistory.jsonExt',
+ ];
+
+ const itemFormatters = () => [
+ (groupHistory) => groupHistory.id,
+ (groupHistory) => groupHistory.head,
+ (groupHistory) => (groupHistory.dateUpdated
+ ? formatDateFromISO(groupHistory.dateUpdated) : EMPTY_STRING
+ ),
+ (groupHistory) => groupHistory.version,
+ (groupHistory) => groupHistory.jsonExt,
+ ];
+
+ const rowIdentifier = (groupHistory) => groupHistory.id;
+
+ const sorts = () => [
+ ['id', false],
+ ['head', true],
+ ['dateUpdated', true],
+ ['version', true],
+ ];
+
+ const defaultFilters = () => {
+ const filters = {
+ isDeleted: {
+ value: false,
+ filter: 'isDeleted: false',
+ },
+ };
+ if (groupId !== null && groupId !== undefined) {
+ filters.groupId = {
+ value: groupId,
+ filter: `id: "${groupId}"`,
+ };
+ }
+ return filters;
+ };
+
+ const groupHistoryFilter = (props) => (
+
+ );
+
+ return (
+
+
+
+ );
+}
+
+export default withHistory(
+ withModulesManager(injectIntl((GroupHistorySearcher))),
+);
diff --git a/src/constants.js b/src/constants.js
index 1c1b222..223655c 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -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 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';
diff --git a/src/index.js b/src/index.js
index acb7984..95b5d23 100644
--- a/src/index.js
+++ b/src/index.js
@@ -19,6 +19,8 @@ import {
IndividualUpdateTaskItemFormatters,
IndividualUpdateTaskTableHeaders,
} from './components/tasks/IndividualUpdateTasks';
+import GroupHistorySearcher from './components/GroupHistorySearcher';
+import { GroupChangelogTabLabel, GroupChangelogTabPanel } from './components/GroupChangelogTab';
const ROUTE_INDIVIDUALS = 'individuals';
const ROUTE_INDIVIDUAL = 'individuals/individual';
@@ -48,14 +50,17 @@ const DEFAULT_CONFIG = {
{ key: 'individual.actions.fetchIndividuals', ref: fetchIndividuals },
{ key: 'individual.actions.downloadIndividuals', ref: downloadIndividuals },
{ key: 'individual.actions.clearIndividualExport', ref: clearIndividualExport },
+ { key: 'individual.GroupHistorySearcher', ref: GroupHistorySearcher },
],
'individual.TabPanel.label': [
IndividualsListTabLabel,
BenefitPlansListTabLabel,
+ GroupChangelogTabLabel,
],
'individual.TabPanel.panel': [
IndividualsListTabPanel,
BenefitPlansListTabPanel,
+ GroupChangelogTabPanel,
],
'individual.BenefitPlansListTabLabel': [BENEFIT_PLAN_TABS_LABEL_REF_KEY],
'individual.BenefitPlansListTabPanel': [BENEFIT_PLAN_TABS_PANEL_REF_KEY],
diff --git a/src/reducer.js b/src/reducer.js
index 49e39f3..1895665 100644
--- a/src/reducer.js
+++ b/src/reducer.js
@@ -31,6 +31,7 @@ export const ACTION_TYPE = {
GROUP_EXPORT: 'GROUP_EXPORT',
INDIVIDUAL_EXPORT: 'INDIVIDUAL_EXPORT',
GROUP_INDIVIDUAL_EXPORT: 'GROUP_INDIVIDUAL_EXPORT',
+ SEARCH_GROUP_HISTORY: 'SEARCH_GROUP_HISTORY',
};
function reducer(
@@ -78,6 +79,12 @@ function reducer(
groupIndividualsExport: null,
groupIndividualsExportPageInfo: {},
errorGroupIndividualsExport: null,
+ fetchingGroupHistory: false,
+ errorGroupHistory: null,
+ fetchedGroupHistory: false,
+ groupHistory: [],
+ groupHistoryPageInfo: {},
+ groupHistoryTotalCount: 0,
},
action,
) {
@@ -128,6 +135,16 @@ function reducer(
group: null,
errorGroup: null,
};
+ case REQUEST(ACTION_TYPE.SEARCH_GROUP_HISTORY):
+ return {
+ ...state,
+ fetchingGroupHistory: true,
+ fetchedGroupHistory: false,
+ groupHistory: [],
+ groupHistoryPageInfo: {},
+ groupHistoryTotalCount: 0,
+ errorGroupHistory: null,
+ };
case SUCCESS(ACTION_TYPE.SEARCH_INDIVIDUALS):
return {
...state,
@@ -184,6 +201,20 @@ function reducer(
groupsTotalCount: action.payload.data.group ? action.payload.data.group.totalCount : null,
errorGroups: formatGraphQLError(action.payload),
};
+ case SUCCESS(ACTION_TYPE.SEARCH_GROUP_HISTORY):
+ return {
+ ...state,
+ fetchingGroupHistory: false,
+ fetchedGroupHistory: true,
+ groupHistory: parseData(action.payload.data.groupHistory)?.map((groupHistory) => ({
+ ...groupHistory,
+ id: decodeId(groupHistory.id),
+ })),
+ groupHistoryPageInfo: pageInfo(action.payload.data.groupHistory),
+ groupHistoryTotalCount: action.payload.data.groupHistory
+ ? action.payload.data.groupHistory.totalCount : null,
+ errorGroupHistory: formatGraphQLError(action.payload),
+ };
case SUCCESS(ACTION_TYPE.GET_INDIVIDUAL):
return {
...state,
@@ -224,6 +255,12 @@ function reducer(
fetchingGroups: false,
errorGroups: formatServerError(action.payload),
};
+ case ERROR(ACTION_TYPE.SEARCH_GROUP_HISTORY):
+ return {
+ ...state,
+ fetchingGroupHistory: false,
+ errorGroupHistory: formatServerError(action.payload),
+ };
case ERROR(ACTION_TYPE.GET_INDIVIDUAL):
return {
...state,
diff --git a/src/translations/en.json b/src/translations/en.json
index cd9f5e5..d838903 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -69,6 +69,21 @@
"mutationLabel": "Delete Group {id}"
}
},
+ "groupHistory": {
+ "pageTitle": "Group {id}",
+ "headPanelTitle": "General Information",
+ "id": "ID",
+ "head": "head",
+ "dateUpdated": "Date Updated",
+ "version": "Version",
+ "any": "ANY",
+ "ok": "ok",
+ "jsonExt": "Additional fields"
+ },
+ "groupHistoryList": {
+ "pageTitle": "Groups History",
+ "searcherResultsTitle": "{groupHistoryTotalCount} Historical Records Found"
+ },
"export": {
"label": "DOWNLOAD",
"dateCreated": "Date Created",
@@ -93,5 +108,6 @@
"groupIndividualRolePicker.HEAD": "HEAD",
"groupIndividualRolePicker.RECIPIENT": "RECIPIENT",
"groupIndividualRolePicker": "Role"
- }
+ },
+ "groupChangelog.label": "Change Log"
}
\ No newline at end of file