/* eslint-disable array-callback-return */
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable react/no-array-index-key */
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable no-plusplus */
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-alert */
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable jsx-a11y/control-has-associated-label */
/* eslint-disable react/button-has-type */
/* eslint-disable max-len */
import React, { useState, useEffect } from 'react';
import { useDropzone } from 'react-dropzone';
import { AiOutlineClose } from 'react-icons/ai';
import FileUploadIcon from '@assets/FileUploadIcon.svg';
import FileListIcon from '@assets/FileListIcon.svg';
import FileUploadIconDark from '@assets/FileUploadIconDark.svg';
import TickMarkIconGreen from '@assets/TickMarkIconGreen.svg';
import TickMarkIconGrey from '@assets/TickMarkIconGrey.svg';
import FileArrowIcon from '@assets/FileArrowIcon.svg';
import './style.scss';
import Box from '@mui/material/Box/Box';
import Button from '@mui/material/Button/Button';
import { useForm } from 'react-hook-form';
import {
fetchDocumentTypeList,
fetchDocumentsList,
deleteDocument,
willDocumentUploadSelector,
saveDocument,
} from '@redux/slices/will-documents-upload';
import { useSelector, useDispatch } from 'react-redux';
import { DocumentTypeList } from '@api/models';
import axios from 'axios';
import { willPersonalInformationSelector } from '@redux/slices/will-personal-information';
import { willsValidatorSelector } from '@redux/slices/willsValidator';
import MirrorWillSwitcher from '@components/MirrorWillSwitcher';
// import custom hook useDocumentsList
import useDocumentsList from '@utils/hooks/useDocumentsList';
import DeleteConfirmationModal from '@components/DeleteConfirmationModal';
import { useLocation } from 'react-router';
import DocumentManagerModal from '@components/DocumentManagerModal';
import { fetchAppointmentDetail } from '@redux/slices/wills-book-appointment';
import { trackPromise } from 'react-promise-tracker';
import { getAppointmentDetail } from '../../redux/slices/wills-book-appointment';
import api from '../../api';
interface Props {
handleNext: (step: number, stepName?: string) => void;
currentBtnStep: number;
handleBack: () => void;
willTypeID: number;
}
function DocumentManager({
handleNext, currentBtnStep, handleBack, willTypeID,
}: Props) {
// const [documentTypeID, setDocumentTypeID] = useState<string>('');
const [documentTypeArray, setDocumentTypeArray] = useState([]);
const [documentTypeID, setDocumentTypeID] = useState();
const [uniqueDocumentId, setUniqueDocumentId] = useState();
const [witnessProfileGUID, setWitnessProfileGUID] = useState();
const [willDocGuid, setWillDocGuid] = useState<string>('');
// const bookedForProfileGUID = 'ba125f2d-8c78-41ce-b576-6aaef9b57c2a';
const [uploadedFiles, setUploadedFiles] = useState([]);
const [showModal, setShowModal] = useState(false);
const { state } = useLocation();
const [showBookAppointnentModal, setShowBookAppointnentModal] = useState(false);
// state for documentIsRequired with boolean type
// profile guid
const { isSpouseSelected, spouseGuid, profileGuid } = useSelector(willPersonalInformationSelector);
const dispatch = useDispatch();
const handleOpenModal = () => {
setShowModal(true);
};
const handleCloseModal = () => {
setShowModal(false);
};
const handleBookAppointmentModalOpen = () => {
setShowBookAppointnentModal(true);
};
const handleBookAppointmentModalClose = () => {
setShowBookAppointnentModal(false);
};
const handleBookAppointmentModalContinue = () => {
setShowBookAppointnentModal(false);
// handleNext(currentBtnStep, 'upload');
};
// Show a modal on load
// useEffect(() => {
// dispatch<any>(fetchAppointmentDetail(profileGuid))
// .then((response: any) => {
// console.log('fetchAppointmentDetailResult', response);
// // log the response status
// if (!response) {
// handleBookAppointmentModalOpen();
// }
// });
// }, []);
useEffect(() => {
const fetchData = async () => {
try {
const response = await trackPromise(api.getAppointmentDetail(profileGuid));
console.log('fetch AppointmentDetail Result', response?.data?.Output);
// log the timeSlotID
console.log('timeSlotID:', response?.data?.Output?.timeSlotID);
if (response?.data?.Output?.timeSlotID === 0) {
console.log('No appointment details found! Opening modal');
handleBookAppointmentModalOpen();
} else {
const appointmentDetails = response.data.Output;
console.log('Appointment details:', appointmentDetails);
}
} catch (error) {
console.error('Error fetching appointment detail:', error);
}
};
fetchData();
}, []);
// API results from Redux
const {
bookedforprofileguid,
docType,
documentTypeList,
documentsList,
docTypeID,
isDocumentsChanged,
} = useSelector(willDocumentUploadSelector);
const { mirrorWillCheck } = useSelector(willsValidatorSelector);
// custom hook for document list
const uploadedDocumentsList = useDocumentsList(documentsList);
/**
* Retrieves the list of document types and sets it to the document type array.
*
* @return {void} No return value.
*/
const getDocumentTypeList = () => {
const documentTypes: any = [];
documentTypeList.forEach((d: DocumentTypeList) => {
documentTypes.push({
documentTypeID: d.docTypeID,
documentTypeName: d.fileName,
witnessProfileGUID: d.witnessProfileGUID,
isRequired: d.isRequired,
});
});
setDocumentTypeArray(documentTypes);
// log the document type array with a message
console.log('Document types array:', documentTypeArray);
};
const [documentIsRequired, setDocumentIsRequired] = useState<boolean>(
documentTypeArray.some((d) => d.isRequired),
);
// Check for Document Required or not
// if (documentTypeArray.some((d) => d.isRequired)) {
// // list reuiqred documents
// const requiredDocuments = documentTypeArray.filter((d) => d.isRequired);
// console.log('Required documents:', requiredDocuments);
// }
// API calls inside useEffects -------
useEffect(() => {
// dispatch<any>(fetchDocumentTypeList(bookedForProfileGUID));
dispatch<any>(fetchDocumentTypeList(isSpouseSelected ? spouseGuid : profileGuid));
}, [dispatch, isSpouseSelected]);
useEffect(() => {
// Dispatch the fetchDocumentsList action when documentTypeID changes
dispatch<any>(fetchDocumentsList(isSpouseSelected ? spouseGuid : profileGuid, documentTypeID, witnessProfileGUID));
}, [uniqueDocumentId, documentTypeID, isDocumentsChanged, isSpouseSelected]);
// Result transformation for UI
useEffect(() => {
getDocumentTypeList();
}, [documentTypeList]);
const { handleSubmit } = useForm();
/**
* Sets the active element to the given element string.
*
* @param {string} element - The string representing the active element to set.
*/
const handleElementClick = (docTypeId: any, witnessProfileGUID: any) => {
const uniqueDocumentId: any = `${docTypeId}-${witnessProfileGUID}`;
setUniqueDocumentId(uniqueDocumentId);
setDocumentTypeID(docTypeId);
setWitnessProfileGUID(witnessProfileGUID);
console.log(witnessProfileGUID, uniqueDocumentId);
};
/**
* Returns the background color style object for a given element.
*
* @param {string} element - The element to get background style for.
* @return {Object} The background color style object for the given element.
*/
const getBackgroundStyle = (element: any) => ({
backgroundColor: uniqueDocumentId === element ? '#023979' : '#F4F4F4',
});
/**
* Returns an object representing the style to be applied to the title element.
*
* @param {string} element - The element to apply the style to.
* @return {Object} An object containing the color property to be applied to the title element.
*/
const getTitleStyle = (element: any) => ({
color: uniqueDocumentId === element ? '#FFFFFF' : '#1B202D',
});
const getFileUploadIcon = (element: any) => (uniqueDocumentId === element ? FileUploadIcon : FileUploadIconDark);
const getTickMarkicon = (element: any) => (uniqueDocumentId === element ? TickMarkIconGreen : TickMarkIconGrey);
/**
* Handles click event on "Add File" button.
*
* @param {any} e - The event object.
* @return {void} Nothing is returned by this function.
*/
const [documentTypeArrayCopy, setDocumentTypeArrayCopy] = useState([...documentTypeArray]);
const handleAddFileClick = (e: any) => {
e.preventDefault();
documentTypeArrayCopy.forEach((d) => {
if (d.isRequired) {
setDocumentIsRequired(true);
// update the documentTypeArrayCopy with the new value of documentIsRequired
documentTypeArrayCopy.push({ ...d, isRequired: false });
// log the updated documentTypeArrayCopy
console.log('documentTypeArrayCopy:', documentTypeArrayCopy);
setDocumentTypeArrayCopy(documentTypeArrayCopy);
}
setDocumentIsRequired(false);
});
if (e.target !== e.currentTarget) {
return;
}
document.getElementById('file-upload-input')?.click();
// store documentTypeArray to a new array
// check if any of the documents is required, if yes, set documentIsRequired to true
// const isRequiredDocumentsExist = documentTypeArray.some((d) => d.isRequired);
// // log the result of the check
// console.log('isRequiredDocumentsExist:', isRequiredDocumentsExist);
// setDocumentIsRequired(isRequiredDocumentsExist);
};
/**
* Handles the file input change event.
*
* @param {any} e - the event object
* @return {void}
*/
const handleFileInputChange = (e: any) => {
const newFiles = Array.from(e.target.files);
checkFileValidity(newFiles);
};
/**
* Handles the file drop event by checking the validity of the accepted files.
*
* @param {any} acceptedFiles - the files that have been accepted for upload
*/
const handleFileDrop = (acceptedFiles: any) => {
checkFileValidity(acceptedFiles);
};
/**
* Prevents the click event from propagating and executing other click events.
*
* @param {any} e - the click event to be stopped from propagating
*/
const handleRowItemClick = (e: any) => {
e.stopPropagation();
};
/**
* Filters files by their extension and size, and adds the valid files to the uploadedFiles state.
*
* @param {Array} files - The array of files to be checked.
* @return {void} Returns nothing.
*/
// Check the validity of uploaded files
const checkFileValidity = async (files: any) => {
const validExtensions = ['.pdf', '.jpeg', '.jpg', '.bmp', '.doc', '.docx'];
const maxFileSize = 20 * 1024 * 1024;
// Filter valid files based on extension and file size
const validFiles = files.filter((file: any) => {
// Check if the file extension is valid
const isValidExtension = validExtensions.some((ext) => file.name.toLowerCase().endsWith(ext));
// Check if the file size is within the allowed limit
const isWithinMaxSize = file.size <= maxFileSize;
return isValidExtension && isWithinMaxSize;
});
// Filter invalid files
const invalidFiles = files.filter(
(file: any) => !validFiles.includes(file),
);
if (invalidFiles.length > 0) {
// Display an alert message for invalid files
const invalidFileNames = invalidFiles
.map((file: any) => file.name)
.join(', ');
alert(
`Invalid files: ${invalidFileNames}. Please use A4-size PDF, JPEG, BMP, DOC, or DOCX files that are within 20MB.`,
);
} else {
// Add valid files to the uploaded files list
setUploadedFiles((prevFiles) => [...prevFiles, ...validFiles]);
if (uploadedFiles) {
const formData = new FormData();
for (let i = 0; i < validFiles.length; i++) {
const file = validFiles[i];
formData.append('FileDoc', file, file.name);
}
dispatch<any>(saveDocument(isSpouseSelected ? spouseGuid : profileGuid, documentTypeID, witnessProfileGUID, formData));
}
}
};
/**
* Removes a file from the system.
*
* @param {string} willDocGuid - the unique identifier of the file to be removed
* @return {any} the result of the delete operation
*/
const removeFile = (willDocGuid: string) => {
setWillDocGuid(willDocGuid);
handleOpenModal();
};
const deleteFile = () => {
console.log(`Delete the doc with GUID: ${willDocGuid}`);
dispatch<any>(deleteDocument(willDocGuid));
// log the result of the delete operation
console.log('File removed successfully!');
setShowModal(false);
};
const handleUploadDocument = (data: any) => {
console.log(data);
// handle document upload
handleNext(currentBtnStep, 'upload');
};
const {
getRootProps, // Props for the file drop zone element
getInputProps, // Props for the file input element
} = useDropzone({
onDrop: handleFileDrop, // Callback function for handling dropped or selected files
});
return (
<main>
<section>
{mirrorWillCheck && <MirrorWillSwitcher />}
<header className="header mt-4">Upload Documents</header>
<p className="description">
Upload the documents in PDF or JPEG format. Click on Next Step to save
the files once all the documents have been uploaded
</p>
</section>
<div className="row document-upload-container">
<div className="col-lg-6 content-wrapper">
{documentTypeArray?.map((type) => (
<div
className={`top${
uniqueDocumentId === `${type?.documentTypeID}-${type?.witnessProfileGUID}` ? ' active' : ''
}`}
style={getBackgroundStyle(`${type?.documentTypeID}-${type?.witnessProfileGUID}`)}
onClick={() => handleElementClick(type?.documentTypeID, type?.witnessProfileGUID)}
>
<div className="left-container">
<div className="file-upload-icon">
<img
src={getFileUploadIcon(`${type?.documentTypeID}-${type?.witnessProfileGUID}`)}
alt="File Uploader Icon"
/>
</div>
<div
className="document-title"
style={getTitleStyle(`${type?.documentTypeID}-${type?.witnessProfileGUID}`)}
>
{type.documentTypeName}
</div>
</div>
<div className="tick-icon">
<img
src={getTickMarkicon(`${type?.documentTypeID}-${type?.witnessProfileGUID}`)}
alt="Tick Mark"
/>
</div>
</div>
))}
</div>
<div
className="col-lg-6 row-item"
{...getRootProps()}
onClick={handleRowItemClick}
>
<div className="file-upload-arrow">
<img src={FileArrowIcon} alt="File Upload Arrow Icon" />
</div>
<div className="file-upload-text">
Drag and drop document here to upload
</div>
<div className="file-attach-instructions">
Please attach the file(s) below (use the Add button). We recommend
using A4-size PDF, BMP, PNG, DOC, DOCX, JPG and JPEG files. File
size cannot be more than 20 megabytes (MB). Your files will be
uploaded when you submit your form.
</div>
<div className="file-add-button">
<button className="add-file-btn" onClick={handleAddFileClick}>
Add File
</button>
<input
type="file"
id="file-upload-input"
name="file-upload-input"
accept=".pdf, .bmp, .png, .doc, .docx, .jpg, .jpeg"
multiple
onChange={handleFileInputChange}
style={{ display: 'none' }}
{...getInputProps()}
/>
</div>
{uploadedDocumentsList.length > 0
&& uploadedDocumentsList.map((file: any, index) => (
<div className="file-list-item" key={index}>
<div className="file-info">
<div className="file-icon">
<img src={FileListIcon} alt="File List Icon" />
</div>
<div className="file-name">{file.fileName}</div>
</div>
<div className="close-icon" onClick={() => removeFile(file.willDocGuid)}>
<span className="close-icon-text">Remove</span>
</div>
</div>
))}
</div>
</div>
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
{documentIsRequired}
<button
type="button"
className="next-btn"
onClick={() => handleNext(state === 'Guardianship Will' ? 6 : currentBtnStep, 'upload')}
disabled={documentIsRequired}
>
Next Step
</button>
</Box>
<span className="next-btn-text mt-4">
*Before clicking next, please make sure the details provided here are
correct.
</span>
<DeleteConfirmationModal
show={showModal}
handleClose={handleCloseModal}
handleContinue={deleteFile}
type="Document"
/>
{/* render DocumentManagerModal */}
{
(willTypeID === 1 || willTypeID === 2) && (
<DocumentManagerModal
showBookAppointnentModal={showBookAppointnentModal}
handleBookAppointmentModalClose={handleBookAppointmentModalClose}
handleBookAppointmentModalContinue={handleBookAppointmentModalContinue}
handleNext={handleNext}
currentBtnStep={currentBtnStep}
handleBack={handleBack}
/>
)
}
</main>
);
}
export default DocumentManager;