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/account/personaldetails.js b/components/account/personaldetails.js index 91624656..42804df9 100644 --- a/components/account/personaldetails.js +++ b/components/account/personaldetails.js @@ -219,13 +219,15 @@ let PersonalDetails = (props) => { /(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/gi; const email = (value) => - value && !emailRegex.test(value) ? "Invalid email address" : undefined; + value && !emailRegex.test(value) + ? t("account:invalid-phone-number-errormsg") + : undefined; const phoneNumber = (value) => value && !/^(?:(?:\(?(?:0(?:0|11)\)?[\s-]?\(?|\+)44\)?[\s-]?(?:\(?0\)?[\s-]?)?)|(?:\(?0))(?:(?:\d{5}\)?[\s-]?\d{4,5})|(?:\d{4}\)?[\s-]?(?:\d{5}|\d{3}[\s-]?\d{3}))|(?:\d{3}\)?[\s-]?\d{3}[\s-]?\d{3,4})|(?:\d{2}\)?[\s-]?\d{4}[\s-]?\d{4}))(?:[\s-]?(?:x|ext\.?|\#)\d{3,4})?$/i.test( value ) - ? "Invalid phone number" + ? t("account:invalid-phone-number-errormsg") : undefined; const postcode = (value) => @@ -274,7 +276,7 @@ let PersonalDetails = (props) => { label={t( "account:account-preferred-language-label" )} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { className="govuk-input govuk-input--width-20" validate={[required]} label={t("account:account-first-name-label")} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { className="govuk-input govuk-input--width-20" validate={[required]} label={t("account:account-last-name-label")} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { type="text" className="govuk-input govuk-input--width-20" label={t("account:account-company-group-label")} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} />
@@ -343,7 +345,7 @@ let PersonalDetails = (props) => { "account:account-address-line-1-label" )} validate={[required]} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { label={t( "account:account-address-line-2-label" )} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { className="govuk-input govuk-input--width-10" label={t("account:account-town-city-label")} validate={[required]} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { className="govuk-input govuk-input--width-10" label={t("account:account-county-label")} validate={[required]} - errorMsg="Is required" + errorMsg={t("account:is-required-label")} /> { 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 && ( +
+
    + {rejectedFiles.map((error, index) => ( +
  • {error}
  • + ))} +
+
+ )} {field.meta.touched && field.meta.error && ( {field.meta.error} )} - {filesUploadObj && Array.isArray(filesUploadObj) && ( -
    - {filesUploadObj.map( - (file, i) => - typeof file.name != "undefined" && ( -
    - {file.name} - - {file.name} ({bytesToSize(file.size)}) - -
    - ) - )} -
+ {!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/case/representation/representationQuestionnaires/representationLPAQuestionnaire_s78.js b/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_s78.js index 6431d6b4..8e780d93 100644 --- a/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_s78.js +++ b/components/case/representation/representationQuestionnaires/representationLPAQuestionnaire_s78.js @@ -1015,7 +1015,7 @@ let S78_Questionnaire = (props) => { "myrepresentations:s78-section-question-signed" )} validate={[required]} - errorMsg="Is required" + errorMsg={t("myrepresentations:is-required-label")} />
@@ -1030,7 +1030,7 @@ let S78_Questionnaire = (props) => { label={t( "myrepresentations:s78-section-question-onbehalf" )} - errorMsg="Is required" + errorMsg={t("myrepresentations:is-required-label")} />
{ validate={[required]} dateStart="0" dateEnd="1-y" - errorMsg="Is required" + errorMsg={t("myrepresentations:is-required-label")} locale={locale} /> diff --git a/components/elements/index.js b/components/elements/index.js index 736b5004..c13f29cb 100644 --- a/components/elements/index.js +++ b/components/elements/index.js @@ -1432,7 +1432,7 @@ export function NumericField(props) { : t("newappeal:is-required-label"); }; const isNumber = (value) => { - const regex = /^\d*\.?\d{0,1}$/; + const regex = /^\d+$/; return regex.test(value) ? undefined : t("newappeal:invalid-number-label"); @@ -2122,11 +2122,12 @@ const RenderFileUpload = (field) => { Uploading {uploadCountMessage} files{" "} )} - {completedUploadFiles > 0 && ( -
- {uploadCountMessage} files uploaded{" "} -
- )} + {completedUploadFiles > 0 && + uploadCountMessage > 0 && ( +
+ {uploadCountMessage} files uploaded{" "} +
+ )} )} @@ -2205,6 +2206,9 @@ const RenderFileUpload = (field) => { { + setUploadCountMessage( + uploadCountMessage - 1 + ); deleteThisBlob( field.containerID, blob.name, diff --git a/components/newappeal/aboutyou.js b/components/newappeal/aboutyou.js index ca635875..6a4ae58c 100644 --- a/components/newappeal/aboutyou.js +++ b/components/newappeal/aboutyou.js @@ -455,7 +455,8 @@ let AboutYou = (props) => { const contactPref = router.locale == "cy" ? ["Ebost", "Post"] : ["Email", "Post"]; - const required = (value) => (value ? undefined : "Is required"); + const required = (value) => + value ? undefined : t("newappeal:is-required-label"); const emojicheck = (value) => { let errors; @@ -465,7 +466,7 @@ let AboutYou = (props) => { /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{2300}-\u{23FF}\u{2B50}\u{1F004}-\u{1F0CF}\u{1F0A0}-\u{1F0A5}\u{1F170}-\u{1F251}]/gu; if (emojiRegex.test(value)) { - errors = "Emojis are not allowed"; + errors = t("newappeal:emojis-not-allowed-label"); } return errors; @@ -474,13 +475,15 @@ let AboutYou = (props) => { /(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/gi; const email = (value) => - value && !emailRegex.test(value) ? "Invalid email address" : undefined; + value && !emailRegex.test(value) + ? t("newappeal:emojis-not-allowed-label") + : undefined; const phoneNumber = (value) => value && !/^(?:(?:\(?(?:0(?:0|11)\)?[\s-]?\(?|\+)44\)?[\s-]?(?:\(?0\)?[\s-]?)?)|(?:\(?0))(?:(?:\d{5}\)?[\s-]?\d{4,5})|(?:\d{4}\)?[\s-]?(?:\d{5}|\d{3}[\s-]?\d{3}))|(?:\d{3}\)?[\s-]?\d{3}[\s-]?\d{3,4})|(?:\d{2}\)?[\s-]?\d{4}[\s-]?\d{4}))(?:[\s-]?(?:x|ext\.?|\#)\d{3,4})?$/i.test( value ) - ? "Invalid phone number" + ? t("newappeal:invalid-phonenumber-label") : undefined; const onHandleSubmit = (values) => { 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/locales/cy/account.json b/locales/cy/account.json index 8d581085..099f8536 100644 --- a/locales/cy/account.json +++ b/locales/cy/account.json @@ -58,5 +58,9 @@ "account-postcode-label": "Cod post", "account-complete-para-1": "Mae eich manylion wedi cael eu diweddaru", "account-complete-para-2": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi a metus non quam mollis egestas. Integer ac leo cursus, laoreet nulla sed, tempus tellus. Mauris condimentum erat arcu.", - "account-complete-return-to-portal-link": "Dychwelyd i Fy Mhorth" + "account-complete-return-to-portal-link": "Dychwelyd i Fy Mhorth", + "is-required-label": "Yn ofynnol", + "invalid-phone-number-errormsg": "Rhif ffôn annilys", + "invalid-email-address-errormsg": "Cyfeiriad e-bost annilys", + "no-emojis-errormsg": "Ni chaniateir emojis" } \ No newline at end of file diff --git a/locales/cy/myrepresentations.json b/locales/cy/myrepresentations.json index e0b6dcfc..df333e7e 100644 --- a/locales/cy/myrepresentations.json +++ b/locales/cy/myrepresentations.json @@ -219,5 +219,7 @@ "statement-onbehalf-label": "Ar ran", "final-comments-label": "Sylwadau terfynol", "representation-type": "Math o sylw", - "select-an-option-label": "Dewiswch opsiwn" + "select-an-option-label": "Dewiswch opsiwn", + "previously-added-files": "Ffeiliau wedi'u llwytho i fyny", + "is-required-label": "Yn ofynnol" } \ No newline at end of file diff --git a/locales/cy/newappeal.json b/locales/cy/newappeal.json index b9d42020..38d2132c 100644 --- a/locales/cy/newappeal.json +++ b/locales/cy/newappeal.json @@ -18,9 +18,10 @@ "is-required-label": "Yn ofynnol", "select-a-date-label": "Dewiswch ddyddiad", "invalid-decimal-label": "Rhif annilys (uchafswm o 7 nod, 2 le degol)", - "invalid-number-label": "Dylai fod yn rhif", + "invalid-number-label": "Dylai fod yn rhif cyfan", "emojis-not-allowed-label": "Ni chaniateir emojis", "invalid-postcode-label": "Cod post annilys", + "invalid-phonenumber-label": "Rhif ffôn annilys", "hascas-confirmation-label": "Cadarnhewch fod angen apêl HAS/CAS", "hascas-confirmation-hint": "Mae'r llwybr apelio hwn yn berthnasol dim ond os yw'ch cais yn ymwneud â gwrthod caniatâd cynllunio, neu wrthod cais i amrywio neu ddileu amod sydd ynghlwm wrth ganiatâd blaenorol. Bydd pob cais arall yn cael ei gyflwyno trwy Apêl Gynllunio Adran 78", "about-you-page-title": "Amdanoch chi", diff --git a/locales/en/account.json b/locales/en/account.json index e0f488da..e7e6aa16 100644 --- a/locales/en/account.json +++ b/locales/en/account.json @@ -58,5 +58,9 @@ "account-postcode-label": "Postcode", "account-complete-para-1": "Your details have been updated", "account-complete-para-2": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi a metus non quam mollis egestas. Integer ac leo cursus, laoreet nulla sed, tempus tellus. Mauris condimentum erat arcu.", - "account-complete-return-to-portal-link": "Return to My portal" + "account-complete-return-to-portal-link": "Return to My portal", + "is-required-label": "Is required", + "invalid-phone-number-errormsg": "Invalid phone number", + "invalid-email-address-errormsg": "Invalid email address", + "no-emojis-errormsg": "Emojis are not allowed" } \ No newline at end of file diff --git a/locales/en/myrepresentations.json b/locales/en/myrepresentations.json index 240e9ac8..3bdf65e6 100644 --- a/locales/en/myrepresentations.json +++ b/locales/en/myrepresentations.json @@ -219,5 +219,7 @@ "statement-onbehalf-label": "On behalf of", "final-comments-label": "Final comments", "representation-type": "Representation type", - "select-an-option-label": "Select an option" + "select-an-option-label": "Select an option", + "previously-added-files": "Uploaded files", + "is-required-label": "Is required" } \ No newline at end of file diff --git a/locales/en/newappeal.json b/locales/en/newappeal.json index 0143ee98..1c7de39f 100644 --- a/locales/en/newappeal.json +++ b/locales/en/newappeal.json @@ -18,9 +18,10 @@ "is-required-label": "Is required", "select-a-date-label": "Select a date", "invalid-decimal-label": "Invalid number (max 7 characters, 2 decimal places)", - "invalid-number-label": "Should be a number", + "invalid-number-label": "Should be a whole number", "emojis-not-allowed-label": "Emojis are not allowed", "invalid-postcode-label": "Invalid postcode", + "invalid-phonenumber-label": "Invalid phone number", "hascas-confirmation-label": "Confirm HAS/CAS appeal is required", "hascas-confirmation-hint": "This appeal route is only applicable if your application relates the refusal of planning permission, or the refusal of an application to vary or remove a condition attached to a previous permision. All other applications will be submitted via a Section 78 Planning Appeal", "about-you-page-title": "About you", 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,