From 676f3f9bcb54ed2e42e4663d77a05e8a55afced6 Mon Sep 17 00:00:00 2001 From: sniedzielski <52816247+sniedzielski@users.noreply.github.com> Date: Wed, 29 May 2024 14:04:35 +0200 Subject: [PATCH] CM-421: added possibility to add new group, fixing move to other group picker (#83) --- src/actions.js | 26 ++++++++++- src/components/GroupHeadPanel.js | 4 +- src/components/GroupTabPanel.js | 2 + src/constants.js | 1 + src/index.js | 4 +- src/pages/GroupPage.js | 43 ++++++++++++------- src/pages/GroupsPage.js | 27 ++++++++++-- .../GroupIndividualRecipientTypePicker.js | 1 - src/pickers/GroupPicker.js | 20 ++++++--- src/pickers/IndividualPicker.js | 2 +- src/reducer.js | 3 ++ src/translations/en.json | 3 ++ 12 files changed, 104 insertions(+), 32 deletions(-) diff --git a/src/actions.js b/src/actions.js index b6956a9..95b9417 100644 --- a/src/actions.js +++ b/src/actions.js @@ -12,7 +12,9 @@ import { ACTION_TYPE } from './reducer'; import { CLEAR, ERROR, REQUEST, SET, SUCCESS, } from './util/action-type'; -import {ACCEPT, APPROVED, FAILED, REJECT} from "./constants"; +import { + ACCEPT, APPROVED, FAILED, REJECT, +} from './constants'; const WORKFLOWS_FULL_PROJECTION = () => [ 'name', @@ -245,6 +247,13 @@ function formatGroupGQL(group, groupIndividualId = null) { ${groupIndividualId ? `groupIndividualId: "${groupIndividualId}"` : ''}`; } +function formatCreateGroupGQL(group) { + return ` + ${group?.code ? `code: "${group.code}"` : ''} + ${'individualsData: []'} + `; +} + function formatIndividualGQL(individual) { return ` ${individual?.id ? `id: "${individual.id}"` : ''} @@ -370,6 +379,21 @@ export function updateGroup(group, clientMutationLabel) { ); } +export function createGroup(group, clientMutationLabel) { + const mutation = formatMutation('createGroup', formatCreateGroupGQL(group), clientMutationLabel); + const requestedDateTime = new Date(); + return graphql( + mutation.payload, + [REQUEST(ACTION_TYPE.MUTATION), SUCCESS(ACTION_TYPE.CREATE_GROUP), ERROR(ACTION_TYPE.MUTATION)], + { + actionType: ACTION_TYPE.CREATE_GROUP, + clientMutationId: mutation.clientMutationId, + clientMutationLabel, + requestedDateTime, + }, + ); +} + export function fetchPendingGroupUploads(variables) { return graphqlWithVariables( ` diff --git a/src/components/GroupHeadPanel.js b/src/components/GroupHeadPanel.js index 056d647..11a0b5d 100644 --- a/src/components/GroupHeadPanel.js +++ b/src/components/GroupHeadPanel.js @@ -21,7 +21,7 @@ const styles = (theme) => ({ class GroupHeadPanel extends FormPanel { render() { const { - edited, classes, mandatoryFieldsEmpty, + edited, classes, mandatoryFieldsEmpty, readOnly, } = this.props; const group = { ...edited }; return ( @@ -55,7 +55,7 @@ class GroupHeadPanel extends FormPanel { this.updateAttribute('code', v)} diff --git a/src/components/GroupTabPanel.js b/src/components/GroupTabPanel.js index 423511e..b03de0b 100644 --- a/src/components/GroupTabPanel.js +++ b/src/components/GroupTabPanel.js @@ -41,6 +41,8 @@ function GroupTabPanel({ groupIndividualIds, groupId, }) { + if (!groupId) return null; + const [activeTab, setActiveTab] = useState(individual ? BENEFIT_PLANS_LIST_TAB_VALUE : INDIVIDUALS_LIST_TAB_VALUE); const isSelected = (tab) => tab === activeTab; diff --git a/src/constants.js b/src/constants.js index d7f05d1..4d45d8e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -39,6 +39,7 @@ export const BENEFIT_PLAN_TABS_LABEL_CONTRIBUTION_KEY = 'individual.BenefitPlans export const BENEFIT_PLAN_TABS_PANEL_CONTRIBUTION_KEY = 'individual.BenefitPlansListTabPanel'; export const TASK_CONTRIBUTION_KEY = 'tasksManagement.tasks'; export const BENEFITS_CONTRIBUTION_KEY = 'payroll.benefitConsumptionPayrollSearcher'; +export const GROUP_ROUTE_GROUP = 'individual.route.group'; export const BENEFICIARY_STATUS = { POTENTIAL: 'POTENTIAL', diff --git a/src/index.js b/src/index.js index f0c9a48..8ab00e8 100644 --- a/src/index.js +++ b/src/index.js @@ -56,8 +56,8 @@ import IndividualPicker from './pickers/IndividualPicker'; import { GroupUploadConfirmationPanel, GroupUploadResolutionItemFormatters, - GroupUploadResolutionTaskTableHeaders -} from "./components/tasks/GroupImportTasks"; + GroupUploadResolutionTaskTableHeaders, +} from './components/tasks/GroupImportTasks'; import EnrollmentGroupPage from './pages/EnrollmentGroupPage'; import GroupMenu from './components/dialogs/GroupMenu'; diff --git a/src/pages/GroupPage.js b/src/pages/GroupPage.js index ce7a4a2..683651e 100644 --- a/src/pages/GroupPage.js +++ b/src/pages/GroupPage.js @@ -18,6 +18,7 @@ import DeleteIcon from '@material-ui/icons/Delete'; import { RIGHT_GROUP_CREATE, RIGHT_GROUP_SEARCH } from '../constants'; import { fetchGroup, deleteGroup, updateGroup, clearGroup, createGroupAndMoveIndividual, + createGroup, } from '../actions'; import GroupHeadPanel from '../components/GroupHeadPanel'; import { ACTION_TYPE } from '../reducer'; @@ -37,6 +38,7 @@ function GroupPage({ group, fetchGroup, deleteGroup, + createGroup, updateGroup, coreConfirm, clearConfirm, @@ -108,18 +110,25 @@ function GroupPage({ const handleSave = () => { setReadOnly(true); - if (editedGroup?.id) { - updateGroup( - editedGroup, - formatMessageWithValues(intl, 'individual', 'group.update.mutationLabel', { - id: group?.id, - }), - ); - } else if (editedGroupIndividual?.id) { - createGroupAndMoveIndividual( + if (groupUuid) { + if (editedGroup?.id) { + updateGroup( + editedGroup, + formatMessageWithValues(intl, 'individual', 'group.update.mutationLabel', { + id: group?.id, + }), + ); + } else if (editedGroupIndividual?.id) { + createGroupAndMoveIndividual( + editedGroup, + editedGroupIndividual.id, + formatMessageWithValues(intl, 'individual', 'group.createGroupAndMoveIndividual.mutationLabel'), + ); + } + } else { + createGroup( editedGroup, - editedGroupIndividual.id, - formatMessageWithValues(intl, 'individual', 'group.createGroupAndMoveIndividual.mutationLabel'), + formatMessageWithValues(intl, 'socialProtection', 'group.create.mutationLabel', titleParams(editedGroup)), ); } }; @@ -141,7 +150,10 @@ function GroupPage({ ); }; - const canAdd = () => rights.includes(RIGHT_GROUP_CREATE) && editedGroupIndividual && !readOnly; + const canAdd = () => { + if (groupUuid) return rights.includes(RIGHT_GROUP_CREATE) && editedGroupIndividual && !readOnly; + return rights.includes(RIGHT_GROUP_CREATE) && !!editedGroup?.code; + }; const actions = [ !!group && { @@ -165,8 +177,8 @@ function GroupPage({ onEditedChanged={setEditedGroup} back={back} mandatoryFieldsEmpty={isMandatoryFieldsEmpty} - canSave={canSave} - save={groupUuid ? handleSave : null} + canSave={groupUuid ? canSave : canAdd} + save={handleSave} HeadPanel={GroupHeadPanel} Panels={[GroupTabPanel]} rights={rights} @@ -176,7 +188,7 @@ function GroupPage({ add={canAdd() ? handleSave : null} setEditedGroupIndividual={setEditedGroupIndividual} editedGroupIndividual={editedGroupIndividual} - readOnly={readOnly} + readOnly={!!groupUuid} groupIndividualIds={groupIndividualIds} groupId={groupUuid} /> @@ -201,6 +213,7 @@ const mapStateToProps = (state, props) => ({ const mapDispatchToProps = (dispatch) => bindActionCreators({ fetchGroup, deleteGroup, + createGroup, updateGroup, clearGroup, createGroupAndMoveIndividual, diff --git a/src/pages/GroupsPage.js b/src/pages/GroupsPage.js index 8388f60..093048c 100644 --- a/src/pages/GroupsPage.js +++ b/src/pages/GroupsPage.js @@ -1,9 +1,13 @@ import React from 'react'; -import { Helmet, withModulesManager, formatMessage } from '@openimis/fe-core'; +import { + Helmet, withModulesManager, withTooltip, formatMessage, historyPush, +} from '@openimis/fe-core'; import { injectIntl } from 'react-intl'; import { withTheme, withStyles } from '@material-ui/core/styles'; import { connect } from 'react-redux'; -import { RIGHT_GROUP_SEARCH } from '../constants'; +import { Fab } from '@material-ui/core'; +import AddIcon from '@material-ui/icons/Add'; +import { GROUP_ROUTE_GROUP, RIGHT_GROUP_CREATE, RIGHT_GROUP_SEARCH } from '../constants'; import GroupSearcher from '../components/GroupSearcher'; const styles = (theme) => ({ @@ -12,13 +16,30 @@ const styles = (theme) => ({ }); function GroupsPage(props) { - const { intl, classes, rights } = props; + const { + intl, classes, modulesManager, history, rights, + } = props; + + const onAdd = () => historyPush( + modulesManager, + history, + GROUP_ROUTE_GROUP, + ); return ( rights.includes(RIGHT_GROUP_SEARCH) && (
+ {rights.includes(RIGHT_GROUP_CREATE) + && withTooltip( +
+ + + +
, + formatMessage(intl, 'individual', 'createButton.tooltip'), + )}
) ); diff --git a/src/pickers/GroupIndividualRecipientTypePicker.js b/src/pickers/GroupIndividualRecipientTypePicker.js index f57dda6..761f63f 100644 --- a/src/pickers/GroupIndividualRecipientTypePicker.js +++ b/src/pickers/GroupIndividualRecipientTypePicker.js @@ -8,7 +8,6 @@ function GroupIndividualRecipientTypePicker(props) { const { required, readOnly, onChange, value, nullLabel, withLabel, } = props; - console.log(value) return ( state.individual.errorGroups); const groups = useSelector((state) => state.individual.groups); const [group, setGroup] = useState(null); + // eslint-disable-next-line no-unused-vars + const [currentString, setCurrentString] = useState(''); + const [filters, setFilters] = useState(['isDeleted: false']); useEffect(() => { - if (!fetchingGroups && !fetchedGroups) { - dispatch(fetchGroups({})); - } - }, []); + dispatch(fetchGroups(filters)); + }, [filters]); - const groupLabel = (option) => option.id; + const groupLabel = (option) => option.code; const getGroupsWithoutCurrentGroup = (options) => options.filter( - (option) => option?.id !== groupIndividual?.group?.id, + (option) => option?.code !== groupIndividual?.group?.code, ); const handleChange = (group) => { @@ -48,11 +49,16 @@ function GroupPicker(props) { withPlaceholder={withPlaceholder} options={getGroupsWithoutCurrentGroup(groups)} isLoading={fetchingGroups} + setCurrentString={setCurrentString} isFetched={fetchedGroups} value={group} getOptionLabel={groupLabel} onChange={handleChange} - onInputChange={() => null} + onInputChange={ + (search) => { + if (search !== undefined) setFilters([`code_Icontains: "${search}"`, 'isDeleted: false']); + } + } /> ); } diff --git a/src/pickers/IndividualPicker.js b/src/pickers/IndividualPicker.js index 0e961e6..45e8b34 100644 --- a/src/pickers/IndividualPicker.js +++ b/src/pickers/IndividualPicker.js @@ -4,7 +4,7 @@ import { TextField, Tooltip } from '@material-ui/core'; import { Autocomplete, useModulesManager, useTranslations, useGraphqlQuery, - decodeId + decodeId, } from '@openimis/fe-core'; import { INDIVIDUALS_QUANTITY_LIMIT } from '../constants'; diff --git a/src/reducer.js b/src/reducer.js index ff317a2..4e901c3 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -29,6 +29,7 @@ export const ACTION_TYPE = { UPDATE_INDIVIDUAL: 'INDIVIDUAL_UPDATE_INDIVIDUAL', UPDATE_GROUP_INDIVIDUAL: 'GROUP_INDIVIDUAL_UPDATE_GROUP_INDIVIDUAL', UPDATE_GROUP: 'GROUP_UPDATE_GROUP', + CREATE_GROUP: 'CREATE_GROUP', CREATE_GROUP_AND_MOVE_INDIVIDUAL: 'CREATE_GROUP_AND_MOVE_INDIVIDUAL', GROUP_EXPORT: 'GROUP_EXPORT', INDIVIDUAL_EXPORT: 'INDIVIDUAL_EXPORT', @@ -654,6 +655,8 @@ function reducer( return dispatchMutationResp(state, 'deleteGroup', action); case SUCCESS(ACTION_TYPE.UPDATE_GROUP): return dispatchMutationResp(state, 'updateGroup', action); + case SUCCESS(ACTION_TYPE.CREATE_GROUP): + return dispatchMutationResp(state, 'createGroup', action); case SUCCESS(ACTION_TYPE.CREATE_GROUP_AND_MOVE_INDIVIDUAL): return dispatchMutationResp(state, 'createGroupAndMoveIndividual', action); case SUCCESS(ACTION_TYPE.RESOLVE_TASK): diff --git a/src/translations/en.json b/src/translations/en.json index e44d857..8eaf31d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -154,6 +154,9 @@ "id": "ID", "head": "Head", "code": "Code", + "create": { + "mutationLabel": "Created Group {id}" + }, "noHeadSpecified": "head not specified", "individual.firstName": "Individual First Name", "individual.lastName": "Individual Last Name",