Files
pedwfrontend/components/elements/fields/renderFileUpload.js
T

449 lines
19 KiB
JavaScript

import { JSONPath as jsonpath } from "jsonpath-plus";
import React, { useMemo, useState } from "react";
import useTranslation from "next-translate/useTranslation";
import Link from "next/link";
import Dropzone from "react-dropzone";
import {
deleteBlob,
getFilesFromBlobHashed,
uploadSingleFile,
getFilesFromBlobproxy
} from "../../../actions/services/documentService";
import { bytesToSize, getThumbnailIconByExtension } from "../../utils";
import {
getDocumentTypePrefix,
getThumbnailIconByMimeType,
validateUploadFilename
} from "../helpers/fileUploadHelpers";
const baseStyle = {
display: "flex",
flexDirection: "column",
alignItems: "center",
padding: "20px",
borderWidth: 2,
borderRadius: 2,
borderColor: "#eeeeee",
borderStyle: "dashed",
backgroundColor: "#fafafa",
color: "#bdbdbd",
transition: "border .3s ease-in-out"
};
const activeStyle = {
borderColor: "#2196f3"
};
const acceptStyle = {
borderColor: "#00e676"
};
const rejectStyle = {
borderColor: "#ff1744"
};
export const RenderFileUpload = (field) => {
let files = field.input.value;
let { t } = useTranslation();
const style = useMemo(
() => ({
...baseStyle
// ...(isDragActive ? activeStyle : {}),
// ...(isDragAccept ? acceptStyle : {}),
// ...(isDragReject ? rejectStyle : {}),
}),
[]
// [isDragActive, isDragReject, isDragAccept]
);
const [filesList, setFilesList] = useState([]);
const thumbs = filesList.map((file) => (
<div key={file.name} className="govuk-!-margin-bottom-5 fileThumb">
<img src={file.preview} alt={file.name} width="50" /> -{" "}
<span className="govuk-body">
{file.name} ({file.size / 1024}kb)
</span>
</div>
));
const filelistObj = field.fileList || {};
const blobList = jsonpath({
path: "$..[?(@ && @.documentType=='" + field.documentTypeCode + "')]",
json: filelistObj,
eval: true
});
//const blobList = filelistObj;
const deleteThisBlob = async (
containerName,
blobName,
deleteblobhash,
getblobshash,
casefolderID
) => {
//console.log(containerName, blobName, deleteblobhash);
deleteBlob(containerName, blobName, deleteblobhash, casefolderID)
.then((data) => data)
.then(() => {
getFilesFromBlobHashed(
containerName,
getblobshash,
casefolderID
).then((newfilelist) => field.setFilesForAppeal(newfilelist));
});
};
const removeFile = (file) => {
const newFilesArr = files.filter((user) => user.name != file.name);
files == newFilesArr;
field.input.onChange(newFilesArr);
};
function removeEmptyObjects(arr) {
return arr.filter((obj) => Object.keys(obj).length > 0);
}
const [errorMessage, setErrorMessage] = useState(null);
const [rejectedFiles, setRejectedFiles] = useState([]); // Store rejected files
const [completedUploadFiles, setCompletedUploadFiles] = useState(false);
const [uploadCountMessage, setUploadCountMessage] = useState("");
const [totalUploadFiles, setTotalUploadFiles] = useState(0);
const handleDropRejected = (fileRejections) => {
const errors = fileRejections
.map(({ file, errors }) => {
return errors.map((error) => {
if (error.code === "file-too-large") {
return `${file.name} ${t(
"newappeal:new-appeal-fileupload-file-error-filesize-label"
)}`;
}
if (error.code === "file-invalid-type") {
return `${file.name} ${t(
"newappeal:new-appeal-fileupload-file-error-invalid-type-label"
)}`;
}
if (error.code === "filename-invalid-chars") {
return (
error.message ||
`${file.name} ${t(
"newappeal:new-appeal-fileupload-file-error-invalid-filename-label"
)}`
);
}
return `${file.name} ${t(
"newappeal:new-appeal-fileupload-file-error-invalid-label"
)}`;
});
})
.flat(); // Flatten the errors array
setRejectedFiles(errors); // Store the error messages for rejected files
};
// Handle accepted files (clear error message when files are accepted)
const handleDropAccepted = () => {
setErrorMessage(null); // Clear any previous error messages when files are accepted
setRejectedFiles([]); // Clear any previously rejected file errorsss
};
return (
<>
<Dropzone
key={field.input.name + "_key"}
maxSize={50 * 1024 * 1024}
accept={{
"application/pdf": [".pdf"],
"application/msword": [".doc"],
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
[".docx"],
"image/tiff": [".tif", ".tiff"],
"image/jpeg": [".jpg", ".jpeg"],
"image/png": [".png"],
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
[".xlsx"]
}}
validator={(file) => validateUploadFilename(file, t)}
onDrop={(acceptedFiles, fileRejections, e) => {
// build + display rejected messages (ALWAYS, even if some accepted)
if (fileRejections?.length) {
handleDropRejected(fileRejections);
} else {
// only clear rejections when nothing was rejected on this drop
setRejectedFiles([]);
setErrorMessage(null);
}
// if nothing accepted, stop (prevents showing only "uploading" when all invalid)
if (!acceptedFiles || acceptedFiles.length === 0) {
setCompletedUploadFiles(true); // optional: depends on your UX
setUploadCountMessage(0);
setTotalUploadFiles(0);
return;
}
// continue with your existing upload logic for accepted files
setCompletedUploadFiles(false);
const renamedAcceptedFiles = acceptedFiles.map(
(file) =>
new File(
[file],
`${getDocumentTypePrefix(field.documentTypeCode)}_-_${
file.name
}`,
{ type: file.type }
)
);
field.input.onChange(renamedAcceptedFiles);
if (typeof field.setFileCount === "function") {
field.setFileCount(
(field.uploadCount || 0) +
renamedAcceptedFiles.length
);
}
setUploadCountMessage(0);
setTotalUploadFiles(renamedAcceptedFiles.length);
uploadSingleFile(
renamedAcceptedFiles,
field.containerID,
field.ticketnumber,
{
onChunkComplete: ({ cumulativeUploaded }) => {
setUploadCountMessage(cumulativeUploaded);
}
}
)
.then((data) => {
let buildAppealFilesArray = [];
let values = field.form.appealForm.values;
let filesUploadObj = renamedAcceptedFiles;
for (let key in filesUploadObj) {
typeof filesUploadObj[key].name !==
"undefined" &&
buildAppealFilesArray.push({
name: filesUploadObj[key].name,
size: filesUploadObj[key].size
});
}
function removeDuplicates(arr, key) {
const seen = new Set();
return arr.filter((item) => {
const value = item[key];
if (seen.has(value)) return false;
seen.add(value);
return true;
});
}
buildAppealFilesArray = values.hasOwnProperty(
"filesList"
)
? buildAppealFilesArray.concat(values.filesList)
: buildAppealFilesArray;
buildAppealFilesArray = removeDuplicates(
buildAppealFilesArray,
"name"
);
Object.assign(values, {
filesList: buildAppealFilesArray
});
// keep your existing server-side invalid handling
if (data.invalidFiles?.length > 0) {
setRejectedFiles((prev) => [
...prev,
...data.invalidFiles
]);
setUploadCountMessage(
renamedAcceptedFiles.length -
data.invalidFiles.length
);
}
return getFilesFromBlobproxy(
field.containerID,
field.ticketnumber
);
})
.then((data) => {
field.setFilesForAppeal(data);
setCompletedUploadFiles(true);
});
}}
>
{({ getRootProps, getInputProps }) => (
<>
<section className="container">
<div {...getRootProps({ style })}>
{" "}
<input id={field.id} {...getInputProps()} />
<div className="govuk-!-font-size-14">
{t(
"newappeal:new-appeal-fileupload-drop-label"
)}{" "}
(Max 50 MB)
</div>
<em className="govuk-!-font-size-14">
(
{t(
"newappeal:new-appeal-fileupload-file-list-label"
)}
)
</em>
</div>
{/* <aside>{thumbs}</aside> */}
</section>
</>
)}
</Dropzone>
{rejectedFiles.length > 0 && (
<div
style={{ color: "red", marginTop: "10px" }}
className="govuk-body govuk-!-font-size-14 "
>
<ul>
{rejectedFiles.map((error, index) => (
<li key={index}>{error}</li>
))}
</ul>
</div>
)}
{totalUploadFiles > 0 && completedUploadFiles == false && (
<div className="govuk-body govuk-!-font-size-14">
{t("home:uploading-files-label", {
number:
totalUploadFiles > 0
? `${uploadCountMessage} of ${totalUploadFiles}`
: uploadCountMessage
})}
</div>
)}
{completedUploadFiles > 0 && uploadCountMessage >= 0 && (
<div className="govuk-body govuk-!-font-size-14">
{t("home:completed-uploading-files-label", {
number: uploadCountMessage
})}
</div>
)}
{field.meta.touched && field.meta.error && (
<span className="error">{field.meta.error}</span>
)}
{files && Array.isArray(files) && (
<>
{/* {(files = removeEmptyObjects(files))} */}
{!completedUploadFiles &&
files.map(
(file, i) =>
typeof file.name != "undefined" && (
<div
key={file.name}
className="govuk-!-margin-bottom-5 fileThumb"
>
{" "}
{/* <span
className="govuk-summary-list__actionLink watched_link govuk-!-margin-right-5 govuk-!-text-align-left"
onClick={() => removeFile(file)}
>
&minus;
</span> */}
<img
src={getThumbnailIconByMimeType(
file.type
)}
alt={file.name}
width="50"
/>
<span className="govuk-body govuk-!-font-size-14 govuk-!-padding-left-5">
{file.name} (
{bytesToSize(file.size)}) <br />
<div id={file.name + "_" + i}>
{completedUploadFiles ? (
<p className="govuk-body govuk-!-font-size-14">
Upload complete
</p>
) : (
<p className="govuk-body textloading govuk-!-font-size-14">
{t(
"home:uploading-files-progress-label"
)}{" "}
</p>
)}
</div>
</span>
</div>
)
)}
</>
)}
{blobList.length > 0 && (
<h4>{t("newappeal:previously-added-files")}</h4>
)}
{blobList.map(
(blob, i) =>
blob.documentType == field.documentTypeCode && (
<div
key={blob.name + "_" + i}
className="govuk-!-margin-bottom-5 fileThumb govuk-!-margin-top-5"
>
<div key={"deleteLnk" + i} className="">
<span
className="govuk-summary-list__actionLink watched_link govuk-!-margin-right-5 govuk-!-text-align-left"
onClick={() => {
setUploadCountMessage(
uploadCountMessage - 1
);
deleteThisBlob(
field.containerID,
blob.name,
blob.hasheddeletepath,
blob.hashgetblobs,
field.ticketnumber
);
}}
title={t("home:remove-this-file-label")}
>
&minus;
</span>
</div>
<img
src={getThumbnailIconByExtension(blob.name)}
alt={blob.name}
width="50"
/>
<Link
key={"lnk" + i}
scroll={false}
href={
"/api/file/downloadblob?container=" +
field.containerID +
"&casefolderID=" +
encodeURIComponent(field.ticketnumber) +
"&blobname=" +
encodeURIComponent(blob.name) +
blob.hashedfilepath
}
className="govuk-body govuk-!-font-size-14 govuk-!-padding-left-5 govuk-link"
>
{blob.name} ({bytesToSize(blob.contentLength)})
</Link>
</div>
)
)}{" "}
</>
);
};