From 5d4e3932da7df6b657f0cee781b1fc314ab32bf3 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 27 May 2024 09:27:58 +0200 Subject: [PATCH] CM-810: add atomic group task --- src/actions.js | 126 +++++++++++++++++++++++++++++++++++++++ src/constants.js | 10 ++++ src/index.js | 14 ++++- src/reducer.js | 36 +++++++++++ src/translations/en.json | 3 +- 5 files changed, 187 insertions(+), 2 deletions(-) diff --git a/src/actions.js b/src/actions.js index b6719c9..dd7a0ff 100644 --- a/src/actions.js +++ b/src/actions.js @@ -5,6 +5,9 @@ import { formatPageQueryWithCount, formatMutation, formatGQLString, + graphqlWithVariables, + useModulesManager, + prepareMutation, } from '@openimis/fe-core'; import { ACTION_TYPE } from './reducer'; import { @@ -330,6 +333,129 @@ export function updateGroup(group, clientMutationLabel) { ); } +export function fetchPendingGroupUploads(variables) { + return graphqlWithVariables( + ` + query ( + $upload_Id: ID, $group_Id_Isnull: Boolean + ${variables.after ? ',$after: String' : ''} + ${variables.before ? ',$before: String' : ''} + ${variables.pageSize ? ',$pageSize: Int' : ''} + ${variables.isDeleted !== undefined ? ',$isDeleted: Boolean' : ''} + ) { + groupDataSource( + upload_Id: $upload_Id, group_Id_Isnull:$group_Id_Isnull, + ${variables.isDeleted !== undefined ? ',isDeleted: $isDeleted' : ''} + ${variables.before ? ',before:$before, last:$pageSize' : ''} + ${!variables.before ? ',first:$pageSize' : ''} + ${variables.after ? ',after:$after' : ''} + ) + { + totalCount + pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor} + edges + { + node + { + id, uuid, jsonExt, group { code } + + } + } + } + } + `, + variables, + ACTION_TYPE.GET_PENDING_GROUPS_UPLOAD, + ); +} + +export const formatTaskResolveGQL = (task, user, approveOrFail, additionalData) => ` + ${task?.id ? `id: "${task.id}"` : ''} + ${user && approveOrFail ? `businessStatus: "{\\"${user.id}\\": \\"${approveOrFail}\\"}"` : ''} + ${additionalData ? `additionalData: "${additionalData}"` : ''} + `; + +export function resolveTask(task, clientMutationLabel, user, approveOrFail, additionalData = null) { + const mutationType = 'resolveTask'; + const mutationInput = formatTaskResolveGQL(task, user, approveOrFail, additionalData); + const mutation = formatMutation(mutationType, mutationInput, clientMutationLabel); + const requestedDateTime = new Date(); + + const userId = user?.id; + + const mutation2 = prepareMutation( + `mutation ($clientMutationLabel:String, $clientMutationId: String, $id:UUID!, + $businessStatus: JSONString!, ${additionalData ? '$additionalData: JSONString!' : ''} + ) { + resolveTask( + input: { + clientMutationId: $clientMutationId + clientMutationLabel: $clientMutationLabel + + id: $id + businessStatus: $businessStatus + ${additionalData ? 'additionalData: $additionalData' : ''} + } + ) { + clientMutationId + internalId + } + }`, + { + id: task?.id, + businessStatus: (() => { + if (!userId) return undefined; + + switch (approveOrFail) { + case 'APPROVED': + case 'FAILED': + return JSON.stringify({ [userId]: approveOrFail }); + case 'ACCEPT': + case 'REJECT': + return JSON.stringify({ [userId]: { [approveOrFail]: additionalData } }); + default: + throw new Error('Invalid approveOrFail value'); + } + })(), + // eslint-disable-next-line max-len + additionalData: additionalData ? JSON.stringify({ entries: additionalData, decision: additionalData }) : undefined, + }, + { + id: task?.id, + businessStatus: (() => { + if (!userId) return undefined; + + switch (approveOrFail) { + case 'APPROVED': + case 'FAILED': + return JSON.stringify({ [userId]: approveOrFail }); + case 'ACCEPT': + case 'REJECT': + return JSON.stringify({ [userId]: { [approveOrFail]: additionalData } }); + default: + throw new Error('Invalid approveOrFail value'); + } + })(), + // eslint-disable-next-line max-len + additionalData: additionalData ? JSON.stringify({ entries: additionalData, decision: additionalData }) : undefined, + }, + ); + + // eslint-disable-next-line no-param-reassign + user.clientMutationId = mutation.clientMutationId; + + return graphqlWithVariables( + mutation2.operation, + { + ...mutation2.variables.input, + }, + ['TASK_MANAGEMENT_MUTATION_REQ', 'TASK_MANAGEMENT_MUTATION_RESP', 'TASK_MANAGEMENT_MUTATION_ERR'], + { + requestedDateTime, clientMutationId: mutation.clientMutationId, clientMutationLabel, userId: user.id, + }, + ); +} + export function downloadGroups(params) { const payload = ` { diff --git a/src/constants.js b/src/constants.js index 632b698..cd66c5f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -89,3 +89,13 @@ export const UPLOAD_STATUS = { }; export const INDIVIDUALS_QUANTITY_LIMIT = 15; export const PYTHON_DEFAULT_IMPORT_WORKFLOW = 'Python Import Individuals'; + +export const TASK_STATUS = { + RECEIVED: 'RECEIVED', + ACCEPTED: 'ACCEPTED', + COMPLETED: 'COMPLETED', + FAILED: 'FAILED', +}; + +export const APPROVED = 'APPROVED'; +export const FAILED = 'FAILED'; diff --git a/src/index.js b/src/index.js index ba9a610..881563b 100644 --- a/src/index.js +++ b/src/index.js @@ -41,7 +41,7 @@ import { INDIVIDUAL_LABEL, INDIVIDUAL_MODULE_NAME, RIGHT_GROUP_SEARCH, - RIGHT_INDIVIDUAL_SEARCH + RIGHT_INDIVIDUAL_SEARCH, } from './constants'; import { GroupCreateTaskItemFormatters, GroupCreateTaskTableHeaders } from './components/tasks/GroupCreateTasks'; import IndividualsUploadDialog from './components/dialogs/IndividualsUploadDialog'; @@ -53,6 +53,11 @@ import { } from './components/GroupIndividualHistoryTab'; import AdvancedCriteriaRowValue from './components/dialogs/AdvancedCriteriaRowValue'; import IndividualPicker from './pickers/IndividualPicker'; +import { + GroupUploadConfirmationPanel, + GroupUploadResolutionItemFormatters, + GroupUploadResolutionTaskTableHeaders +} from "./components/tasks/GroupImportTasks"; const ROUTE_INDIVIDUALS = 'individuals'; const ROUTE_INDIVIDUAL = 'individuals/individual'; @@ -155,6 +160,13 @@ const DEFAULT_CONFIG = { taskSource: ['CreateGroupAndMoveIndividualService'], taskCode: GROUP_LABEL, }, + { + text: , + tableHeaders: GroupUploadResolutionTaskTableHeaders, + itemFormatters: GroupUploadResolutionItemFormatters, + taskSource: ['import_group_valid_items'], + confirmationPanel: GroupUploadConfirmationPanel, + }, ], }; diff --git a/src/reducer.js b/src/reducer.js index d92c30e..6bdad31 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -41,6 +41,8 @@ export const ACTION_TYPE = { CONFIRM_ENROLLMENT: 'CONFIRM_ENROLLMENT', GET_INDIVIDUAL_UPLOAD_HISTORY: 'GET_INDIVIDUAL_UPLOAD_HISTORY', SEARCH_GROUP_INDIVIDUAL_HISTORY: 'SEARCH_GROUP_INDIVIDUAL_HISTORY', + GET_PENDING_GROUPS_UPLOAD: 'GET_PENDING_GROUPS_UPLOAD', + RESOLVE_TASK: 'TASK_MANAGEMENT_RESOLVE_TASK', }; function reducer( @@ -123,6 +125,12 @@ function reducer( groupIndividualHistory: [], groupIndividualHistoryPageInfo: {}, groupIndividualHistoryTotalCount: 0, + + pendingGroups: [], + fetchingPendingGroups: true, + fetchedPendingGroups: false, + errorPendingGroups: null, + pendingGroupsPageInfo: {}, }, action, ) { @@ -193,6 +201,26 @@ function reducer( groupHistoryTotalCount: 0, errorGroupHistory: null, }; + case REQUEST(ACTION_TYPE.GET_PENDING_GROUPS_UPLOAD): + return { + ...state, + pendingGroups: [], + fetchingPendingGroups: true, + fetchedPendingGroups: false, + errorPendingGroups: null, + }; + case SUCCESS(ACTION_TYPE.GET_PENDING_GROUPS_UPLOAD): + return { + ...state, + pendingGroups: parseData(action.payload.data.groupDataSource)?.map((i) => ({ + ...i, + id: decodeId(i.id), + })), + pendingGroupPageInfo: pageInfo(action.payload.data.groupDataSource), + fetchingPendingGroups: false, + fetchedPendingGroups: true, + errorPendingGroups: formatGraphQLError(action.payload), + }; case SUCCESS(ACTION_TYPE.SEARCH_INDIVIDUALS): return { ...state, @@ -305,6 +333,12 @@ function reducer( fetchingIndividuals: false, errorIndividuals: formatServerError(action.payload), }; + case ERROR(ACTION_TYPE.GET_PENDING_GROUPS_UPLOAD): + return { + ...state, + fetchingPendingGroups: false, + errorFieldsFromBfSchema: formatGraphQLError(action.payload), + }; case ERROR(ACTION_TYPE.SEARCH_INDIVIDUAL_HISTORY): return { ...state, @@ -593,6 +627,8 @@ function reducer( return dispatchMutationResp(state, 'updateGroup', action); case SUCCESS(ACTION_TYPE.CREATE_GROUP_AND_MOVE_INDIVIDUAL): return dispatchMutationResp(state, 'createGroupAndMoveIndividual', action); + case SUCCESS(ACTION_TYPE.RESOLVE_TASK): + return dispatchMutationResp(state, 'resolveTask', action); default: return state; } diff --git a/src/translations/en.json b/src/translations/en.json index d67254a..17100cd 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -247,5 +247,6 @@ "changeGroupButtonTooltip": "Move to another group.", "moveToNewGroup": "New Group", "createGroupFromColumns": "Create groups from column:", - "groupAggregationInfo": "To specify recipients include header 'recipient_info' with possible values: '1' - head, '0' or empty - recipient." + "groupAggregationInfo": "To specify recipients include header 'recipient_info' with possible values: '1' - head, '0' or empty - recipient.", + "validation_import_group_valid_items.tasks.title": "Import Group Valid Items" } \ No newline at end of file