SME-Web/src/components/process/processStepOne/SelectedDocuments.tsx

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;