diff --git a/src/App.tsx b/src/App.tsx index 9fa7535..eeccb2a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -60,6 +60,12 @@ const App = () => { } /> + + + + } /> + diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index ccb0d87..6a9a43d 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -272,8 +272,6 @@ const Account = () => { } }; - - return ( <> = ({ signers }) => { {/* Dotted line */} -
+
@@ -129,7 +129,7 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => {
{sortedOrders.map((order, index) => ( -
+
Step {order}
diff --git a/src/components/process/processStepOne/DocumentCard.tsx b/src/components/process/processStepOne/DocumentCard.tsx index e6d13d2..db43178 100644 --- a/src/components/process/processStepOne/DocumentCard.tsx +++ b/src/components/process/processStepOne/DocumentCard.tsx @@ -14,15 +14,17 @@ interface DocumentCardProps { id: string; name: string; isCompleted: boolean; + transactionTemplateToggle:boolean; } -const DocumentCard: React.FC = ({ id, name, isCompleted }) => { - - const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); - if (!accountId || !transactionId) { - window.location.href = "/"; - return null; - } +const DocumentCard: React.FC = ({ id, name, isCompleted , transactionTemplateToggle }) => { + const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); + const accountId = params.accountId; + const transactionId = params.transactionId || params.templateId; + // if (!accountId || !transactionId) { + // window.location.href = "/"; + // return null; + // } const [progress, setProgress] = useState(0) const [message, setMessage] = useState('') @@ -96,7 +98,7 @@ const DocumentCard: React.FC = ({ id, name, isCompleted }) => console.error("Missing document ID, account ID, or transaction ID"); return; } - const res = await api.delete(`/api/accounts/${accountId}/transactions/${transactionId}/documents/${docId}`) + const res = await api.delete(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/documents/${docId}`) if (res.status === 202) { dispatch(deleteDocument(docId)); setDocumentDel({ docId: null, isDel: false }); diff --git a/src/components/process/processStepOne/ProcessStepOne.tsx b/src/components/process/processStepOne/ProcessStepOne.tsx index c6f8a0e..94a8027 100644 --- a/src/components/process/processStepOne/ProcessStepOne.tsx +++ b/src/components/process/processStepOne/ProcessStepOne.tsx @@ -23,6 +23,8 @@ const ProcessStepOne = () => { } let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; + console.log("transactionTemplateToggle1", transactionTemplateToggle) + console.log() const templateId = useParams<{ templateId: string }>().templateId; let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); @@ -31,14 +33,15 @@ const ProcessStepOne = () => { if (!transactionId) { transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; + console.log("transactionTemplateToggle2", transactionTemplateToggle) transactionId = templateId; dispatch(toggleToTemplates()); } console.log("ProcessStepOne called with accountId:", accountId, "and transactionId:", transactionId); - if (!accountId || !transactionId) { - window.location.href = "/"; - return null; - } + // if (!accountId || !transactionId) { + // window.location.href = "/"; + // return null; + // } // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) // const { selectedDocuments, signType } = useSelector((state: RootState) => state.selectedDocuments) @@ -46,6 +49,8 @@ const ProcessStepOne = () => { const selectedDocuments = useSelector((state: RootState) => state.documents.documents) const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients) + console.log("selectedRecipients", selectedRecipients) + useEffect(() => { selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId })); @@ -61,6 +66,8 @@ const ProcessStepOne = () => { dispatch(hideLoader()); }, []) + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + const handleNextClick = async (isSendNow: boolean) => { let formError = { documents: false, @@ -138,28 +145,38 @@ const ProcessStepOne = () => { accessCode: "", accessCodeDelivery: true, language: "en", - name: `${recipient.firstName} ${recipient.lastName}` + name: `${recipient.firstName} ${recipient.lastName}`, + roleName: recipient.roleName })); dispatch(addRecipientAsync({ recipientsData, accountId, transactionId })); dispatch(showLoader("Processing Message...")); - await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { subject: emailDtls.subject, message: emailDtls.message, status: "CREATED" }).then((res) => { - console.log(res); + console.log(`1/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`) + + let res = await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { + subject: emailDtls.subject, message: emailDtls.message, + status: "CREATED" }) - + console.log("Line 158", res.data) if (isSendNow) { dispatch(updateTransactionStatus(TRANSACTION_STATUS_ENUMS.TRANSACTION_SEND_SUCCESS)); setTimeout(() => { // navigate("/"); }, 1000); - } else { + } + else { setTimeout(() => { dispatch(resetDocuments()) dispatch(resetRecipients()) dispatch(hideLoader()); - navigate(`/process-step-two/${accountId}/${transactionId}`); + if (transactionTemplateToggle === 'templates') { + console.log(`/process-step-two/${accountId}/template/${transactionId}`) + navigate(`/process-step-two/${accountId}/template/${transactionId}`) + } else { + navigate(`/process-step-two/${accountId}/${transactionId}`); + } }, 1000); } @@ -223,7 +240,7 @@ const ProcessStepOne = () => { {/* Documents */}

Documents

- +
{/* Recipients */} @@ -232,7 +249,7 @@ const ProcessStepOne = () => {
{/* {signType !== "self-sign" && } */} - +
diff --git a/src/components/process/processStepOne/RecipientAddCard.tsx b/src/components/process/processStepOne/RecipientAddCard.tsx index 3b734a7..eeff5c0 100644 --- a/src/components/process/processStepOne/RecipientAddCard.tsx +++ b/src/components/process/processStepOne/RecipientAddCard.tsx @@ -6,6 +6,8 @@ import { AppDispatch } from "../../../store/store"; import api from "../../../config/api"; import ContactModal from "./ContactModal"; import useDebouncedEffect from "../../../utlity/useDebounce"; +import { IoIosSettings } from "react-icons/io"; +import { FiEdit, FiTrash } from "react-icons/fi"; interface ContactType { id: string; @@ -19,7 +21,7 @@ export interface ContactTypeObj { contacts: ContactType[]; } -const RecipientAddCard = () => { +const RecipientAddCard = ({ transactionTemplateToggle }) => { const [contactData, setContactData] = useState({ type: "", contacts: [] }); const [openContactModal, setOpenContactModal] = useState(false); @@ -27,7 +29,10 @@ const RecipientAddCard = () => { const dispatch = useDispatch(); const formRef = useRef(null); - const [recipient, setRecipients] = useState({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", type: recipientType.SIGNER }); + const [recipient, setRecipients] = useState({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: Boolean, allowSenderToRemove: Boolean }); + + const [showAdvanceSettings, setShowAdvanceSettings] = useState(false) + console.log(recipient) const handleRecipientAdd = (e: React.FormEvent) => { e.preventDefault(); @@ -43,24 +48,24 @@ const RecipientAddCard = () => { // Validation Ends dispatch(addRecipient(recipient)); - setRecipients({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", type: recipientType.SIGNER }); + setRecipients({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: Boolean, allowSenderToRemove: Boolean }); formRef.current?.reset(); } // useEffect(() => { - // if (!recipient.firstName.length) return; + // if (!recipient.firstName.length) return; - // const handler = setTimeout(() => { - // api.get(`/api/contact?firstName=${recipient.firstName}`) - // .then(res => { - // setContactData({ type: "firstName", contacts: res.data.content }); - // }) - // .catch(err => console.log(err)); - // }, 500); // wait 500ms after the last change + // const handler = setTimeout(() => { + // api.get(`/api/contact?firstName=${recipient.firstName}`) + // .then(res => { + // setContactData({ type: "firstName", contacts: res.data.content }); + // }) + // .catch(err => console.log(err)); + // }, 500); // wait 500ms after the last change - // return () => { - // clearTimeout(handler); // cancel previous timeout if effect runs again - // }; + // return () => { + // clearTimeout(handler); // cancel previous timeout if effect runs again + // }; // }, [recipient.firstName]); @@ -81,7 +86,7 @@ const RecipientAddCard = () => { // }) // .catch(err => console.log(err)) // }, [recipient.lastName], 500); - + // useDebouncedEffect(() => { // if (!recipient.email.length) return // api.get(`/api/contact?email=${recipient.email}`) @@ -99,114 +104,238 @@ const RecipientAddCard = () => { return (
- {/* Name Inputs */} -
- setOpenContactModal(true)} - onBlur={handleContactModalBlur} - value={recipient.firstName} - placeholder="First Name" - className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" - onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })} - /> - setOpenContactModal(true)} - onBlur={handleContactModalBlur} - value={recipient.lastName} - placeholder="Last Name" - className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" - onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })} - /> -
- - {recipient.type === recipientType.INPERSONSIGNER &&
- setOpenContactModal(true)} - onBlur={handleContactModalBlur} - value={recipient.hostName} - placeholder="Host Name" - className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" - onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })} - /> - setOpenContactModal(true)} - onBlur={handleContactModalBlur} - value={recipient.hostEmail} - placeholder="Host Email" - className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" - onChange={(e) => setRecipients({ ...recipient, hostEmail: e.target.value })} - /> -
} - - {/* Email Input */} - setOpenContactModal(true)} - onBlur={handleContactModalBlur} - value={recipient.email} - placeholder="Recipient Email" - className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" - onChange={(e) => setRecipients({ ...recipient, email: e.target.value })} - /> - - {/* Contact Modal */} - {contactData.contacts.length > 0 && openContactModal && ( -
- -
- )} - - {/* Signing options + Add button in one line */} -
-
- {[ - { label: "Signer", value: recipientType.SIGNER }, - { label: "In Person Signer", value: recipientType.INPERSONSIGNER }, - { label: "CC", value: recipientType.CC }, - ].map((option) => ( - - ))} -
- - -
-
+ {transactionTemplateToggle === 'templates' && ( +
+ {/* Top Row: Role Input + Settings */} +
+ {/* Role Name Input */} + setOpenContactModal(true)} + onBlur={handleContactModalBlur} + value={recipient.roleName} + placeholder="Role name" + className="w-64 px-3 py-2 me-3 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" + onChange={(e) => setRecipients({ ...recipient, roleName: e.target.value })} + /> + + {/* Settings + Permissions */} +
+
+ {/* Permission Status Text - Always Visible when settings open */} +
+
+ {recipient.allowSenderToEdit && recipient.allowSenderToRemove && "Full permissions granted"} + {recipient.allowSenderToEdit && !recipient.allowSenderToRemove && "Edit permission granted"} + {!recipient.allowSenderToEdit && recipient.allowSenderToRemove && "Delete permission granted"} + {!recipient.allowSenderToEdit && !recipient.allowSenderToRemove && "No permissions granted"} +
+
+ {/* Animated Permission Controls */} +
+
+ {/* Edit Permission */} +
+ + {/* Tooltip */} +
+
+ Allow Edit Permission +
+
+
+
+ + {/* Delete Permission */} +
+ + {/* Tooltip */} +
+
+ Allow Delete Permission +
+
+
+
+
+
+
+ + {/* Settings Icon */} +
+

setShowAdvanceSettings((prev) => !prev)} + > + +

+ {/* Tooltip */} +
+
+ Advanced Settings +
+
+
+
+
+
+
+ )} + + {/* Name Inputs */} +
+ setOpenContactModal(true)} + onBlur={handleContactModalBlur} + value={recipient.firstName} + placeholder="First Name" + className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" + onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })} + /> + setOpenContactModal(true)} + onBlur={handleContactModalBlur} + value={recipient.lastName} + placeholder="Last Name" + className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" + onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })} + /> + setOpenContactModal(true)} + onBlur={handleContactModalBlur} + value={recipient.email} + placeholder="Recipient Email" + className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" + onChange={(e) => setRecipients({ ...recipient, email: e.target.value })} + /> +
+ + + {recipient.type === recipientType.INPERSONSIGNER &&
+ setOpenContactModal(true)} + onBlur={handleContactModalBlur} + value={recipient.hostName} + placeholder="Host Name" + className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" + onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })} + /> + setOpenContactModal(true)} + onBlur={handleContactModalBlur} + value={recipient.hostEmail} + placeholder="Host Email" + className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition" + onChange={(e) => setRecipients({ ...recipient, hostEmail: e.target.value })} + /> +
} + + {/* Contact Modal */} + {contactData.contacts.length > 0 && openContactModal && ( +
+ +
+ )} + + {/* Signing options + Add button in one line */} +
+
+ {[ + { label: "Signer", value: recipientType.SIGNER }, + { label: "In Person Signer", value: recipientType.INPERSONSIGNER }, + { label: "CC", value: recipientType.CC }, + ].map((option) => ( + + ))} +
+ + +
+ ); }; diff --git a/src/components/process/processStepOne/RecipientCards.tsx b/src/components/process/processStepOne/RecipientCards.tsx index 2e8bd3f..97897d8 100644 --- a/src/components/process/processStepOne/RecipientCards.tsx +++ b/src/components/process/processStepOne/RecipientCards.tsx @@ -34,6 +34,7 @@ const RecipientCards = () => { } const recipients = useSelector((state: RootState) => state.selectedRecipients.recipients); + console.log("recipients.length",recipients) // const signingOrder = useSelector((state: RootState) => state.selectedRecipients.signingOrder); const signType = useSelector((state: RootState) => state.selectedDocuments.signType); @@ -137,7 +138,7 @@ const RecipientCards = () => { {recipient.firstName} {recipient.lastName || ""}

