279 lines
13 KiB
TypeScript
279 lines
13 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
import { ReactSortable } from 'react-sortablejs';
|
|
import { RiDraggable } from "react-icons/ri";
|
|
import { FaFileAlt, FaPlus, FaGoogleDrive, FaDropbox, FaMicrosoft, FaCloudUploadAlt } from "react-icons/fa";
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
// import { setSelectedDocuments } from '../../../store/slices/selectedDocsSlice';
|
|
import { setDocuments, uploadDocuments } from '../../../store/slices/documentSlice';
|
|
import { RootState, AppDispatch } from '../../../store/store';
|
|
import { IoMdClose } from 'react-icons/io';
|
|
import { Bounce, toast } from "react-toastify";
|
|
import api from '../../../config/api';
|
|
import DocumentCard from './DocumentCard';
|
|
import { useParams } from 'react-router-dom';
|
|
import { toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice';
|
|
|
|
|
|
const SelectedDocuments = ({ transactionTemplateToggle }) => {
|
|
// let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
|
|
|
// const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
// await sleep(5000);
|
|
|
|
const documents = useSelector((state: RootState) => state.documents.documents);
|
|
const docCount = documents.length;
|
|
|
|
useEffect(() => {
|
|
|
|
console.log("Documents updated:", documents);
|
|
}, [documents]);
|
|
|
|
|
|
|
|
const templateId = useParams<{ templateId: string }>().templateId;
|
|
let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
|
|
|
const dispatch = useDispatch();
|
|
|
|
if (!transactionId) {
|
|
transactionId = templateId;
|
|
}
|
|
|
|
const [isModalOpen, setModalOpen] = useState(false);
|
|
const [activeTab, setActiveTab] = useState('Upload');
|
|
const [files, setFiles] = useState<File[]>([]);
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const tabs = [
|
|
{ name: 'Upload', icon: <FaPlus /> },
|
|
{ name: 'Google Drive', icon: <FaGoogleDrive /> },
|
|
{ name: 'OneDrive', icon: <FaMicrosoft /> },
|
|
{ name: 'Dropbox', icon: <FaDropbox /> },
|
|
];
|
|
|
|
const handleUpload = (filesToUpload: File[]) => {
|
|
const formData = new FormData();
|
|
|
|
filesToUpload.forEach(file => {
|
|
formData.append("file", file);
|
|
});
|
|
// filesToUpload.forEach((file) => formData.append("document", file));
|
|
|
|
dispatch(uploadDocuments({ formData, accountId, transactionId }))
|
|
.unwrap()
|
|
.then((res) => {
|
|
toast.dismiss();
|
|
setFiles([]);
|
|
setModalOpen(false);
|
|
})
|
|
.catch((err) => {
|
|
console.error("Upload failed", err);
|
|
});
|
|
};
|
|
|
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const selectedFiles = e.target.files ? Array.from(e.target.files) : [];
|
|
if (selectedFiles.length > 0) {
|
|
setFiles((prev) => [...prev, ...selectedFiles]);
|
|
handleUpload(selectedFiles);
|
|
}
|
|
};
|
|
|
|
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
|
e.preventDefault();
|
|
const droppedFiles = Array.from(e.dataTransfer.files);
|
|
if (droppedFiles.length > 0) {
|
|
setFiles((prev) => [...prev, ...droppedFiles]);
|
|
handleUpload(droppedFiles);
|
|
}
|
|
};
|
|
|
|
// Get proper Tailwind class names for upload card span
|
|
const getUploadCardSpanClass = () => {
|
|
if (docCount === 0) {
|
|
// Upload card takes all columns only when no docs
|
|
return "col-span-1 sm:col-span-2 md:col-span-3 lg:col-span-4 xl:col-span-5";
|
|
}
|
|
|
|
switch (docCount) {
|
|
case 1:
|
|
return "col-span-1 sm:col-span-1 md:col-span-2 lg:col-span-3 xl:col-span-4";
|
|
case 2:
|
|
return "col-span-1 sm:col-span-1 md:col-span-1 lg:col-span-2 xl:col-span-3";
|
|
case 3:
|
|
return "col-span-1 sm:col-span-1 md:col-span-1 lg:col-span-1 xl:col-span-2";
|
|
case 4:
|
|
return "col-span-1"; // balanced
|
|
default:
|
|
return "col-span-1";
|
|
}
|
|
};
|
|
|
|
|
|
|
|
// Calculate how many documents should be in the first row
|
|
const getFirstRowDocCount = () => {
|
|
if (docCount === 0) return 0;
|
|
if (docCount <= 4) return docCount; // All docs fit in first row
|
|
return 4; // Max 4 docs in first row when upload card takes 1 column
|
|
};
|
|
|
|
const firstRowDocCount = getFirstRowDocCount();
|
|
const remainingDocs = Math.max(0, docCount - firstRowDocCount);
|
|
|
|
return (
|
|
<>
|
|
{/* Upload Card First */}
|
|
<div className="space-y-3">
|
|
<div
|
|
id="upload-doc-step-one"
|
|
className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-5 gap-2"
|
|
>
|
|
{/* Upload Document Card */}
|
|
<div
|
|
onClick={() => setModalOpen(true)}
|
|
className={`${getUploadCardSpanClass()} cursor-pointer h-[170px] flex flex-col items-center justify-center border-2 border-dashed border-gray-300 rounded-xl bg-white hover:bg-gray-50 transition shadow-sm hover:shadow-md`}
|
|
>
|
|
<FaPlus className="text-[var(--primary)] text-3xl mb-2" />
|
|
<p className="text-sm text-gray-600 text-center px-2">Upload Document</p>
|
|
</div>
|
|
|
|
{/* First Row Documents */}
|
|
<ReactSortable
|
|
className="contents"
|
|
list={documents.slice(0, firstRowDocCount).map((doc) => ({ ...doc }))}
|
|
setList={(newState) => {
|
|
const updatedDocs = [...newState, ...documents.slice(firstRowDocCount)];
|
|
dispatch(setDocuments(updatedDocs));
|
|
}}
|
|
handle=".drag-handle"
|
|
animation={200}
|
|
ghostClass="sortable-ghost"
|
|
disabled={false}
|
|
>
|
|
{documents.slice(0, firstRowDocCount).map((doc) => (
|
|
<div key={doc.id} className="col-span-1 mx-auto">
|
|
<DocumentCard
|
|
id={doc.id}
|
|
name={doc.name}
|
|
isCompleted={
|
|
"isCompleted" in doc ||
|
|
("status" in doc && doc.status === "UPLOADED")
|
|
? true
|
|
: false
|
|
}
|
|
transactionTemplateToggle={transactionTemplateToggle}
|
|
/>
|
|
</div>
|
|
))}
|
|
</ReactSortable>
|
|
</div>
|
|
|
|
{/* Additional Rows */}
|
|
{remainingDocs > 0 && (
|
|
<ReactSortable
|
|
className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-5 gap-2"
|
|
list={documents.slice(firstRowDocCount).map((doc) => ({ ...doc }))}
|
|
setList={(newState) => {
|
|
const updatedDocs = [
|
|
...documents.slice(0, firstRowDocCount),
|
|
...newState,
|
|
];
|
|
dispatch(setDocuments(updatedDocs));
|
|
}}
|
|
handle=".drag-handle"
|
|
animation={200}
|
|
ghostClass="sortable-ghost"
|
|
disabled={false}
|
|
>
|
|
{documents.slice(firstRowDocCount).map((doc) => (
|
|
<div key={doc.id} className="col-span-1 mx-auto">
|
|
<DocumentCard
|
|
id={doc.id}
|
|
name={doc.name}
|
|
isCompleted={
|
|
"isCompleted" in doc ||
|
|
("status" in doc && doc.status === "UPLOADED")
|
|
? true
|
|
: false
|
|
}
|
|
transactionTemplateToggle={transactionTemplateToggle}
|
|
/>
|
|
</div>
|
|
))}
|
|
</ReactSortable>
|
|
)}
|
|
|
|
{/* Modal */}
|
|
{isModalOpen && (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-100/60 backdrop-blur-sm px-2 sm:px-4">
|
|
<div className="bg-white w-full h-full sm:max-w-5xl sm:h-[500px] rounded-xl shadow-lg flex flex-col sm:flex-row overflow-hidden relative">
|
|
{/* Close Button */}
|
|
<button
|
|
className="absolute top-2 right-2 text-gray-500 hover:text-red-500 transition text-xl bg-white rounded-full p-2"
|
|
onClick={() => setModalOpen(false)}
|
|
>
|
|
<IoMdClose />
|
|
</button>
|
|
|
|
{/* Sidebar Tabs */}
|
|
<div className="w-full sm:w-1/4 bg-gray-100 p-4 flex flex-row sm:flex-col gap-2 border-b sm:border-b-0 sm:border-r border-gray-200 overflow-x-auto sm:overflow-hidden">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.name}
|
|
className={`flex items-center gap-2 p-3 rounded-md text-sm font-medium transition whitespace-nowrap ${activeTab === tab.name
|
|
? "bg-white shadow text-[var(--primary)]"
|
|
: "hover:bg-gray-200 text-gray-600"
|
|
}`}
|
|
onClick={() => setActiveTab(tab.name)}
|
|
>
|
|
{tab.icon} {tab.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Modal Content */}
|
|
<div className="w-full sm:w-3/4 p-4 sm:p-6 overflow-y-auto">
|
|
{activeTab === "Upload" && (
|
|
<div
|
|
onClick={() => fileInputRef.current?.click()}
|
|
onDragOver={(e) => e.preventDefault()}
|
|
onDrop={handleDrop}
|
|
className="flex flex-col items-center justify-center h-full border-2 border-dashed border-gray-300 rounded-xl p-6 text-center cursor-pointer hover:bg-gray-50"
|
|
>
|
|
<FaCloudUploadAlt className="text-5xl text-[var(--primary)] mb-4" />
|
|
<p className="text-sm text-gray-500 mb-2">
|
|
Drag & drop files here or
|
|
</p>
|
|
<button className="px-5 py-2 text-sm bg-[var(--primary)] text-white rounded-md hover:scale-103 cursor-pointer transition-all">
|
|
Browse Files
|
|
</button>
|
|
<input
|
|
type="file"
|
|
ref={fileInputRef}
|
|
className="hidden"
|
|
onChange={handleFileChange}
|
|
accept=".pdf,.doc,.docx,.jpg,.png,.jpeg,.txt"
|
|
multiple
|
|
/>
|
|
</div>
|
|
)}
|
|
{activeTab !== "Upload" && (
|
|
<div className="flex items-center justify-center h-full text-gray-400 text-sm">
|
|
<p>{activeTab} integration coming soon...</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
|
|
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default SelectedDocuments;
|