import useTranslation from "next-translate/useTranslation";
import dynamic from "next/dynamic";
import Link from "next/link";
import router from "next/router";
import React, { useMemo, useState, useRef } from "react";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import Dropzone from "react-dropzone";
import "react-quill-new/dist/quill.snow.css";
import { Field, change } from "redux-form";
import {
uploadSingleFile,
deleteBlob,
deleteRepBlob,
getFilesFromBlobHashed,
getFilesFromBlobproxy
} from "../../../actions";
const LoadingMessage = () =>
Loading the editor...
;
// Dynamically import ReactQuill with a proper loading state
const ReactQuill = dynamic(
() => import("react-quill-new").then((mod) => mod.default),
{
ssr: false,
loading: () => // This is valid as long as it's a valid React component
}
);
import { useDispatch } from "react-redux"; // Import the useDispatch hook
import {
addDays,
addMonths,
addYears,
parseISO,
subDays,
subMonths,
subYears
} from "date-fns";
import {
bytesToSize,
getThumbnailIconByExtension,
updateLinks
} from "../../utils";
import { useRouter } from "next/router";
export const RenderRadioList = ({
datafieldname,
name,
label,
id,
errorMsg,
options,
input: { onChange, value },
meta: { touched, error },
...custom
}) => {
const required = (value) => (value ? undefined : "Required");
return (
<>
>
);
};
export const RenderRadioListWithText = ({
datafieldname,
name,
label,
id,
errorMsg,
options,
input: { onChange, value },
meta: { touched, error },
...custom
}) => {
const required = (value) => (value ? undefined : "Required");
const router = useRouter();
return (
<>
>
);
};
export const RenderCondtionalRadioList = ({
datafieldname,
legend,
name,
label,
id,
errorMsg,
options,
input: { onChange, value },
meta: { touched, error },
...custom
}) => {
const required = (value) => (value ? undefined : "Required");
let { t } = useTranslation();
return (
<>
>
);
};
export const RenderLPACondtionalRadioList = ({
datafieldname,
legend,
name,
label,
id,
errorMsg,
options,
input: { onChange, value },
meta: { touched, error },
...custom
}) => {
const required = (value) => (value ? undefined : "Required");
let { t } = useTranslation();
return (
<>
>
);
};
export const RenderPickList = ({
datafieldname,
name,
label,
input,
optionsArr,
meta: { touched, error },
errorMsg,
...custom
}) => {
// console.log(custom.disabled);
let { t } = useTranslation();
return (
<>
{touched && error && (
Error:{" "}
{errorMsg}
)}
>
);
};
export const RenderTextfield = ({
id,
rows,
datafieldname,
name,
label,
input,
errorMsg,
type,
className,
meta: { touched, error },
...custom
}) => {
const required = (value) => (value ? undefined : "Required");
return (
<>
{touched && error && (
Error:{" "}
{errorMsg}
)}
>
);
};
export const RenderDatePicker = ({
datafieldname,
name,
label,
id,
errorMsg,
input: { onChange, value },
meta: { touched, error },
...custom
}) => {
const router = useRouter();
var startDateArr =
custom.dateStart != "0" ? custom.dateStart.split("") : custom.dateStart;
var endDateArr =
custom.dateEnd != "0" ? custom.dateEnd.split("") : custom.dateEnd;
var minDate =
startDateArr == 0
? new Date()
: startDateArr[0] == "-"
? startDateArr[2] == "y"
? subYears(new Date(), startDateArr[1])
: startDateArr[2] == "m"
? subMonths(new Date(), startDateArr[1])
: subDays(new Date(), startDateArr[1])
: startDateArr[2] == "y"
? addYears(new Date(), startDateArr[1])
: startDateArr[2] == "m"
? addMonths(new Date(), startDateArr[1])
: addDays(new Date(), startDateArr[1]);
var maxDate =
endDateArr == 0
? new Date()
: startDateArr[0] == "-"
? startDateArr[2] == "y"
? subYears(new Date(), startDateArr[1])
: startDateArr[2] == "m"
? subMonths(new Date(), startDateArr[1])
: subDays(new Date(), startDateArr[1])
: startDateArr[2] == "y"
? addYears(new Date(), startDateArr[1])
: startDateArr[2] == "m"
? addMonths(new Date(), startDateArr[1])
: addDays(new Date(), startDateArr[1]);
return (
<>
>
);
};
export const RenderMultiline = ({
id,
className,
rows,
datafieldname,
name,
label,
input,
meta: { touched, error },
...custom
}) => {
const [editValue, setEditValue] = useState(
input.value != null ? input.value : ""
);
const changeEdit = (valStr) => {
setEditValue(valStr);
input.onChange(valStr);
};
var modules = {
toolbar: [
[{ "header": [1, 2, false] }],
["bold", "italic", "underline", "blockquote"],
[{ "list": "ordered" }, { "list": "bullet" }],
["clean"]
]
};
return (
<>
{"hint" in custom}
{"hint" in custom && (
{custom.hint}
)}
>
);
};
export const RenderRichMultiline = ({
id,
className,
rows,
datafieldname,
name,
label,
input,
meta: { touched, error },
...custom
}) => {
const [editValue, setEditValue] = useState(
input.value != null ? input.value : ""
);
const changeEdit = (valStr) => {
valStr = updateLinks(valStr);
setEditValue(valStr);
input.onChange(valStr);
};
var modules = {
toolbar: [
[{ "header": [1, 2, false] }],
["bold", "italic", "underline", "blockquote"],
[{ "list": "ordered" }, { "list": "bullet" }],
["clean"]
]
};
return (
<>
>
);
};
export function MultiLinefield(props) {
const { name, label } = props;
return (
Do not include personal or financial information, like your
National Insurance number or credit card details.
);
}
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) => {
const files = field.input.value;
const dispatch = useDispatch(); // Get dispatch using useDispatch hook
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) => (

-{" "}
{file.name} ({file.size / 1024}kb)
));
const getThumbnailIcon = (fileObj, fileType) => {
switch (fileType) {
case "text/html":
return "/assets/images/documenttypes/html.png";
break;
case "text/plain":
return "/assets/images/documenttypes/txt.png";
break;
case "application/msword":
return "/assets/images/documenttypes/doc.png";
break;
case "application/pdf":
return "/assets/images/documenttypes/pdf.png";
break;
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
return "/assets/images/documenttypes/docx.png";
break;
case "text/csv":
return "/assets/images/documenttypes/csv.png";
break;
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
return "/assets/images/documenttypes/xlsx.png";
break;
case "image/jpeg":
return "/assets/images/documenttypes/jpg.png";
case "image/png":
return "/assets/images/documenttypes/jpg.png";
//return URL.createObjectURL(fileObj);
break;
default:
return "/assets/images/documenttypes/default.png";
break;
}
};
const filelistObj = field.fileList || {};
let blobList = filelistObj;
//let blobList = field.props.currentView.fileList;
function removeNullObjects(arr) {
if (arr.length === 0) {
return arr;
}
return arr.filter((obj) => obj !== null);
}
function removeObjectsWithUndefinedKey(arr, key) {
if (arr.length === 0) {
return arr;
}
return arr.filter((obj) => obj[key] !== undefined);
}
function removeEmptyObjects(arr) {
if (arr.length === 0) {
return arr;
}
return arr.filter(
(obj) =>
obj && typeof obj === "object" && Object.keys(obj).length > 0
);
}
//blobList = removeEmptyObjects(blobList);
//blobList = removeObjectsWithUndefinedKey(blobList, "name");
//blobList = removeNullObjects(blobList);
let buildAppealFilesArray = [];
let filesUploadObj = field.input.value;
for (let key in filesUploadObj) {
typeof filesUploadObj[key].name != "undefined" &&
buildAppealFilesArray.push({
"name": filesUploadObj[key].name,
"size": filesUploadObj[key].size
});
}
//buildAppealFilesArray = buildAppealFilesArray.concat(blobList);
function removeDuplicates(arr) {
const seen = new Set();
return arr.filter((obj) => {
const serializedObj = JSON.stringify(obj);
if (seen.has(serializedObj)) {
return false; // Skip this object (duplicate)
}
seen.add(serializedObj); // Add the serialized object to the "seen" set
return true; // Keep this object
});
}
buildAppealFilesArray = removeDuplicates(buildAppealFilesArray);
//blobList = buildAppealFilesArray;
const deleteThisBlob = async (
containerName,
blobName,
deleteblobhash,
getblobshash,
casefolderID,
filenamePrefix
) => {
deleteRepBlob(
containerName,
blobName,
deleteblobhash,
casefolderID,
filenamePrefix
)
.then((data) => data)
.then(() => {
getFilesFromBlobHashed(
containerName,
getblobshash,
casefolderID + "/" + filenamePrefix
).then((newfilelist) =>
field.setFilesForRepresentations(newfilelist.value[0])
);
});
};
const [errorMessage, setErrorMessage] = useState(null);
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} ${t(
"myrepresentations:fileupload-file-error-filesize-label"
)}`;
}
if (error.code === "file-invalid-type") {
return `${file.name} ${t(
"myrepresentations: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(
"myrepresentations: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);
setRejectedFiles([]); // Clear any previously rejected file errorsss
};
const FILENAME_ALLOWED = /^[A-Za-z0-9 ._\-:()—]+$/;
const validateFilename = (file, t) => {
const name = file.name;
// block #
if (name.includes("#")) {
return {
code: "filename-invalid-chars",
message: `${name} ${t(
"myrepresentations:fileupload-file-error-invalid-filename-label"
)}`
};
}
// broader blocklist (optional but recommended)
if (!FILENAME_ALLOWED.test(name)) {
return {
code: "filename-invalid-chars",
message: `${name} ${t(
"myrepresentations:fileupload-file-error-invalid-filename-label"
)}`
};
}
// block characters < > " / \ | ? *
if (/[<>"/\\|?*]/.test(name)) {
return {
code: "filename-invalid-chars",
message: `${name} ${t(
"myrepresentations:fileupload-file-error-invalid-filename-label"
)}`
};
}
return null; // valid
};
return (
<>
validateFilename(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);
return;
}
setCompletedUploadFiles(false);
const renamedAcceptedFiles = acceptedFiles.map(
(file) =>
new File(
[file],
`${
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);
if (typeof field.setFileCount === "function") {
field.setFileCount(
(field.uploadCount || 0) +
renamedAcceptedFiles.length
);
}
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,
// });
data.invalidFiles.length > 0 &&
(setRejectedFiles(data.invalidFiles),
setUploadCountMessage(
renamedAcceptedFiles.length -
data.invalidFiles.length
));
getFilesFromBlobproxy(
field.containerID,
field.ticketnumber +
"/" +
(typeof field.filenamePrefix != "object"
? field.filenamePrefix
: encodeURIComponent(
field.filenamePrefix
.representationForm.values
.repfile_name
))
).then((data) => {
data = data.value || {};
field.input.value = "";
field.setFilesForRepresentations(data[0]);
setCompletedUploadFiles(true);
dispatch(
change("representationForm", field.name, null)
); // Set the field value to null
});
});
}}
>
{({ getRootProps, getInputProps }) => (
<>
>
)}
{rejectedFiles.length > 0 && (
{rejectedFiles.map((error, index) => (
- {error}
))}
)}
{uploadCountMessage > 0 && completedUploadFiles == false && (
{t("home:uploading-files-label", {
number: uploadCountMessage
})}
)}
{completedUploadFiles > 0 && uploadCountMessage >= 0 && (
{t("home:completed-uploading-files-label", {
number: uploadCountMessage
})}
)}
{field.meta.touched && field.meta.error && (
{field.meta.error}
)}
{!completedUploadFiles &&
filesUploadObj &&
Array.isArray(filesUploadObj) &&
filesUploadObj.map(
(file, i) =>
typeof file.name != "undefined" && (
{file.name} ({bytesToSize(file.size)}
)
{completedUploadFiles ? (
Upload complete
) : (
{t(
"home:uploading-files-progress-label"
)}
)}
)
)}
{blobList.length > 0 && (
{t("myrepresentations:previously-added-files")}
)}
{blobList.map((blob, i) => (
{
setUploadCountMessage(uploadCountMessage - 1);
deleteThisBlob(
field.containerID,
blob.name,
blob.hasheddeletepath,
blob.hashgetblobs,
field.ticketnumber,
blob.path.split("/")[1]
);
}}
title={t("home:remove-this-file-label")}
>
−
{blob.name} (
{bytesToSize(blob.contentLength || blob.size)})
))}{" "}
>
);
};
export function FileUploadField(props) {
return (
<>
{props.label}
>
);
}
export const shouldDisableButton = ({ props, formObj }) =>
!(
props?.currentView?.fileList?.length > 0 ||
typeof formObj?.representationForm?.values?.representationComments !==
"undefined"
);
// export const RepresentationGuidanceText = (appealType) => {
// let { t } = useTranslation();
// console.log(appealType);
// switch (appealType) {
// case "846040004": //has/cas
// return (
//
// {t("myrepresentations:questionnaire-publish-policy-label")}{" "}
//
// https://gov.wales/planning-and-environment-decisions-wales-publishing-policy-html
//
//
// );
// break;
// default:
// return (
//
// {t("myrepresentations:publish-policy-label")}{" "}
//
// https://gov.wales/planning-and-environment-decisions-wales-publishing-policy-html
//
//
// );
// break;
// }
// return guidanceString;
// };