- {recipient.email} | {recipient.type} + {recipient.email} | {recipient.type} {recipient.roleName ? `| Role : ${recipient.roleName}` : ""}

diff --git a/src/components/process/processStepOne/SelectedDocuments.tsx b/src/components/process/processStepOne/SelectedDocuments.tsx index 5d8509d..0a3fb61 100644 --- a/src/components/process/processStepOne/SelectedDocuments.tsx +++ b/src/components/process/processStepOne/SelectedDocuments.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react'; +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"; @@ -13,10 +13,21 @@ import DocumentCard from './DocumentCard'; import { useParams } from 'react-router-dom'; import { toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'; -const SelectedDocuments = () => { +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); + + useEffect(() => { + + console.log("Documents updated:", documents); + }, [documents]); + + + const templateId = useParams<{ templateId: string }>().templateId; let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); @@ -60,6 +71,7 @@ const SelectedDocuments = () => { formData.append("file", filesToUpload[0]) console.log(formData, "Line 59") // filesToUpload.forEach((file) => formData.append("document", file)); + dispatch(uploadDocuments({ formData, accountId, transactionId })) .unwrap() .then((res) => { @@ -79,6 +91,7 @@ const SelectedDocuments = () => { // Update selected documents after upload // dispatch(setSelectedDocuments([...documents, ...res])); + // debugger; setFiles([]); setModalOpen(false); }) @@ -102,7 +115,7 @@ const SelectedDocuments = () => { disabled={false} > {documents.map((doc) => ( - + ))} diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index 57ccd1c..f91f4c0 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -10,14 +10,13 @@ import { useNavigate, useParams } from 'react-router-dom'; import { IoMdClose } from 'react-icons/io'; -import { fetchRecipients } from '../../../store/slices/selectedRecipientsSlice'; +import { fetchRecipients, toggleToTemplates } from '../../../store/slices/selectedRecipientsSlice'; import { fetchDocuments } from '../../../store/slices/documentSlice'; import { FaQuestionCircle } from 'react-icons/fa'; import typeToIconMap from '../../../helper/iconMap'; import { IoAdd } from 'react-icons/io5'; import Loader from '../../dashboard/Loader'; -import { handleGroupBorderPosition } from '../../../utlity/tagGroupConUtils'; export interface DroppedItemsObj { [key: string]: DroppedItem[] @@ -42,7 +41,16 @@ interface DocumentContainerProps { } const DocumentContainer: React.FC = ({ isSend }) => { - const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); + let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; + const dispatch = useDispatch(); + const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); + const accountId = params.accountId; + let transactionId = params.transactionId + if (!transactionId) { + transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; + transactionId = params.templateId; + dispatch(toggleToTemplates()); + } if (!accountId || !transactionId) { window.location.href = "/"; return null; @@ -50,7 +58,6 @@ const DocumentContainer: React.FC = ({ isSend }) => { // const transactionId = useSelector((state: RootState) => state.transaction.transactionId) const navigate = useNavigate(); - const dispatch = useDispatch(); const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients); // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) @@ -70,7 +77,7 @@ const DocumentContainer: React.FC = ({ isSend }) => { const fetchTags = async () => { try { const requests = selectedRecipients.map((recipient) => - api.get(`/api/accounts/${accountId}/transactions/${transactionId}/recipients/${recipient.id}/tags`) + api.get(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipient.id}/tags`) ); const response = await Promise.all(requests); let recTags = {} @@ -154,7 +161,7 @@ const DocumentContainer: React.FC = ({ isSend }) => { const requests = Object.entries(updatedData).map(([recipientId, tags]) => { return api.put( - `/api/accounts/${accountId}/transactions/${transactionId}/recipients/${recipientId}/tags`, + `/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipientId}/tags`, tags ); }); @@ -282,7 +289,7 @@ const DocumentContainer: React.FC = ({ isSend }) => { sendTagsToRecipients(tagsData, accountId, transactionId); (async () => { - const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, { status: "SENT" }) + const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, {status: "SENT"}) console.log(res, "Check123") })() }, [isSend]) @@ -546,7 +553,8 @@ const DocumentContainer: React.FC = ({ isSend }) => { (async () => { if ("id" in tag) { try { - const response = await api.delete(`/api/accounts/${accountId}/transactions/${transactionId}/recipients/${recipientId}/tags/${tag.id}`) + + const response = await api.delete(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipientId}/tags/${tag.id}`) if (response.status !== 200) throw "Tag Deletion error" } catch (err) { console.error("Tag Deletion error") @@ -563,28 +571,9 @@ const DocumentContainer: React.FC = ({ isSend }) => { })() } - // const handleAddRadioClick = (key: string) => { - // console.log("Handle Radio Clicled", key) - // console.log(droppedItems, "line 525") - // } - - const handleAddGroupItemClicked = (key: string, item) => { - - - const groupId = item.group; - - item = { ...item, dimensions: { ...item.dimensions, top: item.dimensions.top + item.dimensions.height + 20 } } - - // dimensions: { ...item.dimensions, left: newLeft, top: newTop } - - setTimeout(() => { - handleIGroupBorderPosition(item.group) - }, 0) - - setDroppedItems({ - ...droppedItems, - [key]: [...droppedItems[key], { ...item, isGroupExist: true }], - }); + const handleAddRadioClick = (key:string) => { + console.log("Handle Radio Clicled", key) + console.log(droppedItems, "line 525") } // const handleAddRadio = (e: React.MouseEvent, key: string, index: number) => { @@ -912,8 +901,7 @@ const DocumentContainer: React.FC = ({ isSend }) => { disabled: false, stepSize: 5, className: '' - }} /> - } + }} />} {/* Center Doc Container - END */} ) diff --git a/src/components/process/processStepTwo/LeftSideBar.tsx b/src/components/process/processStepTwo/LeftSideBar.tsx index c689615..6a97bc1 100644 --- a/src/components/process/processStepTwo/LeftSideBar.tsx +++ b/src/components/process/processStepTwo/LeftSideBar.tsx @@ -3,7 +3,7 @@ import { RootState } from '../../../store/store'; import { useDispatch, useSelector } from 'react-redux'; import FieldList from './FieldList'; -import { addColorsToRecipient, fetchRecipients } from '../../../store/slices/selectedRecipientsSlice'; +import { addColorsToRecipient, fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'; import { IoCheckmark } from 'react-icons/io5' import { MdKeyboardArrowDown } from 'react-icons/md' @@ -17,13 +17,21 @@ interface LeftSideBarProps { } const LeftSideBar: React.FC = ({ isFields, selectedRecipientIndx, setSelectedRecipient }) => { - const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); + let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; + const dispatch = useDispatch(); + const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); + const accountId = params.accountId; + let transactionId = params.transactionId + if (!transactionId) { + transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; + transactionId = params.templateId; + dispatch(toggleToTemplates()); + } if (!accountId || !transactionId) { window.location.href = "/"; return null; } - const dispatch = useDispatch(); const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients) const selectedDocuments = useSelector((state: RootState) => state.documents.documents) @@ -88,7 +96,7 @@ const LeftSideBar: React.FC = ({ isFields, selectedRecipientIn setDropDownToggle(false); }, 0) }} - > + >

{/* */} diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index d6b2268..533d067 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -8,7 +8,7 @@ import { IoIosDocument } from 'react-icons/io' import { LuTableOfContents } from 'react-icons/lu' import { MdKeyboardArrowDown } from 'react-icons/md' import { RiResetLeftLine, RiResetRightLine } from 'react-icons/ri' -import { fetchRecipients } from '../../../store/slices/selectedRecipientsSlice' +import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice' import { fetchDocuments } from '../../../store/slices/documentSlice' import { useNavigate, useParams } from 'react-router-dom' @@ -45,7 +45,16 @@ const fitObj = [ ] const NavTwo: React.FC = ({ isFields, setIsFields, isSend, setIsSend }) => { - const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); + let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; + const dispatch = useDispatch(); + const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); + const accountId = params.accountId; + let transactionId = params.transactionId + if (!transactionId) { + transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; + transactionId = params.templateId; + dispatch(toggleToTemplates()); + } if (!accountId || !transactionId) { window.location.href = "/"; return null; @@ -70,8 +79,6 @@ const NavTwo: React.FC = ({ isFields, setIsFields, isSend, setIsSen const [nav2RecToggle, setNav2RecToggle] = useState(false); const [nav2DocToggle, setNav2DocToggle] = useState(false); - const dispatch = useDispatch(); - function handleSend() { // dispatch(showLoader("Loading ...")); if (isSend === null) { diff --git a/src/components/process/processStepTwo/ProcessStepTwo.tsx b/src/components/process/processStepTwo/ProcessStepTwo.tsx index b9fb584..19dbaad 100644 --- a/src/components/process/processStepTwo/ProcessStepTwo.tsx +++ b/src/components/process/processStepTwo/ProcessStepTwo.tsx @@ -10,6 +10,7 @@ import Loader from '../../dashboard/Loader'; import { hideLoader } from '../../../store/slices/loaderSlice'; const ProcessStepTwo = () => { + const [isFields, setIsFields] = useState(true); const [selectedRecipient, setSelectedRecipient] = useState(0); const [isSend, setIsSend] = useState(null); diff --git a/src/components/templates/Templates.tsx b/src/components/templates/Templates.tsx index 15fd0ae..52a25f8 100644 --- a/src/components/templates/Templates.tsx +++ b/src/components/templates/Templates.tsx @@ -1,16 +1,26 @@ -import { useState } from "react"; +import { use, useEffect, useState } from "react"; import { PiDotsThreeOutlineVerticalFill } from "react-icons/pi"; import { Bounce, toast } from "react-toastify"; import api from "../../config/api"; import { IoMdAdd } from "react-icons/io"; import { useNavigate } from "react-router-dom"; import { useSelector } from "react-redux"; +import { FaSpinner } from "react-icons/fa"; +import LoaderUi from "../../helper/LoaderUi"; + + const Templates = () => { const [activeFilter, setActiveFilter] = useState("All"); - + const [loading, setLoading] = useState(false) + const [templates, setTemplates] = useState([]) const accountDetails = useSelector((state: any) => state.user.account); const accountId = accountDetails.id || ""; + const [showMenu, setShowMenu] = useState(null); + + const [activeAccountId, setActiveAccountId] = useState( + JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') + ); const navigate = useNavigate(); @@ -18,11 +28,45 @@ const Templates = () => { const handleAddTemplate = async () => { console.log("handleAddTemplate called"); - const res = await api.post(`/api/accounts/${accountId}/templates`); + try { + const res = await api.post(`/api/accounts/${activeAccountId}/templates`); + navigate(`/process-step-one/${activeAccountId}/template/${res.data}`) + } catch (error) { + console.log(error) + } - navigate(`/process-step-one/${accountId}/template/${res.data}`) } + const getAllTemplates = async () => { + setLoading(true) + try { + let res = await api.get(`/api/accounts/${activeAccountId}/templates`) + let templateData = res?.data + setTemplates(templateData) + } catch (error) { + console.log(error) + } finally { + setLoading(false) + } + } + + useEffect(() => { + if (activeAccountId) { + getAllTemplates() + } + }, [activeAccountId]) + + + const formatDate = (timestamp: number) => { + return new Date(timestamp * 1000).toLocaleString(); + }; + +const handleUseFun = (id) => { +alert(id) +} + + + return (
{/* Header */} @@ -56,42 +100,109 @@ const Templates = () => {
{/* Table */} -
- - +
+
+ - - - - - - - - - - - - - - - + + + + + + - + + + {templates.length > 0 ? ( + + {templates.map((item, index) => { + return ( + + + + + + + {/* Dropdown Trigger + Tooltip */} + + + + ); + })} + + + ) : ( + + + + + + + )}
NameOwnerCreatedLast Updated
+
{/* */}
- -
Test TemplateUser 12025-07-14Just now - - NameCreatedLast UpdatedUseAction
+
+ {/* */} +
+
{item.name || 'Dummy'}{formatDate(item.createdAt)}{formatDate(item.lastModifiedAt)} handleUseFun(item.id)}>Use + + + {showMenu === index && ( +
{ + // Ensure it only closes if focus truly leaves the container + if (!e.currentTarget.contains(e.relatedTarget)) { + setShowMenu(null); + } + }} + > + + +
+ )} +
+
+ {/* Pulsing background div */} +
+ + {/* Loader stays on top, no animation */} + +
+
+ +
); }; export default Templates; + + + diff --git a/src/config/api.tsx b/src/config/api.tsx index 0d90aa8..c7606bf 100644 --- a/src/config/api.tsx +++ b/src/config/api.tsx @@ -26,6 +26,7 @@ api.interceptors.response.use( return response; }, async error => { + const originalRequest = error.config; // Prevent multiple refresh attempts // if (error.response?.status === 401 || error.response?.status === 500) { diff --git a/src/helper/LoaderUi.tsx b/src/helper/LoaderUi.tsx new file mode 100644 index 0000000..c118cb4 --- /dev/null +++ b/src/helper/LoaderUi.tsx @@ -0,0 +1,19 @@ + +interface StyleProps { + classStyle : string +} + +const LoaderUi = ({ classStyle }:StyleProps) => { + return ( +
+ + + + + + +
+ ) +} + +export default LoaderUi \ No newline at end of file diff --git a/src/index.css b/src/index.css index 71fe6fe..ba2b68c 100644 --- a/src/index.css +++ b/src/index.css @@ -128,4 +128,209 @@ canvas { /* Group Border - END */ -/* Loader CSS */ \ No newline at end of file + /* Loader CSS */ + /* From Uiverse.io by Juanes200122 */ +.pl { + width: 6em; + height: 6em; +} + +.pl__ring { + animation: ringA 2s linear infinite; +} + +.pl__ring--a { + stroke: #41644A; +} + +.pl__ring--b { + animation-name: ringB; + stroke: #7e7e7e; +} + +.pl__ring--c { + animation-name: ringC; + stroke: #686868; +} + +.pl__ring--d { + animation-name: ringD; + stroke: #41644A; +} + + /* Animations */ +@keyframes ringA { + from, 4% { + stroke-dasharray: 0 660; + stroke-width: 20; + stroke-dashoffset: -330; + } + + 12% { + stroke-dasharray: 60 600; + stroke-width: 30; + stroke-dashoffset: -335; + } + + 32% { + stroke-dasharray: 60 600; + stroke-width: 30; + stroke-dashoffset: -595; + } + + 40%, 54% { + stroke-dasharray: 0 660; + stroke-width: 20; + stroke-dashoffset: -660; + } + + 62% { + stroke-dasharray: 60 600; + stroke-width: 30; + stroke-dashoffset: -665; + } + + 82% { + stroke-dasharray: 60 600; + stroke-width: 30; + stroke-dashoffset: -925; + } + + 90%, to { + stroke-dasharray: 0 660; + stroke-width: 20; + stroke-dashoffset: -990; + } +} + +@keyframes ringB { + from, 12% { + stroke-dasharray: 0 220; + stroke-width: 20; + stroke-dashoffset: -110; + } + + 20% { + stroke-dasharray: 20 200; + stroke-width: 30; + stroke-dashoffset: -115; + } + + 40% { + stroke-dasharray: 20 200; + stroke-width: 30; + stroke-dashoffset: -195; + } + + 48%, 62% { + stroke-dasharray: 0 220; + stroke-width: 20; + stroke-dashoffset: -220; + } + + 70% { + stroke-dasharray: 20 200; + stroke-width: 30; + stroke-dashoffset: -225; + } + + 90% { + stroke-dasharray: 20 200; + stroke-width: 30; + stroke-dashoffset: -305; + } + + 98%, to { + stroke-dasharray: 0 220; + stroke-width: 20; + stroke-dashoffset: -330; + } +} + +@keyframes ringC { + from { + stroke-dasharray: 0 440; + stroke-width: 20; + stroke-dashoffset: 0; + } + + 8% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -5; + } + + 28% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -175; + } + + 36%, 58% { + stroke-dasharray: 0 440; + stroke-width: 20; + stroke-dashoffset: -220; + } + + 66% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -225; + } + + 86% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -395; + } + + 94%, to { + stroke-dasharray: 0 440; + stroke-width: 20; + stroke-dashoffset: -440; + } +} + +@keyframes ringD { + from, 8% { + stroke-dasharray: 0 440; + stroke-width: 20; + stroke-dashoffset: 0; + } + + 16% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -5; + } + + 36% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -175; + } + + 44%, 50% { + stroke-dasharray: 0 440; + stroke-width: 20; + stroke-dashoffset: -220; + } + + 58% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -225; + } + + 78% { + stroke-dasharray: 40 400; + stroke-width: 30; + stroke-dashoffset: -395; + } + + 86%, to { + stroke-dasharray: 0 440; + stroke-width: 20; + stroke-dashoffset: -440; + } +} \ No newline at end of file diff --git a/src/store/slices/documentSlice.ts b/src/store/slices/documentSlice.ts index ae655b7..56cbdf9 100644 --- a/src/store/slices/documentSlice.ts +++ b/src/store/slices/documentSlice.ts @@ -223,6 +223,7 @@ export const fetchDocuments = createAsyncThunk< // ----------------------- // Thunk: Upload Documents // ----------------------- +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const uploadDocuments = createAsyncThunk< Document[], { formData: FormData; accountId: string; transactionId: string } @@ -237,6 +238,10 @@ export const uploadDocuments = createAsyncThunk< }; const toggle = state.selectedRecipients.transactionTemplateToggle; + console.log(`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`) + + + const response = await api.post( `/api/accounts/${accountId}/${toggle}/${transactionId}/documents`, formData, @@ -246,7 +251,8 @@ export const uploadDocuments = createAsyncThunk< }, } ); - + console.log("// await sleep(5000);", response.data) + // await sleep(5000); dispatch(hideLoader()); return [response.data]; } catch (error: any) { diff --git a/src/store/slices/selectedRecipientsSlice.tsx b/src/store/slices/selectedRecipientsSlice.tsx index 85c610c..87ac190 100644 --- a/src/store/slices/selectedRecipientsSlice.tsx +++ b/src/store/slices/selectedRecipientsSlice.tsx @@ -351,6 +351,7 @@ export interface Recipient { hostEmail: string; type: recipientType; color?: string; + roleName:string } export interface SelectedRecipientInt {