Skip to content

Commit

Permalink
OM-316: add downloading file with errors, overall improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
olewandowski1 committed Oct 15, 2024
1 parent 438e8c7 commit a1ce8b3
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 22 deletions.
48 changes: 37 additions & 11 deletions src/components/UploadWorkerModal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useRef, useState } from 'react';
import { useSelector } from 'react-redux';

import {
Box,
Expand All @@ -22,7 +23,7 @@ import WarningIcon from '@material-ui/icons/Warning';
import { makeStyles } from '@material-ui/styles';

import { useModulesManager, useTranslations } from '@openimis/fe-core';
import { MODULE_NAME, UPLOAD_STAGE } from '../constants';
import { MODULE_NAME, RIGHT_WORKER_UPLOAD, UPLOAD_STAGE } from '../constants';
import { useUploadWorkerContext } from '../context/UploadWorkerContext';

const useStyles = makeStyles((theme) => ({
Expand Down Expand Up @@ -92,6 +93,7 @@ function UploadWorkerModal({ open, onClose }) {
const classes = useStyles();
const modulesManager = useModulesManager();
const { formatMessage, formatMessageWithValues } = useTranslations(MODULE_NAME, modulesManager);
const rights = useSelector((state) => state.core?.user?.i_user?.rights ?? []);
const fileInputRef = useRef(null);
const [fileUploadError, setFileUploadError] = useState(null);
const maxSizeInMB = 10;
Expand All @@ -108,12 +110,17 @@ function UploadWorkerModal({ open, onClose }) {
onWorkersUpload,
uploadSummary,
resetFile,
workersWithError,
downloadWorkersWithError,
} = useUploadWorkerContext();

const isFileUploadStage = uploadStage === UPLOAD_STAGE.FILE_UPLOAD;
const isWorkerUploadStage = uploadStage === UPLOAD_STAGE.WORKER_UPLOAD;
const isWorkerWithErrorAvailable = Object.keys(workersWithError).length > 0;

const handleFileChange = (e) => {
setFileUploadError(null);

const uploadedFile = e.target.files[0];

if (!uploadedFile) {
Expand All @@ -138,6 +145,10 @@ function UploadWorkerModal({ open, onClose }) {
fileInputRef.current.value = null;
};

if (!rights.includes(RIGHT_WORKER_UPLOAD)) {
return null;
}

return (
<Dialog open={open} onClose={onClose} disableBackdropClick>
<DialogTitle>{formatMessage('UploadWorkerModal.dialogTitle')}</DialogTitle>
Expand Down Expand Up @@ -252,16 +263,31 @@ function UploadWorkerModal({ open, onClose }) {
</>
)}
{isWorkerUploadStage && (
<Button
onClick={() => {
resetFile();
onClose();
}}
disabled={isUploading}
className={classes.secondaryButton}
>
{formatMessage('UploadWorkerModal.close')}
</Button>
<>
<Button
onClick={() => {
resetFile();
onClose();
}}
disabled={isUploading}
className={classes.secondaryButton}
>
{formatMessage('UploadWorkerModal.close')}
</Button>
{isWorkerWithErrorAvailable && (
<Button
onClick={() => {
downloadWorkersWithError();
resetFile();
onClose();
}}
disabled={isUploading}
className={classes.errorButton}
>
{formatMessage('UploadWorkerModal.downloadWorkersWithError')}
</Button>
)}
</>
)}
</DialogActions>
</Dialog>
Expand Down
7 changes: 5 additions & 2 deletions src/components/WorkerSearcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from '../constants';
import WorkerFilter from './WorkerFilter';
import { ACTION_TYPE } from '../reducer';
import { useUploadWorkerContext } from '../context/UploadWorkerContext';

const WORKER_SEARCHER_ACTION_CONTRIBUTION_KEY = 'workerVoucher.WorkerSearcherAction.select';

Expand Down Expand Up @@ -72,6 +73,8 @@ function WorkerSearcher({ downloadWorkers, fetchWorkers: fetchWorkersAction, cle
const [workerToDelete, setWorkerToDelete] = useState(null);
const [exportFileFormat, setExportFileFormat] = useState(EXPORT_FILE_FORMATS.csv);

const { validationSuccess, validationWarning } = useUploadWorkerContext();

const exportConfiguration = {
exportFields: ['chf_id', 'last_name', 'other_names'],
additionalExportFields: {
Expand Down Expand Up @@ -225,10 +228,10 @@ function WorkerSearcher({ downloadWorkers, fetchWorkers: fetchWorkersAction, cle
};

useEffect(() => {
if (queryParams.length) {
if (queryParams.length && (economicUnit || validationSuccess || validationWarning)) {
fetchWorkers(queryParams);
}
}, [economicUnit, queryParams]);
}, [economicUnit, queryParams, validationSuccess, validationWarning]);

useEffect(() => {
if (prevSubmittingMutationRef.current && !submittingMutation) {
Expand Down
5 changes: 4 additions & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const EMPLOYER_RIGHT_SEARCH = 203000;
export const VOUCHER_PRICE_MANAGEMENT_RIGHT = 205001;
export const INSPECTOR_RIGHT = 204005;
export const ADMIN_RIGHT = 204004;
export const RIGHT_WORKER_UPLOAD = 101101;
export const RIGHT_WORKER_UPLOAD = 101102;
export const RIGHT_WORKER_SEARCH = 101101;
export const RIGHT_WORKER_ADD = 101102;
export const RIGHT_WORKER_EDIT = 101103;
Expand Down Expand Up @@ -99,6 +99,9 @@ export const WORKER_IMPORT_PLANS = [
},
];

// There are 2 worker upload stages. Depending on the stage, the UI will show different fields/buttons.
// 1. FILE_UPLOAD: When the user uploads a file
// 2. WORKER_UPLOAD: When the user confirms the upload (after validation workers are uploaded)
export const UPLOAD_STAGE = {
FILE_UPLOAD: 'FILE_UPLOAD',
WORKER_UPLOAD: 'WORKER_UPLOAD',
Expand Down
57 changes: 52 additions & 5 deletions src/context/UploadWorkerContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import React, {
} from 'react';
import { useSelector } from 'react-redux';

import { baseApiUrl, useTranslations } from '@openimis/fe-core';
import { EMPTY_STRING, MODULE_NAME, UPLOAD_STAGE } from '../constants';
import { baseApiUrl, useTranslations, openBlob } from '@openimis/fe-core';
import {
EMPTY_OBJECT, EMPTY_STRING, MODULE_NAME, UPLOAD_STAGE,
} from '../constants';

const UploadWorkerContext = createContext();

Expand All @@ -18,6 +20,7 @@ export function UploadWorkerProvider({ children }) {
const [validationError, setValidationError] = useState(EMPTY_STRING);
const [validationSuccess, setValidationSuccess] = useState(EMPTY_STRING);
const [validationWarning, setValidationWarning] = useState(EMPTY_STRING);
const [workersWithError, setWorkersWithError] = useState(EMPTY_OBJECT);
const [uploadSummary, setUploadSummary] = useState({
affectedRows: 0,
totalNumberOfRecordsInFile: 0,
Expand Down Expand Up @@ -58,8 +61,24 @@ export function UploadWorkerProvider({ children }) {

const data = await response.json();

console.log(response);
console.log(data);
setUploadSummary({
affectedRows: data.summary?.affected_rows || 0,
totalNumberOfRecordsInFile: data.summary?.total_number_of_records_in_file || 0,
skippedRows: data.summary?.skipped_items || 0,
});

if (!data.success) {
setValidationError(formatMessage('UploadWorkerModal.workerUploadError'));
return;
}

if (!!data?.summary?.skipped_items && !!Object.keys(data?.error).length) {
setWorkersWithError(data.error);
setValidationWarning(formatMessage('UploadWorkerModal.workerUploadWarning'));
return;
}

setValidationSuccess(formatMessage('UploadWorkerModal.workerUploadSuccess'));
} catch (error) {
setValidationError(formatMessage('UploadWorkerModal.workerUploadError'));
} finally {
Expand All @@ -68,6 +87,21 @@ export function UploadWorkerProvider({ children }) {
}
};

const downloadWorkersWithError = async () => {
const baseUrl = new URL(`${window.location.origin}${baseApiUrl}/worker_voucher/download_worker_upload_file/`);
baseUrl.searchParams.append('economic_unit_code', economicUnit.code);
baseUrl.searchParams.append('filename', file.name);

try {
const response = await fetch(baseUrl);
const blob = await response.blob();
return openBlob(blob, `errors_${file.name}`, file.type);
} catch (error) {
// eslint-disable-next-line no-console
throw new Error(`[UPLOAD_WORKER_CONTEXT]: Upload failed. ${error}`);
}
};

const onWorkersUpload = async () => {
setUploadStage(UPLOAD_STAGE.WORKER_UPLOAD);

Expand All @@ -81,6 +115,7 @@ export function UploadWorkerProvider({ children }) {
setValidationSuccess(EMPTY_STRING);
setValidationWarning(EMPTY_STRING);
setUploadStage(UPLOAD_STAGE.FILE_UPLOAD);
setWorkersWithError(EMPTY_OBJECT);
setUploadSummary({
affectedRows: 0,
totalNumberOfRecordsInFile: 0,
Expand All @@ -102,6 +137,7 @@ export function UploadWorkerProvider({ children }) {
validationWarning,
uploadSummary,
uploadStage,
workersWithError,
onWorkersUpload,
setFile,
setIsUploading,
Expand All @@ -111,8 +147,19 @@ export function UploadWorkerProvider({ children }) {
onFileUpload,
setUploadSummary,
resetFile,
downloadWorkersWithError,
}),
[file, isUploading, uploadStage, uploadSummary, isUploaded, validationError, validationSuccess, validationWarning],
[
file,
isUploading,
workersWithError,
uploadStage,
uploadSummary,
isUploaded,
validationError,
validationSuccess,
validationWarning,
],
);

return <UploadWorkerContext.Provider value={memoizedContextValue}>{children}</UploadWorkerContext.Provider>;
Expand Down
7 changes: 4 additions & 3 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,12 @@
"workerVoucher.UploadWorkerModal.affectedRows": "Affected Rows",
"workerVoucher.UploadWorkerModal.totalNumberOfRecordsInFile": "Total Number of Records in File",
"workerVoucher.UploadWorkerModal.skippedRows": "Skipped Rows",
"workerVoucher.UploadWorkerModal.fileRequired": "File is required",
"workerVoucher.UploadWorkerModal.fileSizeError": "File size should be less than {fileSize} MB",
"workerVoucher.UploadWorkerModal.fileRequired": "File is required. Please upload a file.",
"workerVoucher.UploadWorkerModal.fileSizeError": "File size should be less than {fileSize} MB. Please upload a smaller file.",
"workerVoucher.UploadWorkerModal.workerUploadSuccess": "Workers have been successfully uploaded. You can close this dialog.",
"workerVoucher.UploadWorkerModal.workerUploadError": "Failed to upload workers. Please ensure that the file is in the correct format and try again.",
"workerVoucher.UploadWorkerModal.workerUploadError": "Failed to upload workers. Please ensure that the content of the file is correct and try again.",
"workerVoucher.UploadWorkerModal.workerUploadWarning": "Some workers have been skipped. You can download the list of skipped workers by clicking the button below.",
"workerVoucher.UploadWorkerModal.downloadWorkersWithError": "Download Errors and close",
"workerVoucher.WorkerSearcher.selection": "Selected {count} workers",
"workerVoucher.WorkerSearcherSelectActions.delete": "Delete Selected Workers",
"workerVoucher.WorkerSearcherSelectActions.dialog.title": "Are you sure you want to proceed?",
Expand Down

0 comments on commit a1ce8b3

Please sign in to comment.