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