diff --git a/src/actions.js b/src/actions.js index 8b8162f..7bf2e62 100644 --- a/src/actions.js +++ b/src/actions.js @@ -11,7 +11,7 @@ import { ACTION_TYPE } from './reducer'; import { CLEAR, ERROR, REQUEST, SUCCESS, } from './utils/action-type'; -import { EMPTY_STRING } from './constants'; +import { DRAFT_FORM_TYPE, EMPTY_STRING } from './constants'; const WORKER_VOUCHER_PROJECTION = (modulesManager) => [ 'id', @@ -56,12 +56,16 @@ export const GROUP_PROJECTION = (modulesManager, withWorkers = true) => [ `policyholder ${modulesManager.getProjection('policyHolder.PolicyHolderPicker.projection')}`, `groupWorkers { totalCount - ${withWorkers ? `edges { + ${ + withWorkers + ? `edges { node { isDeleted, insuree ${modulesManager.getProjection('insuree.InsureePicker.projection')}, } - }` : ''} + }` + : '' +} }`, ]; @@ -336,9 +340,7 @@ export async function fetchAllPages(dispatch, query, variables, categories) { while (hasNextPage) { try { // eslint-disable-next-line no-await-in-loop - const response = await dispatch( - graphqlWithVariables(query, { ...variables, after }), - ); + const response = await dispatch(graphqlWithVariables(query, { ...variables, after })); const data = response?.payload?.data || {}; const pageInfos = categories.map((category) => processCategoryData(category, data, allData)); @@ -414,12 +416,11 @@ export async function fetchAllAvailableWorkers(dispatch, economicUnitCode, dateR } } `; - const response = await fetchAllPages( - dispatch, - query, - { economicUnitCode, dateRange }, - ['allAvailableWorkers', 'previousWorkers', 'previousDayWorkers'], - ); + const response = await fetchAllPages(dispatch, query, { economicUnitCode, dateRange }, [ + 'allAvailableWorkers', + 'previousWorkers', + 'previousDayWorkers', + ]); return response; } @@ -679,3 +680,77 @@ export function fetchPublicVoucherDetails(voucherUuid) { { voucherUuid }, ); } + +export function fetchVoucherDraftForm(modulesManager, economicUnitCode) { + return graphqlWithVariables( + ` + query getCurrentDraft($economicUnitCode: String) { + voucherFormDraft(policyholder_Code: $economicUnitCode) { + edges { + node { + policyholder ${modulesManager.getProjection('policyHolder.PolicyHolderPicker.projection')} + workers ${modulesManager.getProjection('insuree.InsureePicker.projection')} + dateRanges { startDate endDate } + } + } + } + } + `, + { economicUnitCode }, + ); +} + +export function createOrUpdateVoucherDraftForm( + voucherAssignment, + clientMutationLabel, + typeOfForm = DRAFT_FORM_TYPE.ASSIGNMENT, +) { + const { employer, workers, dateRanges } = voucherAssignment; + const workerIds = workers ? workers.map((worker) => decodeId(worker.id)).join(', ') : EMPTY_STRING; + + const mutationInput = ` + typeOfForm: "${typeOfForm}" + economicUnitCode: "${employer.code}" + workers: [${workerIds}] + ${dateRanges ? `dateRanges: ${formatGraphQLDateRanges(dateRanges)}` : 'dateRanges: []'} + `; + + const mutation = formatMutation('createOrUpdateVoucherDraftForm', mutationInput, clientMutationLabel); + const requestedDateTime = new Date(); + + return graphql( + mutation.payload, + [ + REQUEST(ACTION_TYPE.MUTATION), + SUCCESS(ACTION_TYPE.CREATE_OR_UPDATE_ASSIGNMENT_DRAFT), + ERROR(ACTION_TYPE.MUTATION), + ], + { + actionType: ACTION_TYPE.CREATE_OR_UPDATE_ASSIGNMENT_DRAFT, + clientMutationId: mutation.clientMutationId, + clientMutationLabel, + requestedDateTime, + }, + ); +} + +export function deleteVoucherDraftForm(economicUnit, clientMutationLabel, typeOfForm = DRAFT_FORM_TYPE.ASSIGNMENT) { + const mutationInput = ` + ${`economicUnitCode: "${economicUnit.code}"`} + ${`typeOfForm: "${typeOfForm}"`} + `; + + const mutation = formatMutation('deleteVoucherDraftForm', mutationInput, clientMutationLabel); + const requestedDateTime = new Date(); + + return graphql( + mutation.payload, + [REQUEST(ACTION_TYPE.MUTATION), SUCCESS(ACTION_TYPE.DELETE_ASSIGNMENT_DRAFT), ERROR(ACTION_TYPE.MUTATION)], + { + actionType: ACTION_TYPE.DELETE_ASSIGNMENT_DRAFT, + clientMutationId: mutation.clientMutationId, + clientMutationLabel, + requestedDateTime, + }, + ); +} diff --git a/src/components/VoucherAcquirementGenericVoucher.js b/src/components/VoucherAcquirementGenericVoucher.js index 3f374a0..1c3c35a 100644 --- a/src/components/VoucherAcquirementGenericVoucher.js +++ b/src/components/VoucherAcquirementGenericVoucher.js @@ -100,7 +100,7 @@ function VoucherAcquirementGenericVoucher() { historyPush(modulesManager, history, REF_ROUTE_BILL, [billId]); dispatch( coreAlert( - formatMessage('menu.voucherAcquirementSuccess'), + formatMessage('menu.voucherAcquirement'), formatMessageWithValues('workerVoucher.VoucherAcquirementForm.genericVoucherConfirmation', { quantity: voucherAcquirement?.quantity, }), diff --git a/src/components/VoucherAcquirementSpecificWorker.js b/src/components/VoucherAcquirementSpecificWorker.js index 00ac740..1a06ac6 100644 --- a/src/components/VoucherAcquirementSpecificWorker.js +++ b/src/components/VoucherAcquirementSpecificWorker.js @@ -110,7 +110,7 @@ function VoucherAcquirementSpecificWorker() { historyPush(modulesManager, history, REF_ROUTE_BILL, [billId]); dispatch( coreAlert( - formatMessage('menu.voucherAcquirementSuccess'), + formatMessage('menu.voucherAcquirement'), formatMessage('workerVoucher.VoucherAcquirementForm.specificVoucherConfirmation'), ), ); diff --git a/src/components/VoucherAssignmentForm.js b/src/components/VoucherAssignmentForm.js index e169960..7beb520 100644 --- a/src/components/VoucherAssignmentForm.js +++ b/src/components/VoucherAssignmentForm.js @@ -1,25 +1,29 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import _ from 'lodash'; import { - Divider, Grid, Paper, Typography, Button, Tooltip, + Button, Divider, Grid, Paper, Tooltip, Typography, } from '@material-ui/core'; -import { makeStyles } from '@material-ui/styles'; import AssignmentIndIcon from '@material-ui/icons/AssignmentInd'; +import { makeStyles } from '@material-ui/styles'; import { + InfoButton, coreAlert, - useModulesManager, - useTranslations, - journalize, historyPush, useHistory, - InfoButton, + useModulesManager, + useTranslations, + parseData, } from '@openimis/fe-core'; -import { assignVouchers, voucherAssignmentValidation } from '../actions'; -import { MODULE_NAME, REF_ROUTE_WORKER_VOUCHERS, USER_ECONOMIC_UNIT_STORAGE_KEY } from '../constants'; +import { + assignVouchers, deleteVoucherDraftForm, fetchVoucherDraftForm, voucherAssignmentValidation, +} from '../actions'; +import { MODULE_NAME, REF_ROUTE_WORKER_VOUCHERS } from '../constants'; import AssignmentVoucherForm from './AssignmentVoucherForm'; import VoucherAssignmentConfirmModal from './VoucherAssignmentConfirmModal'; +import VoucherAssignmentProgressTracker from './VoucherAssignmentProgressTracker'; export const useStyles = makeStyles((theme) => ({ paper: { ...theme.paper.paper, margin: '10px 0 0 0' }, @@ -46,7 +50,6 @@ export const useStyles = makeStyles((theme) => ({ })); function VoucherAssignmentForm() { - const prevSubmittingMutationRef = useRef(); const modulesManager = useModulesManager(); const dispatch = useDispatch(); const classes = useStyles(); @@ -57,8 +60,8 @@ function VoucherAssignmentForm() { const [assignmentSummaryLoading, setAssignmentSummaryLoading] = useState(false); const [isAssignmentLoading, setIsAssignmentLoading] = useState(false); const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false); - const { mutation, submittingMutation } = useSelector((state) => state.workerVoucher); const { economicUnit } = useSelector((state) => state.policyHolder); + const prevEconomicUnitRef = useRef(); const assignmentBlocked = (voucherAssignment) => !voucherAssignment?.workers?.length || !voucherAssignment?.dateRanges?.length; @@ -93,6 +96,7 @@ function VoucherAssignmentForm() { 'Assign Vouchers', ), ); + await dispatch(deleteVoucherDraftForm(economicUnit, 'Delete Voucher Draft')); historyPush(modulesManager, history, REF_ROUTE_WORKER_VOUCHERS); dispatch( coreAlert( @@ -109,26 +113,38 @@ function VoucherAssignmentForm() { setIsConfirmationModalOpen((prevState) => !prevState); }; - useEffect(() => { - if (prevSubmittingMutationRef.current && !submittingMutation) { - dispatch(journalize(mutation)); + useEffect(async () => { + if (_.isEqual(economicUnit, prevEconomicUnitRef.current)) { + return; } - }, [submittingMutation]); - - useEffect(() => { - prevSubmittingMutationRef.current = submittingMutation; - }); - - useEffect(() => { - const storedUserEconomicUnit = localStorage.getItem(USER_ECONOMIC_UNIT_STORAGE_KEY); - if (storedUserEconomicUnit) { - const userEconomicUnit = JSON.parse(storedUserEconomicUnit); - setVoucherAssignment((prevState) => ({ - ...prevState, - employer: userEconomicUnit, - workers: [], - dateRanges: [], + + try { + const response = await dispatch(fetchVoucherDraftForm(modulesManager, economicUnit.code)); + + if (response.error) { + // eslint-disable-next-line no-console + console.error(`[ERROR]: Error while fetching voucher draft form. ${response.error}`); + } + + const voucherFormDraft = parseData(response.payload.data.voucherFormDraft)?.[0]; + + if (!voucherFormDraft) { + setVoucherAssignment(() => ({ + employer: economicUnit, + workers: [], + dateRanges: [], + })); + return; + } + + setVoucherAssignment(() => ({ + employer: voucherFormDraft.policyholder, + workers: voucherFormDraft.workers, + dateRanges: voucherFormDraft.dateRanges, })); + } catch (error) { + // eslint-disable-next-line no-console + console.error(`[ERROR]: Error during assignment init. ${error}`); } }, [setVoucherAssignment, economicUnit]); @@ -168,6 +184,8 @@ function VoucherAssignmentForm() { + + ({ + container: { + display: 'flex', + alignItems: 'center', + justifyContent: 'start', + gap: '4px', + padding: theme.spacing(1), + }, +})); + +function VoucherAssignmentProgressTracker({ voucherAssignment }) { + const prevVoucherAssignment = useRef(); + const dispatch = useDispatch(); + const classes = useStyles(); + const { showError } = useToast(); + const { formatMessage } = useTranslations(MODULE_NAME); + const [isSaving, setIsSaving] = useState(false); + + const saveAssignmentDraft = useCallback( + _debounce(async (voucherAssignment) => { + try { + await dispatch(createOrUpdateVoucherDraftForm(voucherAssignment, 'Save Assignment Draft')); + } catch (error) { + showError('[ERROR]: Error while saving the progress.'); + // eslint-disable-next-line no-console + console.error(error); + } finally { + setIsSaving(false); + } + }, DEFAULT_DEBOUNCE_TIME * 2), + [dispatch, showError], + ); + + useEffect(() => { + if ( + !_.isEqual(voucherAssignment, prevVoucherAssignment.current) + && !_.isEmpty(voucherAssignment) + ) { + setIsSaving(true); + prevVoucherAssignment.current = voucherAssignment; + saveAssignmentDraft(voucherAssignment); + } + }, [voucherAssignment, saveAssignmentDraft]); + + return ( + + {isSaving ? ( + <> + + {formatMessage('VoucherAssignmentProgressTracker.saving')} + + ) : ( + <> + + {formatMessage('VoucherAssignmentProgressTracker.upToDate')} + + )} + + ); +} + +export default VoucherAssignmentProgressTracker; diff --git a/src/constants.js b/src/constants.js index 73b275a..ded1284 100644 --- a/src/constants.js +++ b/src/constants.js @@ -112,3 +112,7 @@ export const UPLOAD_STAGE = { FILE_UPLOAD: 'FILE_UPLOAD', WORKER_UPLOAD: 'WORKER_UPLOAD', }; + +export const DRAFT_FORM_TYPE = { + ASSIGNMENT: 'ASSIGNMENT', +}; diff --git a/src/pickers/WorkerMultiplePicker.js b/src/pickers/WorkerMultiplePicker.js index bc49422..2809c39 100644 --- a/src/pickers/WorkerMultiplePicker.js +++ b/src/pickers/WorkerMultiplePicker.js @@ -136,6 +136,7 @@ function WorkerMultiplePicker({ flexDirection: 'column', gap: '8px', alignItems: 'end', + marginTop: '8px', }} >