diff --git a/actions/azurestorage.js b/actions/azurestorage.js index b41984a9..33cc4475 100644 --- a/actions/azurestorage.js +++ b/actions/azurestorage.js @@ -1321,6 +1321,92 @@ export const getRepsBlobs = async (containerName) => { return blobObj; }; +export const getRepsFilesBlobs = async ( + containerName, + casefolderID, + filenamePrefix +) => { + const containerToken = await createContainerSas(containerName); + const sasUrl = `${STORAGE_PATH}/${containerName}?${containerToken}`; + const containerClient = new ContainerClient(sasUrl); + + //conLog"getBlobs " + sasUrl); + + //console.log(containerClient.listBlobsFlat()); + + const blobObj = []; + for await (const blob of containerClient.listBlobsFlat({ + prefix: casefolderID + "/" + filenamePrefix + "/files/", + })) { + let blobDocumentType = blob.name + .split("/")[2] + .slice(0, blob.name.split("/")[2].indexOf("_")); + + console.log(blob.name); + + blobObj.push({ + "name": blob.name.split("/")[3], + "path": blob.name, + "documentType": getDocumentTypeFromFilename( + blob.name.split("/")[3] + ), + "versionId": blob.versionId, + "isCurrentVersion": blob.isCurrentVersion, + "contentLength": blob.properties.contentLength, + "filenameprefix": filenamePrefix, + "filepath": + "/api/file/downloadblob?container=" + + containerName + + "&casefolderID=" + + blob.name.split("/")[0] + + "/" + + blob.name.split("/")[1] + + "&blobname=" + + blob.name.split("/")[3], + "hashedfilepath": hashAPIPath( + "/api/file/downloadblob?container=" + + containerName + + "&casefolderID=" + + blob.name.split("/")[0] + + "/" + + blob.name.split("/")[1] + + "&blobname=" + + blob.name + ), + "deletepath": + "/api/file/deleteblob?container=" + + containerName + + "&casefolderID=" + + blob.name.split("/")[0] + + "/" + + blob.name.split("/")[1] + + "&blobname=" + + blob.name.split("/")[3], + "hasheddeletepath": hashAPIPath( + "/api/file/deleteblob?container=" + + containerName + + "&casefolderID=" + + blob.name.split("/")[0] + + "/" + + blob.name.split("/")[1] + + "&blobname=" + + blob.name.split("/")[3] + ), + "hashgetblobs": hashAPIPath( + "/api/file/getbloblist?container=" + + containerName + + "&casefolderID=" + + blob.name.split("/")[0] + + "/" + + blob.name.split("/")[1] + ), + }); + } + //console.log("blobObj:", blobObj); + + return blobObj; +}; + export const createQueueSas = async (queueName) => { // Get environment variables const account = process.env.AZURE_STORAGE_ACCOUNT_NAME; diff --git a/actions/index.js b/actions/index.js index 2513ca08..a8c0c1f2 100644 --- a/actions/index.js +++ b/actions/index.js @@ -1762,6 +1762,31 @@ export const deleteBlob = async ( } }; +export const deleteRepBlob = async ( + containerName, + blobName, + deleteblobhash, + casefolderID, + filenamePrefix +) => { + try { + const res = await axios.get( + "/api/file/deleteblob?container=" + + containerName + + "&casefolderID=" + + casefolderID + + "/" + + filenamePrefix + + "&blobname=" + + blobName + + deleteblobhash + ); + return res.data; + } catch (error) { + consoleLogger(error); + } +}; + export const downloadBlob = (containerName, blobName) => { return axios .get( diff --git a/components/case/representation/index.js b/components/case/representation/index.js index b79aba93..5abedbfb 100644 --- a/components/case/representation/index.js +++ b/components/case/representation/index.js @@ -59,6 +59,7 @@ let MakeRepresentation = (props) => { setShowQuestionnaireSection, showQuestionnaireSection, initialValues, + setCurrentReference, } = props; const formObj = props.props.form; let setCaseQueryObj = props[currentType] || {}; @@ -413,6 +414,7 @@ let MakeRepresentation = (props) => { updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} savingStatus={savingStatus} + setCurrentReference={setCurrentReference} /> ); } else { @@ -442,6 +444,7 @@ let MakeRepresentation = (props) => { updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} savingStatus={savingStatus} + setCurrentReference={setCurrentReference} />; break; case "appellant": @@ -467,6 +470,7 @@ let MakeRepresentation = (props) => { updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} savingStatus={savingStatus} + setCurrentReference={setCurrentReference} /> ); break; @@ -502,6 +506,7 @@ let MakeRepresentation = (props) => { updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} savingStatus={savingStatus} + setCurrentReference={setCurrentReference} /> ); break; @@ -528,6 +533,7 @@ let MakeRepresentation = (props) => { updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} savingStatus={savingStatus} + setCurrentReference={setCurrentReference} /> ); break; @@ -554,6 +560,7 @@ let MakeRepresentation = (props) => { updateRepresentation={updateRepresentation} setSavingStatus={setSavingStatus} savingStatus={savingStatus} + setCurrentReference={setCurrentReference} /> ); break; @@ -575,6 +582,7 @@ let MakeRepresentation = (props) => { props={props} setRepFileName={setRepFileName} updateRepresentation={updateRepresentation} + setCurrentReference={setCurrentReference} /> ); break; @@ -596,6 +604,7 @@ let MakeRepresentation = (props) => { caseReference={caseReference} setRepFileName={setRepFileName} updateRepresentation={updateRepresentation} + setCurrentReference={setCurrentReference} /> ); } diff --git a/components/case/representation/representationAppellant.js b/components/case/representation/representationAppellant.js index c9fa83df..9af76345 100644 --- a/components/case/representation/representationAppellant.js +++ b/components/case/representation/representationAppellant.js @@ -12,10 +12,11 @@ import { } from "./representationElements"; import { useDropzone } from "react-dropzone"; -// import { -// setRepresentationCapacity, -// setRepresentationSubmit, -// } from "../../../store/currentView/action"; +import { + setRepresentationCapacity, + setRepresentationSubmit, + setFilesForRepresentations, +} from "../../../store/currentView/action"; const RepAppellant = (props) => { let { t } = useTranslation(); @@ -32,6 +33,7 @@ const RepAppellant = (props) => { updateRepresentation, setSavingStatus, savingStatus, + setFilesForRepresentations, } = props; const { acceptedFiles, getRootProps, getInputProps } = useDropzone(); @@ -170,51 +172,36 @@ const RepAppellant = (props) => { )} ticketnumber={props.props.caseReference} hint={"sdsd"} - fileList={[]} - setFilesForRepresentation={[]} + fileList={ + props.currentView.caseReference.hasOwnProperty( + "repDetails" + ) + ? props.currentView.caseReference.repDetails.hasOwnProperty( + "filesList" + ) && + props.currentView + .caseReference.repDetails + .filesList.length > 0 + ? props.currentView + .caseReference + .repDetails.filesList + : [] + : [] + } + setFilesForRepresentations={ + setFilesForRepresentations + } containerID={ props.props.props.accountDetails .containerID } filenamePrefix={props.formObj} + props={props.props.props} + setCurrentReference={ + props.setCurrentReference + } /> - {props.currentView.caseReference.hasOwnProperty( - "repDetails" - ) ? ( - props.currentView.caseReference.repDetails.hasOwnProperty( - "filesList" - ) && - props.currentView.caseReference.repDetails - .filesList.length > 0 ? ( -
- {filterFilesList.map((blob, i) => ( -
- {blob.name} - - - {blob.name} ( - {bytesToSize(blob.size)} - ) - -
- ))} -
- ) : ( - "" - ) - ) : ( - "" - )} {" "} @@ -296,10 +283,16 @@ const RepAppellant = (props) => { //const mapStateToProps = (state, props) => {}; -const mapDispatchToProps = (dispatch) => ({ - updateField: (form, field, newValue) => - dispatch(change(form, field, newValue)), -}); +const mapDispatchToProps = (dispatch) => { + return { + updateField: (form, field, newValue) => + dispatch(change(form, field, newValue)), + + setFilesForRepresentations: (fileList) => { + dispatch(setFilesForRepresentations(fileList)); + }, + }; +}; export default connect( null, diff --git a/components/case/representation/representationCapacitySelection.js b/components/case/representation/representationCapacitySelection.js index 1e396958..25b5df15 100644 --- a/components/case/representation/representationCapacitySelection.js +++ b/components/case/representation/representationCapacitySelection.js @@ -17,6 +17,7 @@ let RepCapacitySelection = (props) => { casesObj, validate, handleSubmit, + setCurrentReference, } = props; const { acceptedFiles, getRootProps, getInputProps } = useDropzone(); const files = acceptedFiles.map((file) => ( @@ -37,6 +38,7 @@ let RepCapacitySelection = (props) => { searchResultsObj={props.props.searchResultsObj} myRepresentations={props.props.myRepresentations} props={props} + setCurrentReference={setCurrentReference} />{" "}
diff --git a/components/case/representation/representationCompleteSubmit.js b/components/case/representation/representationCompleteSubmit.js index daba364f..0a6f4a89 100644 --- a/components/case/representation/representationCompleteSubmit.js +++ b/components/case/representation/representationCompleteSubmit.js @@ -141,14 +141,15 @@ const RepCompleteSubmit = (props) => { let buildAppealFilesArray = []; - let filesUploadObj = - formObj.representationForm.values.representationDocuments; + let filesUploadObj = props.currentView.fileList; + + // formObj.representationForm.values.representationDocuments; for (let key in filesUploadObj) { typeof filesUploadObj[key].name != "undefined" && buildAppealFilesArray.push({ "name": filesUploadObj[key].name, - "size": filesUploadObj[key].size, + "size": filesUploadObj[key].contentLength, }); } @@ -163,8 +164,6 @@ const RepCompleteSubmit = (props) => { // ? formObj.representationForm.values.filesList // : []; - console.log(buildAppealFilesArray); - let arrUniq = [ ...new Map(buildAppealFilesArray.map((v) => [v.name, v])).values(), ]; @@ -177,9 +176,7 @@ const RepCompleteSubmit = (props) => { // (obj1, i, arr) => arr.findIndex((obj2) => obj2.name === obj1.name) === i // ); - console.log(arrUniq, buildAppealFilesArray); - - arrUniq = arrUniq.concat(filesUploadObj); + //arrUniq = arrUniq.concat(filesUploadObj); function removeNullObjects(arr) { if (arr.length === 0) { diff --git a/components/case/representation/representationElements.js b/components/case/representation/representationElements.js index 773c0e29..767f7ab3 100644 --- a/components/case/representation/representationElements.js +++ b/components/case/representation/representationElements.js @@ -8,6 +8,14 @@ import "react-datepicker/dist/react-datepicker.css"; import Dropzone from "react-dropzone"; import "react-quill-new/dist/quill.snow.css"; import { Field } from "redux-form"; +import { + uploadSingleFile, + deleteBlob, + deleteRepBlob, + getFilesFromBlobHashed, + getFilesFromBlobproxy, +} from "../../../actions"; + const ReactQuill = dynamic(() => import("react-quill-new"), { ssr: false }); import { @@ -911,9 +919,9 @@ export const RenderFileUpload = (field) => { const filelistObj = field.fileList || {}; - console.log("----", filelistObj.length); + //let blobList = filelistObj; - let blobList = filelistObj; + let blobList = field.props.currentView.fileList; function removeNullObjects(arr) { if (arr.length === 0) { @@ -939,9 +947,9 @@ export const RenderFileUpload = (field) => { ); } - blobList = removeEmptyObjects(blobList); - blobList = removeObjectsWithUndefinedKey(blobList, "name"); - blobList = removeNullObjects(blobList); + //blobList = removeEmptyObjects(blobList); + //blobList = removeObjectsWithUndefinedKey(blobList, "name"); + //blobList = removeNullObjects(blobList); let buildAppealFilesArray = []; @@ -978,22 +986,60 @@ export const RenderFileUpload = (field) => { blobName, deleteblobhash, getblobshash, - casefolderID + casefolderID, + filenamePrefix ) => { - deleteBlob(containerName, blobName, deleteblobhash, casefolderID) + deleteRepBlob( + containerName, + blobName, + deleteblobhash, + casefolderID, + filenamePrefix + ) .then((data) => data) .then(() => { getFilesFromBlobHashed( containerName, getblobshash, - casefolderID - ).then((newfilelist) => field.setFilesForAppeal(newfilelist)); + casefolderID + "/" + filenamePrefix + ).then((newfilelist) => + field.setFilesForRepresentations(newfilelist.value[0]) + ); }); }; + const [rejectedFiles, setRejectedFiles] = useState([]); // Store rejected files + const [completedUploadFiles, setCompletedUploadFiles] = useState(false); + const [uploadCountMessage, setUploadCountMessage] = useState(""); + + const handleDropRejected = (fileRejections) => { + const errors = fileRejections + .map(({ file, errors }) => { + return errors.map((error) => { + if (error.code === "file-too-large") { + return `${file.name} is too large. Please upload a smaller file.`; + } + if (error.code === "file-invalid-type") { + return `${file.name} has an invalid type. Please upload an image.`; + } + return `${file.name} is invalid.`; + }); + }) + .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 ( <> { (file) => new File( [file], - `${field.filenamePrefix}_-_Attachment_-_${file.name}`, + `${ + field.filenamePrefix.hasOwnProperty( + "representationForm" + ) && + field.filenamePrefix.representationForm.hasOwnProperty( + "values" + ) && + field.filenamePrefix.representationForm.values.hasOwnProperty( + "repfile_name" + ) && + field.filenamePrefix.representationForm + .values.repfile_name + }_-_Attachment_-_${file.name}`, { type: file.type, } ) ); field.input.onChange(renamedAcceptedFiles); + setUploadCountMessage(renamedAcceptedFiles.length); + + uploadSingleFile( + renamedAcceptedFiles, + field.containerID, + field.ticketnumber + + "/" + + field.filenamePrefix.representationForm.values + .repfile_name + ).then((data) => { + console.log(data); + let buildAppealFilesArray = []; + let values = field; + let filesUploadObj = renamedAcceptedFiles; + + for (let key in filesUploadObj) { + typeof filesUploadObj[key].name != "undefined" && + buildAppealFilesArray.push({ + "name": filesUploadObj[key].name, + "size": filesUploadObj[key].size, + }); + } + + //console.log(buildAppealFilesArray); + function removeDuplicates(arr, key) { + const seen = new Set(); + return arr.filter((item) => { + const value = item[key]; + if (seen.has(value)) { + return false; // Duplicate found + } else { + seen.add(value); // Add value to the set + return true; // Keep the item + } + }); + } + + buildAppealFilesArray = values.hasOwnProperty( + "fileList" + ) + ? buildAppealFilesArray.concat(values.fileList) + : buildAppealFilesArray; + + buildAppealFilesArray = removeDuplicates( + buildAppealFilesArray, + "name" + ); + + // Object.assign(values, { + // "fileList": buildAppealFilesArray, + // }); + getFilesFromBlobproxy( + field.containerID, + field.ticketnumber + + "/" + + (typeof field.filenamePrefix != "object" + ? field.filenamePrefix + : field.filenamePrefix.representationForm + .values.repfile_name) + ).then((data) => { + data = data.value || {}; + field.input.value == null; + field.setFilesForRepresentations(data[0]); + setCompletedUploadFiles(true); + }); + }); }} + onDropRejected={handleDropRejected} + onDropAccepted={handleDropAccepted} > {({ getRootProps, getInputProps }) => ( <> @@ -1030,6 +1156,7 @@ export const RenderFileUpload = (field) => { {t( "myrepresentations:fileupload-drop-label" )} + (Max 500MB)
( @@ -1040,57 +1167,93 @@ export const RenderFileUpload = (field) => {
+ {uploadCountMessage > 0 && + completedUploadFiles == false && ( +
+ Uploading {uploadCountMessage} files{" "} +
+ )} + {completedUploadFiles > 0 && + uploadCountMessage > 0 && ( +
+ {uploadCountMessage} files uploaded{" "} +
+ )} )} + {rejectedFiles.length > 0 && ( +
+ +
+ )} {field.meta.touched && field.meta.error && ( {field.meta.error} )} - {filesUploadObj && Array.isArray(filesUploadObj) && ( - + {!completedUploadFiles && + filesUploadObj && + Array.isArray(filesUploadObj) && + filesUploadObj.map( + (file, i) => + typeof file.name != "undefined" && ( +
+ {file.name} + + {file.name} ({bytesToSize(file.size)} + )
+
+ {completedUploadFiles ? ( +

+ Upload complete +

+ ) : ( +

+ Uploading... +

+ )} +
+
+
+ ) + )} + {blobList.length > 0 && ( +

{t("myrepresentations:previously-added-files")}

)} {blobList.map((blob, i) => (
-
- { - deleteThisBlob( - field.containerID, - blob.name, - blob.hasheddeletepath, - blob.hashgetblobs, - field.ticketnumber - ); - }} - title="Remove this file" - > - − - -
+ { + setUploadCountMessage(uploadCountMessage - 1); + deleteThisBlob( + field.containerID, + blob.name, + blob.hasheddeletepath, + blob.hashgetblobs, + field.ticketnumber, + blob.path.split("/")[1] + ); + }} + title="Remove this file" + > + − + + {blob.name} ); diff --git a/components/representation.js b/components/representation.js index 0f4495b1..418f80ac 100644 --- a/components/representation.js +++ b/components/representation.js @@ -33,6 +33,7 @@ const CaseRepresentation = (props) => { setShowQuestionnaireSection={ props.setShowQuestionnaireSection } + setCurrentReference={props.setCurrentReference} />
diff --git a/pages/api/file/getbloblist.js b/pages/api/file/getbloblist.js index a7f9f733..ae972fc5 100644 --- a/pages/api/file/getbloblist.js +++ b/pages/api/file/getbloblist.js @@ -30,7 +30,7 @@ */ import { hashAPIPath } from "../../../actions"; -import { getBlobs } from "../../../actions/azurestorage"; +import { getBlobs, getRepsFilesBlobs } from "../../../actions/azurestorage"; import nextConnect from "next-connect"; import middleware from "../middleware/middleware"; @@ -57,9 +57,17 @@ ApiProxy.get(async (req, res) => { // console.log(hashAPIPath(checkquerypath) == "&hash=" + checkHash); if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) { - await getBlobs(containerName, casefolderID).then((data) => { - return res.status(200).json({ "value": [data] }); - }); + casefolderID.split("/").length > 1 + ? await getRepsFilesBlobs( + containerName, + casefolderID.split("/")[0], + casefolderID.split("/")[1] + ).then((data) => { + return res.status(200).json({ "value": [data] }); + }) + : await getBlobs(containerName, casefolderID).then((data) => { + return res.status(200).json({ "value": [data] }); + }); } else { return res.status(400).json(); } diff --git a/pages/myportal/index.js b/pages/myportal/index.js index c79c38d7..8debb32e 100644 --- a/pages/myportal/index.js +++ b/pages/myportal/index.js @@ -34,7 +34,11 @@ import { setAwaitingSubmissionDetails, setAwaitingSubmissionFromBlob, } from "../../store/awaitingSubmission/action"; -import { setShowReps, setLocale } from "../../store/currentView/action"; +import { + setShowReps, + setLocale, + setFilesForRepresentation, +} from "../../store/currentView/action"; import { setMyCases, setMyCasesDetails } from "../../store/myCases/action"; import { setMyRepresentations, diff --git a/pages/myportal/representation.js b/pages/myportal/representation.js index 56e54c9a..f395072d 100644 --- a/pages/myportal/representation.js +++ b/pages/myportal/representation.js @@ -13,6 +13,7 @@ import { getCase, getPortalModuleDetails, getBasicSearch, + getFilesFromBlob, } from "../../actions"; import { getFormCollectionByID } from "../../components/utils"; import Breadcrumbs from "../../components/breadcrumbs"; @@ -31,6 +32,7 @@ import { getSearchDetails } from "../../components/utils"; import { setCurrentReference, setCurrentView, + setFilesForRepresentations, setRepresentationCapacity, } from "../../store/currentView/action"; import { @@ -49,6 +51,7 @@ import { setMySubmittedReps, setMySubmittedRepsDetails, } from "../../store/myRepresentations/action"; +import { getRepsFilesBlobs } from "../../actions/azurestorage"; const RepresentationF = (props) => { const { footerLinks, pages } = props; @@ -201,6 +204,7 @@ const RepresentationF = (props) => { setShowQuestionnaireSection={ setShowQuestionnaireSection } + setCurrentReference={setCurrentReference} showQuestionnaireSection={showQuestionnaireSection} /> @@ -298,7 +302,24 @@ export const getServerSideProps = wrapper.getServerSideProps( "repDetails": result, }) ); + + store.dispatch( + setRepresentationCapacity(result.representationCapacity) + ); + + // console.log( + // "===========================\n getting files", + // result.filesList + // ); + + const repsFileListObj = await getRepsFilesBlobs( + result.containerID, + result.caseRef, + result.repfile_name + ); + store.dispatch(setAccountDetails(accountDetails)); + store.dispatch(setFilesForRepresentations(repsFileListObj)); return { props: { @@ -340,6 +361,7 @@ export const getServerSideProps = wrapper.getServerSideProps( : "", }) ); + store.dispatch(setAccountDetails(accountDetails)); // return { diff --git a/store/currentView/action.js b/store/currentView/action.js index 4e263ffa..91a04c5e 100644 --- a/store/currentView/action.js +++ b/store/currentView/action.js @@ -12,6 +12,7 @@ export const currentViewActionTypes = { SETREPRESENTATIONRESET: "SETREPRESENTATIONRESET", SETSHOWLOGIN: "SETSHOWLOGIN", SETLOCALE: "SETLOCALE", + SETFILELISTREPS: "SETFILELISTREPS", }; export const getCurrentViewObj = () => (dispatch) => { @@ -33,6 +34,12 @@ export const setCurrentReference = (caseReference) => (dispatch) => { caseReference: caseReference, }); }; +export const setFilesForRepresentations = (fileList) => (dispatch) => { + return dispatch({ + type: currentViewActionTypes.SETFILELISTREPS, + fileList: fileList, + }); +}; export const setRepresentationCapacity = (representationCapacity) => (dispatch) => { diff --git a/store/currentView/reducer.js b/store/currentView/reducer.js index 41f10da2..4b1d968d 100644 --- a/store/currentView/reducer.js +++ b/store/currentView/reducer.js @@ -12,6 +12,7 @@ const currentViewInitialState = { showReps: false, showLogin: false, locale: "", + fileList: [], }; export default function reducer(state = currentViewInitialState, action) { @@ -37,6 +38,12 @@ export default function reducer(state = currentViewInitialState, action) { ...state, representationCapacity: action.representationCapacity, }; + case currentViewActionTypes.SETFILELISTREPS: + return { + ...state, + fileList: action.fileList, + }; + case currentViewActionTypes.SETREPRESENTATIONSUBMIT: return { ...state,