Skip to content

Commit

Permalink
OM-325: add assignment progress tracker (#83)
Browse files Browse the repository at this point in the history
* OM-325: add assignment progress tracker

* OM-325: sync with be

* OM-3325: fix sonar
  • Loading branch information
olewandowski1 authored Oct 30, 2024
1 parent 943957e commit d7ed8f1
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 47 deletions.
99 changes: 87 additions & 12 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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')},
}
}` : ''}
}`
: ''
}
}`,
];

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
},
);
}
2 changes: 1 addition & 1 deletion src/components/VoucherAcquirementGenericVoucher.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
Expand Down
2 changes: 1 addition & 1 deletion src/components/VoucherAcquirementSpecificWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
),
);
Expand Down
76 changes: 47 additions & 29 deletions src/components/VoucherAssignmentForm.js
Original file line number Diff line number Diff line change
@@ -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' },
Expand All @@ -46,7 +50,6 @@ export const useStyles = makeStyles((theme) => ({
}));

function VoucherAssignmentForm() {
const prevSubmittingMutationRef = useRef();
const modulesManager = useModulesManager();
const dispatch = useDispatch();
const classes = useStyles();
Expand All @@ -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;
Expand Down Expand Up @@ -93,6 +96,7 @@ function VoucherAssignmentForm() {
'Assign Vouchers',
),
);
await dispatch(deleteVoucherDraftForm(economicUnit, 'Delete Voucher Draft'));
historyPush(modulesManager, history, REF_ROUTE_WORKER_VOUCHERS);
dispatch(
coreAlert(
Expand All @@ -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]);

Expand Down Expand Up @@ -168,6 +184,8 @@ function VoucherAssignmentForm() {
</Grid>
</Grid>
<Divider />
<VoucherAssignmentProgressTracker voucherAssignment={voucherAssignment} />
<Divider />
<AssignmentVoucherForm
edited={voucherAssignment}
onEditedChange={setVoucherAssignment}
Expand Down
77 changes: 77 additions & 0 deletions src/components/VoucherAssignmentProgressTracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, {
useEffect, useRef, useState, useCallback,
} from 'react';
import { useDispatch } from 'react-redux';
import _ from 'lodash';
import _debounce from 'lodash/debounce';

import { CircularProgress, Grid, Typography } from '@material-ui/core';
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
import { makeStyles } from '@material-ui/styles';

import { useToast, useTranslations } from '@openimis/fe-core';
import { createOrUpdateVoucherDraftForm } from '../actions';
import { DEFAULT_DEBOUNCE_TIME, MODULE_NAME } from '../constants';

const useStyles = makeStyles((theme) => ({
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 (
<Grid className={classes.container}>
{isSaving ? (
<>
<CircularProgress size={16} thickness={5} />
<Typography variant="body2">{formatMessage('VoucherAssignmentProgressTracker.saving')}</Typography>
</>
) : (
<>
<CheckCircleIcon color="primary" fontSize="small" />
<Typography variant="body2">{formatMessage('VoucherAssignmentProgressTracker.upToDate')}</Typography>
</>
)}
</Grid>
);
}

export default VoucherAssignmentProgressTracker;
4 changes: 4 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ export const UPLOAD_STAGE = {
FILE_UPLOAD: 'FILE_UPLOAD',
WORKER_UPLOAD: 'WORKER_UPLOAD',
};

export const DRAFT_FORM_TYPE = {
ASSIGNMENT: 'ASSIGNMENT',
};
1 change: 1 addition & 0 deletions src/pickers/WorkerMultiplePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ function WorkerMultiplePicker({
flexDirection: 'column',
gap: '8px',
alignItems: 'end',
marginTop: '8px',
}}
>
<Autocomplete
Expand Down
2 changes: 2 additions & 0 deletions src/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const ACTION_TYPE = {
DELETE_GROUP: 'WORKER_VOUCHER_DELETE_GROUP',
CREATE_GROUP: 'WORKER_VOUCHER_CREATE_GROUP',
UPDATE_GROUP: 'WORKER_VOUCHER_UPDATE_GROUP',
CREATE_OR_UPDATE_ASSIGNMENT_DRAFT: 'WORKER_VOUCHER_CREATE_OR_UPDATE_ASSIGNMENT_DRAFT',
DELETE_ASSIGNMENT_DRAFT: 'WORKER_VOUCHER_DELETE_ASSIGNMENT_DRAFT',
};

const STORE_STATE = {
Expand Down
Loading

0 comments on commit d7ed8f1

Please sign in to comment.