From fbc880d741de398f6a1da7e7cb77cd73246c0d3f Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 5 Jan 2024 01:25:55 +0100 Subject: [PATCH 1/4] CM-404: move individual to another group --- src/components/GroupChangeDialog.js | 62 +++++++++++++++++++++++ src/components/GroupIndividualSearcher.js | 46 ++++++++++++++++- src/pickers/GroupPicker.js | 60 ++++++++++++++++++++++ src/translations/en.json | 18 ++++++- 4 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 src/components/GroupChangeDialog.js create mode 100644 src/pickers/GroupPicker.js diff --git a/src/components/GroupChangeDialog.js b/src/components/GroupChangeDialog.js new file mode 100644 index 0000000..56df643 --- /dev/null +++ b/src/components/GroupChangeDialog.js @@ -0,0 +1,62 @@ +import React, { useState } from 'react'; +import { injectIntl } from 'react-intl'; + +import { withTheme, withStyles } from '@material-ui/core/styles'; +import { + Button, Dialog, DialogActions, DialogContent, DialogTitle, +} from '@material-ui/core'; +import { useTranslations, useModulesManager } from '@openimis/fe-core'; +import GroupPicker from '../pickers/GroupPicker'; + +const styles = (theme) => ({ + primaryButton: theme.dialog.primaryButton, + secondaryButton: theme.dialog.secondaryButton, +}); + +function GroupChangeDialog({ + classes, + confirmState, + onClose, + onConfirm, + groupIndividual, +}) { + const modulesManager = useModulesManager(); + const { formatMessage, formatMessageWithValues } = useTranslations('individual', modulesManager); + const [groupToBeChanged, setGroupToBeChanged] = useState(null); + + const handleConfirm = (groupToBeChanged) => { + onConfirm(groupToBeChanged); + onClose(); + }; + + return ( + + + {formatMessageWithValues('groupChangeDialog.confirmTitle', { + firstName: groupIndividual?.individual?.firstName, lastName: groupIndividual?.individual?.lastName, + })} + + + + + + + + + + ); +} + +export default injectIntl(withTheme(withStyles(styles)(GroupChangeDialog))); diff --git a/src/components/GroupIndividualSearcher.js b/src/components/GroupIndividualSearcher.js index 1111671..de8cbde 100644 --- a/src/components/GroupIndividualSearcher.js +++ b/src/components/GroupIndividualSearcher.js @@ -19,6 +19,7 @@ import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip, } from '@material-ui/core'; import EditIcon from '@material-ui/icons/Edit'; +import GroupIcon from '@material-ui/icons/Group'; import DeleteIcon from '@material-ui/icons/Delete'; import { clearGroupIndividualExport, @@ -29,13 +30,14 @@ import { } from '../actions'; import { DEFAULT_PAGE_SIZE, - EMPTY_STRING, + EMPTY_STRING, GROUP_INDIVIDUAL_ROLES, RIGHT_GROUP_INDIVIDUAL_DELETE, RIGHT_GROUP_INDIVIDUAL_UPDATE, ROWS_PER_PAGE_OPTIONS, } from '../constants'; import GroupIndividualFilter from './GroupIndividualFilter'; import GroupIndividualRolePicker from '../pickers/GroupIndividualRolePicker'; +import GroupChangeDialog from './GroupChangeDialog'; function GroupIndividualSearcher({ intl, @@ -68,6 +70,8 @@ function GroupIndividualSearcher({ const prevSubmittingMutationRef = useRef(); const [updatedGroupIndividuals, setUpdatedGroupIndividuals] = useState([]); const [refetch, setRefetch] = useState(null); + const [isChangeGroupModalOpen, setIsChangeGroupModalOpen] = useState(false); + const [groupIndividualToGroupChange, setGroupIndividualToGroupChange] = useState(null); function groupIndividualUpdatePageUrl(groupIndividual) { return `${modulesManager.getRef('individual.route.individual')}/${groupIndividual.individual?.id}`; @@ -124,6 +128,7 @@ function GroupIndividualSearcher({ 'individual.lastName', 'individual.dob', 'groupIndividual.individual.role', + 'emptyLabel', ]; if (rights.includes(RIGHT_GROUP_INDIVIDUAL_UPDATE)) { headers.push('emptyLabel'); @@ -161,6 +166,11 @@ function GroupIndividualSearcher({ } }; + const handleGroupChange = (groupIndividual) => { + setIsChangeGroupModalOpen(true); + setGroupIndividualToGroupChange(groupIndividual); + }; + const isRowUpdated = (groupIndividual) => ( updatedGroupIndividuals.some((item) => item.id === groupIndividual.id)); @@ -168,6 +178,22 @@ function GroupIndividualSearcher({ const isRowDisabled = (_, groupIndividual) => isRowDeleted(groupIndividual) || isRowUpdated(groupIndividual); + const onChangeGroupConfirm = (groupToBeChanged) => { + const updateIndividual = { + ...groupIndividualToGroupChange, + group: groupToBeChanged, + role: GROUP_INDIVIDUAL_ROLES.RECIPIENT, + }; + updateGroupIndividual( + updateIndividual, + formatMessageWithValues(intl, 'individual', 'individual.groupChange.confirm.message', { + individualId: updateIndividual?.individual?.id, + groupId: groupToBeChanged?.id, + }), + ); + setRefetch(groupToBeChanged?.id); + }; + const itemFormatters = () => { const formatters = [ (groupIndividual) => groupIndividual.individual.firstName, @@ -185,6 +211,18 @@ function GroupIndividualSearcher({ onChange={(role) => handleRoleOnChange(groupIndividual, role)} /> ) : groupIndividual.role), + (groupIndividual) => (rights.includes(RIGHT_GROUP_INDIVIDUAL_UPDATE) ? ( + ( + + handleGroupChange(groupIndividual)} + disabled={isRowDeleted(groupIndividual)} + > + + + + ) + ) : null), ]; if (rights.includes(RIGHT_GROUP_INDIVIDUAL_UPDATE)) { formatters.push((groupIndividual) => ( @@ -266,6 +304,12 @@ function GroupIndividualSearcher({ return (
+ setIsChangeGroupModalOpen(false)} + onConfirm={onChangeGroupConfirm} + groupIndividual={groupIndividualToGroupChange} + /> state.individual.fetchingGroups); + const fetchedGroups = useSelector((state) => state.individual.fetchedGroups); + const errorGroups = useSelector((state) => state.individual.errorGroups); + const groups = useSelector((state) => state.individual.groups); + const [group, setGroup] = useState(null); + + useEffect(() => { + if (!fetchingGroups && !fetchedGroups) { + dispatch(fetchGroups({})); + } + }, []); + + const groupLabel = (option) => option.id; + + const getGroupsWithoutCurrentGroup = (options) => options.filter( + (option) => option?.id !== groupIndividual?.group?.id, + ); + + const handleChange = (group) => { + onChange(group); + setGroup(group); + }; + + return ( + null} + /> + ); +} + +export default GroupPicker; diff --git a/src/translations/en.json b/src/translations/en.json index 965b594..cb8abc2 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -43,7 +43,12 @@ }, "any": "ANY", "ok": "ok", - "individualChangelog.label": "Change Log" + "individualChangelog.label": "Change Log", + "groupChange": { + "confirm": { + "message": "Individual {individualId} moved to Group {groupId}" + } + } }, "individualHistory": { "pageTitle": "Individual {firstName} {lastName}", @@ -130,5 +135,14 @@ "groupIndividualRolePicker.RECIPIENT": "RECIPIENT", "groupIndividualRolePicker": "Role" }, - "groupChangelog.label": "Change Log" + "groupChangelog.label": "Change Log", + "groupPicker": { + "label": "Select a group." + }, + "groupChangeDialog": { + "confirmTitle": "Move {firstName} {lastName} to a different group." + }, + "confirm": "Confirm", + "cancel": "Cancel", + "changeGroupButtonTooltip": "Move to another group." } \ No newline at end of file From 6ba80d91850137f213cb0f605564270ff0bc1632 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 8 Jan 2024 13:15:59 +0100 Subject: [PATCH 2/4] CM-405: add individual id --- .../tasks/GroupIndividualUpdateTasks.js | 16 ++++++++++++++++ src/index.js | 13 ++++++++++++- src/translations/en.json | 9 ++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/components/tasks/GroupIndividualUpdateTasks.js diff --git a/src/components/tasks/GroupIndividualUpdateTasks.js b/src/components/tasks/GroupIndividualUpdateTasks.js new file mode 100644 index 0000000..fbe63ef --- /dev/null +++ b/src/components/tasks/GroupIndividualUpdateTasks.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { FormattedMessage } from '@openimis/fe-core'; + +const GroupIndividualUpdateTaskTableHeaders = () => [ + , + , + , +]; + +const GroupIndividualUpdateTaskItemFormatters = () => [ + (groupIndividual) => groupIndividual?.group ?? groupIndividual?.group_id, + (groupIndividual) => groupIndividual?.individual, + (groupIndividual) => groupIndividual?.role, +]; + +export { GroupIndividualUpdateTaskTableHeaders, GroupIndividualUpdateTaskItemFormatters }; diff --git a/src/index.js b/src/index.js index 0f850da..c49f64f 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,10 @@ import { } from './components/tasks/IndividualUpdateTasks'; import GroupHistorySearcher from './components/GroupHistorySearcher'; import { GroupChangelogTabLabel, GroupChangelogTabPanel } from './components/GroupChangelogTab'; +import { + GroupIndividualUpdateTaskItemFormatters, + GroupIndividualUpdateTaskTableHeaders +} from "./components/tasks/GroupIndividualUpdateTasks"; const ROUTE_INDIVIDUALS = 'individuals'; const ROUTE_INDIVIDUAL = 'individuals/individual'; @@ -77,7 +81,14 @@ const DEFAULT_CONFIG = { tableHeaders: IndividualUpdateTaskTableHeaders, itemFormatters: IndividualUpdateTaskItemFormatters, taskSource: ['IndividualService'], - }], + }, + { + text: , + tableHeaders: GroupIndividualUpdateTaskTableHeaders, + itemFormatters: GroupIndividualUpdateTaskItemFormatters, + taskSource: ['GroupIndividualService'], + }, + ], }; export const IndividualModule = (cfg) => ({ ...DEFAULT_CONFIG, ...cfg }); diff --git a/src/translations/en.json b/src/translations/en.json index cb8abc2..b1af43c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -125,12 +125,19 @@ }, "groupIndividual": { "individual": { - "role": "Role" + "role": "Role", + "groupId": "Group ID", + "individualId": "Individual ID" }, "update": { "label": "Update Individual", "mutationLabel":"Update Individual {id}" }, + "tasks": { + "update": { + "title": "Group Update Tasks" + } + }, "groupIndividualRolePicker.HEAD": "HEAD", "groupIndividualRolePicker.RECIPIENT": "RECIPIENT", "groupIndividualRolePicker": "Role" From 9d94825bef7948cb4e651a846d80161f76596489 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 8 Jan 2024 19:21:23 +0100 Subject: [PATCH 3/4] CM-405: fix label in group tasks --- src/components/tasks/GroupIndividualUpdateTasks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/tasks/GroupIndividualUpdateTasks.js b/src/components/tasks/GroupIndividualUpdateTasks.js index fbe63ef..4c92007 100644 --- a/src/components/tasks/GroupIndividualUpdateTasks.js +++ b/src/components/tasks/GroupIndividualUpdateTasks.js @@ -9,7 +9,7 @@ const GroupIndividualUpdateTaskTableHeaders = () => [ const GroupIndividualUpdateTaskItemFormatters = () => [ (groupIndividual) => groupIndividual?.group ?? groupIndividual?.group_id, - (groupIndividual) => groupIndividual?.individual, + (groupIndividual, jsonExt) => jsonExt?.individual_identity ?? groupIndividual?.individual, (groupIndividual) => groupIndividual?.role, ]; From e2f7624ea53fc2e26f2c5cc15caee3164a94daa8 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 8 Jan 2024 19:27:10 +0100 Subject: [PATCH 4/4] CM-405: fix eslint --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index c49f64f..e5fb3dc 100644 --- a/src/index.js +++ b/src/index.js @@ -28,8 +28,8 @@ import GroupHistorySearcher from './components/GroupHistorySearcher'; import { GroupChangelogTabLabel, GroupChangelogTabPanel } from './components/GroupChangelogTab'; import { GroupIndividualUpdateTaskItemFormatters, - GroupIndividualUpdateTaskTableHeaders -} from "./components/tasks/GroupIndividualUpdateTasks"; + GroupIndividualUpdateTaskTableHeaders, +} from './components/tasks/GroupIndividualUpdateTasks'; const ROUTE_INDIVIDUALS = 'individuals'; const ROUTE_INDIVIDUAL = 'individuals/individual';