From 1ac979ed2ac87e57abc6fa528ccc6ee3a24fe75b Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 5 Jan 2024 09:45:41 +0100 Subject: [PATCH] CM-404: move individual to another group (#36) Co-authored-by: Jan --- 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