Merged PR 1343: file uplaod fix for reps

Related work items: #12878, #12956
This commit is contained in:
Robert Bond
2024-12-18 13:46:35 +00:00
23 changed files with 485 additions and 137 deletions
+86
View File
@@ -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;
+25
View File
@@ -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(
+12 -10
View File
@@ -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")}
/>
<Field
@@ -285,7 +287,7 @@ let PersonalDetails = (props) => {
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")}
/>
<Field
name="lastname"
@@ -295,7 +297,7 @@ let PersonalDetails = (props) => {
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")}
/>
<Field
name="emailaddress1"
@@ -323,7 +325,7 @@ let PersonalDetails = (props) => {
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")}
/>
</fieldset>
<fieldset className="govuk-fieldset">
@@ -343,7 +345,7 @@ let PersonalDetails = (props) => {
"account:account-address-line-1-label"
)}
validate={[required]}
errorMsg="Is required"
errorMsg={t("account:is-required-label")}
/>
<Field
name="address1_line2"
@@ -354,7 +356,7 @@ let PersonalDetails = (props) => {
label={t(
"account:account-address-line-2-label"
)}
errorMsg="Is required"
errorMsg={t("account:is-required-label")}
/>
<Field
name="address1_city"
@@ -364,7 +366,7 @@ let PersonalDetails = (props) => {
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")}
/>
<Field
name="address1_county"
@@ -374,7 +376,7 @@ let PersonalDetails = (props) => {
className="govuk-input govuk-input--width-10"
label={t("account:account-county-label")}
validate={[required]}
errorMsg="Is required"
errorMsg={t("account:is-required-label")}
/>
<Field
name="address1_postalcode"
+9
View File
@@ -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}
/>
);
}
@@ -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
}
/>
</div>
{props.currentView.caseReference.hasOwnProperty(
"repDetails"
) ? (
props.currentView.caseReference.repDetails.hasOwnProperty(
"filesList"
) &&
props.currentView.caseReference.repDetails
.filesList.length > 0 ? (
<div className="govuk-form-group govuk-!-margin-bottom-9">
{filterFilesList.map((blob, i) => (
<div
key={blob.name + "_" + i}
className="govuk-!-margin-bottom-5 fileThumb "
>
<img
src={getThumbnailIconByExtension(
blob.name
)}
alt={blob.name}
width="50"
/>
<span className="govuk-body govuk-!-font-size-14 govuk-!-padding-left-5">
{blob.name} (
{bytesToSize(blob.size)}
)
</span>
</div>
))}
</div>
) : (
""
)
) : (
""
)}
</fieldset>
</div>{" "}
</div>
@@ -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,
@@ -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}
/>{" "}
<div id="rep-details" className="">
<div className="govuk-grid-column-full">
@@ -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) {
@@ -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 (
<>
<Dropzone
maxSize={10 * 1024 * 1024}
accept={{
"application/pdf": [".pdf"],
"application/msword": [".doc"],
@@ -1011,14 +1057,94 @@ export const RenderFileUpload = (field) => {
(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)
</div>
<em>
(
@@ -1040,57 +1167,93 @@ export const RenderFileUpload = (field) => {
</em>
</div>
<aside>{thumbs}</aside>
{uploadCountMessage > 0 &&
completedUploadFiles == false && (
<div className="govuk-body govuk-!-font-size-14">
Uploading {uploadCountMessage} files{" "}
</div>
)}
{completedUploadFiles > 0 &&
uploadCountMessage > 0 && (
<div className="govuk-body govuk-!-font-size-14">
{uploadCountMessage} files uploaded{" "}
</div>
)}
</section>
</>
)}
</Dropzone>
{rejectedFiles.length > 0 && (
<div style={{ color: "red", marginTop: "10px" }}>
<ul>
{rejectedFiles.map((error, index) => (
<li key={index}>{error}</li>
))}
</ul>
</div>
)}
{field.meta.touched && field.meta.error && (
<span className="error">{field.meta.error}</span>
)}
{filesUploadObj && Array.isArray(filesUploadObj) && (
<ul>
{filesUploadObj.map(
(file, i) =>
typeof file.name != "undefined" && (
<div
key={file.name}
className="govuk-!-margin-bottom-5 fileThumb"
>
<img
src={getThumbnailIcon(file, file.type)}
alt={file.name}
width="50"
/>
<span className="govuk-body govuk-!-font-size-14 govuk-!-padding-left-5">
{file.name} ({bytesToSize(file.size)})
</span>
</div>
)
)}
</ul>
{!completedUploadFiles &&
filesUploadObj &&
Array.isArray(filesUploadObj) &&
filesUploadObj.map(
(file, i) =>
typeof file.name != "undefined" && (
<div
key={file.name}
className="govuk-!-margin-bottom-5 fileThumb"
>
<img
src={getThumbnailIcon(file, 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">
Uploading...
</p>
)}
</div>
</span>
</div>
)
)}
{blobList.length > 0 && (
<h4>{t("myrepresentations:previously-added-files")}</h4>
)}
{blobList.map((blob, i) => (
<div
key={blob.name + "_" + i}
className="govuk-!-margin-bottom-5 fileThumb govuk-!-margin-top-5"
>
<div className="">
<span
className="govuk-summary-list__actionLink watched_link govuk-!-margin-right-5 govuk-!-text-align-left"
onClick={() => {
deleteThisBlob(
field.containerID,
blob.name,
blob.hasheddeletepath,
blob.hashgetblobs,
field.ticketnumber
);
}}
title="Remove this file"
>
&minus;
</span>
</div>
<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,
blob.path.split("/")[1]
);
}}
title="Remove this file"
>
&minus;
</span>
<img
src={getThumbnailIconByExtension(blob.name)}
alt={blob.name}
@@ -1134,7 +1297,7 @@ export function FileUploadField(props) {
ticketnumber={props.ticketnumber}
hint={props.hint}
fileList={props.fileList}
setFilesForAppeal={props.setFilesForAppeal}
setFilesForRepresentations={props.setFilesForRepresentations}
containerID={props.containerID}
filenamePrefix={
props.filenamePrefix.hasOwnProperty("representationForm") &&
@@ -1151,6 +1314,7 @@ export function FileUploadField(props) {
.values.repfile_name
: ""
}
props={props}
/>
</>
);
@@ -1015,7 +1015,7 @@ let S78_Questionnaire = (props) => {
"myrepresentations:s78-section-question-signed"
)}
validate={[required]}
errorMsg="Is required"
errorMsg={t("myrepresentations:is-required-label")}
/>
</div>
<div className="govuk-form-group">
@@ -1030,7 +1030,7 @@ let S78_Questionnaire = (props) => {
label={t(
"myrepresentations:s78-section-question-onbehalf"
)}
errorMsg="Is required"
errorMsg={t("myrepresentations:is-required-label")}
/>
</div>
<Field
@@ -1041,7 +1041,7 @@ let S78_Questionnaire = (props) => {
validate={[required]}
dateStart="0"
dateEnd="1-y"
errorMsg="Is required"
errorMsg={t("myrepresentations:is-required-label")}
locale={locale}
/>
</fieldset>
+10 -6
View File
@@ -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{" "}
</div>
)}
{completedUploadFiles > 0 && (
<div className="govuk-body govuk-!-font-size-14">
{uploadCountMessage} files uploaded{" "}
</div>
)}
{completedUploadFiles > 0 &&
uploadCountMessage > 0 && (
<div className="govuk-body govuk-!-font-size-14">
{uploadCountMessage} files uploaded{" "}
</div>
)}
</section>
</>
)}
@@ -2205,6 +2206,9 @@ const RenderFileUpload = (field) => {
<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,
+7 -4
View File
@@ -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) => {
+1
View File
@@ -33,6 +33,7 @@ const CaseRepresentation = (props) => {
setShowQuestionnaireSection={
props.setShowQuestionnaireSection
}
setCurrentReference={props.setCurrentReference}
/>
</div>
</div>
+5 -1
View File
@@ -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"
}
+3 -1
View File
@@ -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"
}
+2 -1
View File
@@ -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",
+5 -1
View File
@@ -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"
}
+3 -1
View File
@@ -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"
}
+2 -1
View File
@@ -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",
+12 -4
View File
@@ -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();
}
+5 -1
View File
@@ -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,
+22
View File
@@ -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}
/>
</div>
@@ -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 {
+7
View File
@@ -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) => {
+7
View File
@@ -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,