diff --git a/.env b/.env index a291dd4..03d87bb 100644 --- a/.env +++ b/.env @@ -6,5 +6,5 @@ # VITE_BASE_URI="https://smashing-albacore-engaging.ngrok-free.app" # VITE_BASE_URI="https://basically-polished-yeti.ngrok-free.app" # VITE_BASE_URI="https://sacha-countryfied-evolutionistically.ngrok-free.dev" -VITE_BASE_URI="http://52.10.89.255:8083" -VITE_SSE_URI="http://52.10.89.255:8083" \ No newline at end of file +VITE_BASE_URI="http://52.10.89.255" +VITE_SSE_URI="http://52.10.89.255" \ No newline at end of file diff --git a/index.html b/index.html index c118459..17f9bd3 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Sign Made Easy diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000..63c92c5 Binary files /dev/null and b/public/favicon.png differ diff --git a/public/favicon/android-chrome-192x192.png b/public/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..021e776 Binary files /dev/null and b/public/favicon/android-chrome-192x192.png differ diff --git a/public/favicon/android-chrome-512x512.png b/public/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..81c55b1 Binary files /dev/null and b/public/favicon/android-chrome-512x512.png differ diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png new file mode 100644 index 0000000..70c1240 Binary files /dev/null and b/public/favicon/apple-touch-icon.png differ diff --git a/public/favicon/favicon-16x16.png b/public/favicon/favicon-16x16.png new file mode 100644 index 0000000..cfb3fbf Binary files /dev/null and b/public/favicon/favicon-16x16.png differ diff --git a/public/favicon/favicon-32x32.png b/public/favicon/favicon-32x32.png new file mode 100644 index 0000000..4d32c59 Binary files /dev/null and b/public/favicon/favicon-32x32.png differ diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico new file mode 100644 index 0000000..8786e4f Binary files /dev/null and b/public/favicon/favicon.ico differ diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/public/favicon/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 8c731dc..94b09fe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,6 +25,8 @@ import Contacts from "./components/contacts/Contacts"; import TransactionSentSuccess from "./components/process/CompletionScreen/TransactionSentSuccess"; import CompletionScreen from "./components/client/CompletionScreen"; import SessionExpired from "./components/client/SessionExpired"; + +import ExpiredPage from "./components/ExpiredPage"; const App = () => { return ( @@ -113,8 +115,10 @@ const App = () => { } /> } /> - } /> - } /> + } /> + } /> + + } /> {/* Links work wothout Login */} diff --git a/src/components/ExpiredPage.tsx b/src/components/ExpiredPage.tsx new file mode 100644 index 0000000..516bd11 --- /dev/null +++ b/src/components/ExpiredPage.tsx @@ -0,0 +1,72 @@ +"use client" + +import { FiClock } from "react-icons/fi" +import { Link } from "react-router-dom" + +export interface ExpiredPageProps { + title?: string + message?: string + actionLabel?: string + actionHref?: string + onAction?: () => void + secondaryLabel?: string + secondaryHref?: string +} + +export default function ExpiredPage({ + title = "Link expired", + message = "This link has expired or is no longer valid. Request a new one to continue.", + actionLabel = "Go back home", + actionHref = "/", + onAction, + secondaryLabel, + secondaryHref, +}: ExpiredPageProps) { + return ( +
+
+
+ {/* Decorative icon */} +
+ +
+

+ {title} +

+

{message}

+
+ +
+ {onAction ? ( + // Primary button (handler) + + ) : ( + // Primary button (link) + + {actionLabel} + + )} + + {secondaryHref && secondaryLabel ? ( + + {secondaryLabel} + + ) : null} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index 333deec..acd414a 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -12,6 +12,7 @@ import { CgSandClock } from "react-icons/cg"; import TransactionDataTable from "./TransactionDataTable"; import { useSelector } from "react-redux"; import { RootState } from "../../store/store"; +import { RiDraftLine } from "react-icons/ri"; interface Transaction { id: number; @@ -45,15 +46,25 @@ const Account = () => { shadowColor: "shadow-yellow-100", navigateTo: "/transactions/waiting_for_response" }, + // { + // title: "Expiring Soon", + // count: data.expiring || 0, + // icon: , + // iconBg: "bg-red-100 border-red-200", + // textColor: "text-red-600", + // gradient: "bg-white", + // shadowColor: "shadow-red-100", + // navigateTo: "/transactions/expiring_soon" + // }, { - title: "Expiring Soon", - count: data.expiring || 0, - icon: , - iconBg: "bg-red-100 border-red-200", - textColor: "text-red-600", + title: "Draft", + count: data.draft || 0, + icon: , + iconBg: "bg-indigo-100 border-indigo-200", + textColor: "text-indigo-600", gradient: "bg-white", - shadowColor: "shadow-red-100", - navigateTo: "/transactions/expiring_soon" + shadowColor: "shadow-indigo-100", + navigateTo: "/transactions/draft" }, { title: "Completed", @@ -185,17 +196,17 @@ const Account = () => { } let res = await api.post(`/api/v1/accounts/${data.i_accountName}/invite`, payloadInvite) if (res?.data) { - toast.success("Invite Sent Successfully...!", { - position: "top-center", - autoClose: 1500, - hideProgressBar: false, - closeOnClick: false, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "light", - transition: Bounce, - }); + // toast.success("Invite Sent Successfully...!", { + // position: "top-center", + // autoClose: 1500, + // hideProgressBar: false, + // closeOnClick: false, + // pauseOnHover: true, + // draggable: true, + // progress: undefined, + // theme: "light", + // transition: Bounce, + // }); await fetchTransactions() setIsModalOpen(false); } diff --git a/src/components/accounts/Accounts.tsx b/src/components/accounts/Accounts.tsx index ddca8a1..0eaf64f 100644 --- a/src/components/accounts/Accounts.tsx +++ b/src/components/accounts/Accounts.tsx @@ -62,17 +62,17 @@ const Accounts = () => { } let res = await api.post(`/api/v1/user/accounts`, payloadAccount) if (res?.data) { - toast.success("Account Add Successfully...!", { - position: "top-center", - autoClose: 5000, - hideProgressBar: false, - closeOnClick: false, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "light", - transition: Bounce, - }); + // toast.success("Account Add Successfully...!", { + // position: "top-center", + // autoClose: 5000, + // hideProgressBar: false, + // closeOnClick: false, + // pauseOnHover: true, + // draggable: true, + // progress: undefined, + // theme: "light", + // transition: Bounce, + // }); await fetchAccounts() setIsModalOpen(false); } diff --git a/src/components/accounts/DownloadDocument.tsx b/src/components/accounts/DownloadDocument.tsx index 878bb6b..5b8ee86 100644 --- a/src/components/accounts/DownloadDocument.tsx +++ b/src/components/accounts/DownloadDocument.tsx @@ -40,15 +40,15 @@ const DownloadDocument: React.FC = ({ showMenu, setShowMenu Choose file to download
diff --git a/src/components/accounts/TransactionDataTable.tsx b/src/components/accounts/TransactionDataTable.tsx index 31e6235..09cf9b1 100644 --- a/src/components/accounts/TransactionDataTable.tsx +++ b/src/components/accounts/TransactionDataTable.tsx @@ -52,7 +52,7 @@ const TransactionDataTable = ({ activeAccountId }) => { let addParams = `?sortBy=createdAt desc&page=${page}&size=${Math.min(size, 30)}` switch(filter) { case "action_required": - addParams = addParams+"&filter=inbox" + addParams = addParams+"&filter=inbox&status=SENT" break; case "waiting_for_response": addParams = addParams+"&status=sent" @@ -61,7 +61,10 @@ const TransactionDataTable = ({ activeAccountId }) => { addParams = addParams+"&filter=inbox" break; case "completed": - addParams = addParams+"&status=completed&filter=completed" + addParams = addParams+"&filter=completed" + break; + case "draft": + addParams = addParams+"&filter=draft" break; default: break; diff --git a/src/components/auth/UsertAuthUI.tsx b/src/components/auth/UsertAuthUI.tsx index 202d9bc..67ec3b0 100644 --- a/src/components/auth/UsertAuthUI.tsx +++ b/src/components/auth/UsertAuthUI.tsx @@ -8,6 +8,7 @@ import { AppDispatch } from "../../store/store"; import { fetchUserDetails } from "../../store/slices/userSlice"; import LoginRegisterImg from "../../assets/illustration_1_.png"; import SMELogo from "../../assets/SMELogo.png"; +import api from "../../config/api"; function UserAuthUI() { const [isLogin, setIsLogin] = useState(true); @@ -34,6 +35,12 @@ function UserAuthUI() { // Your register API call logic here }; + useEffect(() => { + (async () => { + await api.get(`/api/v1/user/details`); + })() + }, []); + return (
{/* Left Side - Branding */} diff --git a/src/components/client/CompletionScreen.tsx b/src/components/client/CompletionScreen.tsx index d360607..581094d 100644 --- a/src/components/client/CompletionScreen.tsx +++ b/src/components/client/CompletionScreen.tsx @@ -1,5 +1,6 @@ -import React from "react"; +import React, { useEffect } from "react"; import { RiFileList3Line } from "react-icons/ri"; +import confetti from 'canvas-confetti' interface CompletionScreenProps { hit?: string; @@ -14,6 +15,40 @@ const CompletionScreen: React.FC = ({ message = "Your document is now complete and securely stored. We’ve emailed you a copy for your reference.", textColorSuccess = true }) => { + + + useEffect(() => { + + + const duration = 3000; + const end = Date.now() + duration; + + const interval = setInterval(() => { + if (Date.now() > end) { + clearInterval(interval); + return; + } + + confetti({ + particleCount: 5, // fewer particles per burst for smoothness + angle: 90, // falling straight down + spread: 30, // slight spread + startVelocity: 8, // gentle falling speed + origin: { + x: Math.random(), // random horizontal position across the top + y: 0, + }, + scalar: 1, + zIndex: 9999, + }); + }, 50); // fire every 50ms for continuous effect + + return () => clearInterval(interval); + + }, []); + + + return (
@@ -49,7 +84,7 @@ const CompletionScreen: React.FC = ({ {/* */} -

+

Access your documents anytime — just Login

diff --git a/src/components/client/SignDocument.tsx b/src/components/client/SignDocument.tsx index a3aa60c..7c7707c 100644 --- a/src/components/client/SignDocument.tsx +++ b/src/components/client/SignDocument.tsx @@ -33,6 +33,7 @@ import CompletionScreen from "./CompletionScreen"; import { GrPrint } from "react-icons/gr"; import ClientDownloadBtn from "./ClientDownloadBtn"; import ClientAuditModal from "./ClientAuditModal"; +import axiosInstance from "../../config/axiosInstance"; @@ -250,9 +251,10 @@ const SignDocument = () => { // Ping Request - START useEffect(() => { const intervalId = setInterval(() => { - axios.get( - `${import.meta.env.VITE_BASE_URI}/api/v1/session/ping?sessionId=${recipientId}`, + axiosInstance.get( + `/api/v1/session/ping?sessionId=${recipientId}`, { + maxRedirects: 5, headers: { "ngrok-skip-browser-warning": "69420" }, } ).catch(err => console.error("Ping failed:", err)); @@ -333,7 +335,6 @@ const SignDocument = () => { accepted: true, index: index } - setSignConfObj((prev) => { return ( { ...prev, [index]: { ...newObj } } @@ -343,8 +344,9 @@ const SignDocument = () => { }, [saved]) const fetchData = (isSign: boolean = false) => { - axios.get(`${import.meta.env.VITE_BASE_URI}/api/v1/sign/${recipientId}`, + axiosInstance.get(`/api/v1/sign/${recipientId}`, { + maxRedirects: 5, headers: { "ngrok-skip-browser-warning": "69420", } }) @@ -376,9 +378,35 @@ const SignDocument = () => { // setShowCompletionScreen(true) setTransparentTags(true) setHideTags(true) - } + + if (res?.data?.tagJsons && res?.data?.tagJsons.length > 0) { + for (const tag of res?.data?.tagJsons) { + if (tag.type === "SIGNATURE" && "value" in tag && tag.value) { + + axiosInstance.get(`/api/v1/sign/${recipientId}/signatures/${tag.value}`).then((res) => { + console.log(res, "line 386") + }).catch((err) => { + console.log(err) + }) + + // const [saved, setSaved] = useState({ + // signatureId: null, + // saveType: null, + // save: false, + // signature: null, + // initial: null, + // }); + + + } else if (tag.type === "INITIALS") { + + } + } + } + + dispatch(hideLoader()); } @@ -389,9 +417,28 @@ const SignDocument = () => { } const handleFormSubmit = async () => { + let newSignatureIdForCreateDefault = null; + + if (saved.isDefaultAndClicked) { + try { + const res = await axiosInstance.post(`/api/v1/sign/${recipientId}/signatures`, { + signatureId: saved.signatureId + }); + + if (res.status === 201 || res.status === 202) { + newSignatureIdForCreateDefault = res.data.id; + } + } catch (err) { + console.log(err); + } + } + const submitData: TagsSubmitDataObj = { tags: [] } + const createTagsAndSubmit: TagsSubmitDataObj = { + tags: [] + } if (!droppedItems) return; @@ -407,13 +454,13 @@ const SignDocument = () => { return; } - for (const tag of droppedItems) { + for (let tag of droppedItems) { if ("required" in tag && tag.required && ((tag.type !== "SIGNATURE" && tag.type !== "INITIALS") && (tag.value === undefined || tag.value === null || tag.value === "" || !("value" in tag)))) { // handleToastError(`${tag.label} is required`) return; } - if (tag.type === "SIGNATURE" || tag.type === "INITIALS") { + if ("id" in tag && (tag.type === "SIGNATURE" || tag.type === "INITIALS")) { const tagData: TagSubmitData = { id: tag.id as string, type: tag.type, @@ -422,7 +469,7 @@ const SignDocument = () => { value: saved.signatureId as string } submitData.tags.push(tagData) - } else { + } else if ("id" in tag) { const tagData: TagSubmitData = { id: tag.id as string, type: tag.type, @@ -431,20 +478,49 @@ const SignDocument = () => { value: tag.value ? tag.value as string : "" } submitData.tags.push(tagData); + } else if (tag.type === "SIGNATURE" || tag.type === "INITIALS") { + // Not getting recipient id in details it is undefined - not working self sign + tag.recipientId = recipient.id; + tag.documentId = Number(tag.documentId) + + createTagsAndSubmit.tags.push( + { + ...tag, + type: tag.type, + // subType: saved.saveType as string, + signed: true, + value: newSignatureIdForCreateDefault as string + }) + } else { + tag.recipientId = recipient.id + tag.documentId = Number(tag.documentId) + createTagsAndSubmit.tags.push( + { + ...tag, + type: tag.type, + // subType: tag.subType ? tag.subType : "", + signed: true, + value: tag.value ? tag.value as string : "" + }) } } // return; try { dispatch(showLoader("Signing ...")); - await axios.put(`${import.meta.env.VITE_BASE_URI}/api/v1/sign/${recipientId}/tags`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } }) - const signRes = await axios.post(`${import.meta.env.VITE_BASE_URI}/api/v1/sign/${recipientId}`, {}, { headers: { "ngrok-skip-browser-warning": "69420", } }) + if (submitData.tags.length) { + await axiosInstance.put(`/api/v1/sign/${recipientId}/tags`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } }) + } else if (createTagsAndSubmit.tags.length) { + await axiosInstance.post(`/api/v1/sign/${recipientId}/tags`, createTagsAndSubmit, { headers: { "ngrok-skip-browser-warning": "69420", } }) + } + + const signRes = await axiosInstance.post(`/api/v1/sign/${recipientId}`, {}, { headers: { "ngrok-skip-browser-warning": "69420", } }) dispatch(hideLoader()); if (signRes.status === 200) { dispatch(hideLoader()); setTransparentTags(true) - setShowConfetti(true) + // setShowConfetti(true) // setShowCompletionScreen(true) setSelectedTag(null) // navigate("/transaction-complete") @@ -548,7 +624,21 @@ const SignDocument = () => { useEffect(() => { const allRequiredFields = droppedItems.filter((item, idx) => { if (!item.required) return false - if ((item.required && item.value && item.type != "SIGNATURE" && item.type != "INITIALS") || (idx in signConfObj && saved.save)) return false + if ((item.required && item.value && item.type != "SIGNATURE" && item.type != "INITIALS") || + + + ( + (item.id !== null && item.id !== undefined && item.id !== "") + ? ( + (`${item.id}`) in signConfObj && signConfObj[`${item.id}`].accepted + ) : ( + (`${idx}`) in signConfObj && signConfObj[`${idx}`].accepted + ) + ) + + + + ) return false return true }) @@ -558,7 +648,7 @@ const SignDocument = () => { }, 0) setHorizontalFormLoader({ requiredFilled: (totalRequiredFields - allRequiredFields.length), totalRequired: totalRequiredFields }) - setRequiredFilled(allRequiredFields.length ? false : true) + setRequiredFilled(allRequiredFields.length || !droppedItems.length ? false : true) }, [droppedItems, signConfObj]) @@ -663,7 +753,7 @@ const SignDocument = () => { const handleDeclineConfirm = async (reason: string, comments: string) => { try { - await axios.post(`${import.meta.env.VITE_BASE_URI}/api/v1/sign/${recipientId}/decline`, { reason: reason + ", " + comments }); + await axiosInstance.post(`/api/v1/sign/${recipientId}/decline`, { reason: reason + ", " + comments }); setSignerConsent((prev) => ({ ...prev, decline: true })); setRecSignStatus("DECLINED"); // setShowCompletionScreen(true); @@ -673,7 +763,7 @@ const SignDocument = () => { } const handleReassignConfirm = (data: { firstName: string; lastName: string; email: string; message: string }) => { - axios.post(`${import.meta.env.VITE_BASE_URI}/api/v1/sign/${recipientId}/reassign`, { + axiosInstance.post(`/api/v1/sign/${recipientId}/reassign`, { name: data.firstName ? data.firstName + " " + data.lastName : data.lastName, email: data.email, message: data.message, @@ -845,77 +935,27 @@ const SignDocument = () => { transactionId={"1"} /> - {!transparentTags && recipient?.type !== "CC" && <> -
-

By Signing I agree Sign Made Easy Terms & Conditions.

- {/*
- handleTagNavClick("P")} - className="bg-[var(--mainPrimaryBtn)] p-1 text-2xl text-white rounded-md" /> -
- {focusElement?.navigationOrder} - / - {droppedItems.length} - Fields -
- handleTagNavClick("N")} - className="bg-[var(--mainPrimaryBtn)] p-1 text-2xl text-white rounded-md" /> -
*/} -
- {/*

Sign

*/} - -
- {/* FIT - START */} -
setIsFitDd(false)} - > -
setIsFitDd(!isFitDd)} - className='bg-white flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm ml-2'> -

Fit

- -
- - {isFitDd &&
- {fitObj.map((val, indx) => { - return (
setSelFitIndx(indx)} - className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'> -
-

{val.label}

- {selFitIndx === indx && } -
-
) - }) - } -
} -
- {/* FIT - END */} -
-
- } - - - {!transparentTags && recipient?.type !== "CC" && <> -
+ {!transparentTags && recipient?.type !== "CC" && ( +

All Set to Wrap This Up?

-

You've completed the required fields. Review your work, then select Finish.

+

+ You've completed the required fields. Review your work, then select Finish. +

+ disabled={!requiredFilled} + className="font-semibold text-sm px-8 py-2 bg-slate-100 rounded-md text-[var(--primary)] cursor-pointer" + > + Finish +
- } + )}
@@ -944,13 +984,13 @@ const SignDocument = () => { {/* Document Container Start */} -
+
{/* Left Side pages start */} {/*
*/}
+ className={`absolute w-[220px] left-0 top-[42px] border-r-2 sm:border-none border-slate-200 z-10 sm:static sm:w-auto sm:col-span-2 h-full p-4 overflow-y-auto ${recipient?.type !== "CC" ? "bg-white" : ""} ${!provideTags && isFields ? "hidden" : ""}`}> {!transparentTags &&
@@ -1017,13 +1057,13 @@ const SignDocument = () => { style={{ "width": page.width, "height": page.height, "maxWidth": "100%", "maxHeight": "100%" }} draggable="false" // onLoad={(imgElem) => { - // setPageScales((prev) => ({ - // ...prev, - // [page.index]: { - // x: imgElem.clientWidth / page.width, - // y: imgElem.clientHeight / page.height, - // }, - // })); + // setPageScales((prev) => ({ + // ...prev, + // [page.index]: { + // x: imgElem.clientWidth / page.width, + // y: imgElem.clientHeight / page.height, + // }, + // })); // }} width={page.width} height={page.height} @@ -1182,6 +1222,67 @@ const SignDocument = () => { // onConfirm={({ firstName, lastName, email, message }) => handleReassignConfirm({ firstName, lastName, email, message })} /> + + + {!transparentTags && recipient?.type !== "CC" && <> +
+

By Signing I agree Sign Made Easy Terms & Conditions.

+ {/*
+ handleTagNavClick("P")} + className="bg-[var(--mainPrimaryBtn)] p-1 text-2xl text-white rounded-md" /> +
+ {focusElement?.navigationOrder} + / + {droppedItems.length} + Fields +
+ handleTagNavClick("N")} + className="bg-[var(--mainPrimaryBtn)] p-1 text-2xl text-white rounded-md" /> +
*/} +
+ {/*

Sign

*/} + +
+ {/* FIT - START */} +
setIsFitDd(false)} + > +
setIsFitDd(!isFitDd)} + className='bg-white flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm ml-2'> +

Fit

+ +
+ + {isFitDd &&
+ {fitObj.map((val, indx) => { + return (
setSelFitIndx(indx)} + className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'> +
+

{val.label}

+ {selFitIndx === indx && } +
+
) + }) + } +
} +
+ {/* FIT - END */} +
+
+ } + +
) } diff --git a/src/components/client/TagsCon.tsx b/src/components/client/TagsCon.tsx index 192a64f..898ad82 100644 --- a/src/components/client/TagsCon.tsx +++ b/src/components/client/TagsCon.tsx @@ -238,8 +238,8 @@ const TagsCon: React.FC = ({ // }); } - const handleSignClick = async (index: number, id: number) => { - + const handleSignClick = async (index: number, id: number | undefined) => { + id = (id !== undefined && id !== null) ? id : index setSignTagId(id) if (saved.save) { if (saved.isDefaultAndClicked) { @@ -256,15 +256,15 @@ const TagsCon: React.FC = ({ } - if (index in signConfObj) { + if (id in signConfObj) { setOpenSign(true); return }; setSignConfObj({ ...signConfObj, - [index]: { + [id]: { accepted: true, - index: index + index: id } }) return @@ -279,9 +279,9 @@ const TagsCon: React.FC = ({ // }) // }) const newObj = { - [index]: { + [id]: { "accepted": false, - index: index + index: id } } setSignConfObj({ ...newObj }) @@ -302,10 +302,12 @@ const TagsCon: React.FC = ({ } + console.log(signConfObj, "Line 305") + return ( <> {currDocTags.map((item, index) => { - if (item.pageIndex !== pageIndex || item.documentId != documentId) return <>; + if (item.pageIndex !== pageIndex || Number(item.documentId) !== Number(documentId)) return <>; console.log('pageWidth:', pageWidth, 'computed left:', (item.dimensions.left / pageWidth) * 100); return (
= ({ > {/* */} { - (!transparentTags && !hideTags && !(saved.save && (index in signConfObj && signConfObj[index].accepted))) ? ( + (!transparentTags && !hideTags && !(saved.save && + ( + (item.id !== null && item.id !== undefined && item.id !== "") + ? ( + (`${item.id}`) in signConfObj && signConfObj[`${item.id}`].accepted + ) : ( + (`${index}`) in signConfObj && signConfObj[`${index}`].accepted + ) + ) + )) ? ( ) : saved.saveType === "SELECT" ? ( <>{saved.fullName} @@ -647,7 +658,16 @@ const TagsCon: React.FC = ({ className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`} // style={{ height: item.dimensions.height + "px" }} > - {((!transparentTags && !hideTags) && !(saved.save && (index in signConfObj) && signConfObj[index].accepted)) ? + {((!transparentTags && !hideTags) && !(saved.save && + ( + (item.id !== null && item.id !== undefined && item.id !== "") + ? ( + (`${item.id}`) in signConfObj && signConfObj[`${item.id}`].accepted + ) : ( + (`${index}`) in signConfObj && signConfObj[`${index}`].accepted + ) + ) + )) ? : { shadowColor: "shadow-yellow-100", navigateTo: "/transactions/waiting_for_response" }, + // { + // title: "Expiring Soon", + // count: data.expiring || 0, + // icon: , + // iconBg: "bg-red-100 border-red-200", + // textColor: "text-red-600", + // gradient: "bg-white", + // shadowColor: "shadow-red-100", + // navigateTo: "/transactions/expiring_soon" + // }, { - title: "Expiring Soon", - count: data.expiring || 0, - icon: , - iconBg: "bg-red-100 border-red-200", - textColor: "text-red-600", + title: "Draft", + count: data.draft || 0, + icon: , + iconBg: "bg-indigo-100 border-indigo-200", + textColor: "text-indigo-600", gradient: "bg-white", - shadowColor: "shadow-red-100", - navigateTo: "/transactions/expiring_soon" + shadowColor: "shadow-indigo-100", + navigateTo: "/transactions/draft" }, { title: "Completed", diff --git a/src/components/process/processStepOne/RecipientAddCard.tsx b/src/components/process/processStepOne/RecipientAddCard.tsx index cefc90f..13c41f3 100644 --- a/src/components/process/processStepOne/RecipientAddCard.tsx +++ b/src/components/process/processStepOne/RecipientAddCard.tsx @@ -352,11 +352,14 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase, document.addEventListener("mousedown", handleClickOutside); document.addEventListener("focusin", handleClickOutside); // Also handles tabbing out + handleFocus("firstName"); + setOpenContactModal(false) return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("focusin", handleClickOutside); }; + }, []); diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index 5c788f5..124c0ca 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -282,8 +282,25 @@ const DocumentContainer: React.FC = ({ transactionSaving // if (!field) return; // If not found, exit const dropZone = event.currentTarget.getBoundingClientRect(); - const x = event.clientX - dropZone.left - dragElemData.offsetX; - const y = event.clientY - dropZone.top - dragElemData.offsetY; + let x = event.clientX - dropZone.left - dragElemData.offsetX; + let y = event.clientY - dropZone.top - dragElemData.offsetY; + + // Handling Drop Over Right + if(dropZone.width < x + dragElemData.width) { + x -= (x + dragElemData.width) - dropZone.width; + } + // Handling Drop Over Bottom + if(dropZone.height < y + dragElemData.height) { + y -= (y + dragElemData.height) - dropZone.height; + } + // Handling Drop Over Left + if(x < 0) { + x = 0; + } + // Handling Drop Over Top + if(y < 0) { + y = 0; + } const key = `${docId}_${pageIndex}`; // const IconComponent = typeToIconMap[field.type] || ; diff --git a/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx b/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx index 2d948fb..0ec7b0d 100644 --- a/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx +++ b/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx @@ -284,9 +284,21 @@ const DroppedItemsTagsCon = forwardRef(({ const handleMouseMove = (event: MouseEvent) => { + + const dropZone: DOMRect = targetContainer?.getBoundingClientRect() as DOMRect; + const droppedElemRect: DOMRect = targetElement?.getBoundingClientRect() as DOMRect; + + + + + + console.log(dropZone, "dropZone Line 309") + console.log(droppedElemRect, "rect Line 309") + let newWidth = startWidth + (event.clientX - startX); let newHeight = startHeight + (event.clientY - startY); - + + if (newHeight > 200 || newWidth > 400) { if (newHeight > 200) { newHeight = 200 @@ -296,6 +308,13 @@ const DroppedItemsTagsCon = forwardRef(({ newWidth = 400 } } + + if ((dropZone.right - (droppedElemRect.left + newWidth)) < 0) { + newWidth = startWidth + } + if ((dropZone.bottom - (droppedElemRect.bottom + newHeight)) < 0) { + newHeight = startHeight + } // Apply the new width & height setDroppedItems({ diff --git a/src/components/process/processStepTwo/FieldList.tsx b/src/components/process/processStepTwo/FieldList.tsx index c3ca6ff..9927641 100644 --- a/src/components/process/processStepTwo/FieldList.tsx +++ b/src/components/process/processStepTwo/FieldList.tsx @@ -153,6 +153,122 @@ const FieldList: React.FC = ({ color, recipientId, recipientIndx }) .catch((err) => { console.error(err.message); + let data = [ + { + "editable": true, + "required": false, + "navigationOrder": 1, + "type": "RADIO" + }, + { + "editable": true, + "required": true, + "navigationOrder": 10, + "type": "SIGNATURE" + }, + { + "editable": true, + "required": false, + "navigationOrder": 9, + "type": "STAMP" + }, + { + "editable": true, + "required": false, + "navigationOrder": 9, + "type": "NAME", + "lastName": true, + "firstName": true + }, + { + "editable": true, + "required": false, + "navigationOrder": 9, + "type": "TEXT", + "maxLength": 256, + "minLength": 0, + "readOnly": false, + "multiLine": false, + "regex": [] + }, + { + "editable": true, + "required": false, + "navigationOrder": 1, + "type": "TEMPORAL", + "format": "date", + "dateFormat": "MM/dd/YYYY", + "currentTimeStamp": true + }, + { + "editable": true, + "required": false, + "navigationOrder": 1, + "type": "SELECT", + "multiSelect": false + }, + { + "editable": true, + "required": false, + "navigationOrder": 1, + "type": "ATTACHMENT" + }, + { + "editable": true, + "required": false, + "navigationOrder": 9, + "type": "NUMBER", + "decimalPlace": 0 + }, + { + "editable": true, + "required": false, + "navigationOrder": 1, + "type": "COMPANY" + }, + { + "editable": true, + "required": false, + "navigationOrder": 9, + "type": "CHECKBOX" + }, + { + "editable": true, + "required": false, + "navigationOrder": 8, + "type": "EMAIL" + }, + { + "editable": true, + "required": true, + "navigationOrder": 10, + "type": "INITIALS" + } + ] + + const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP']; + + data = data.filter((obj, indx) => { + if (obj.type === "STAMP" || obj.type === "RADIO" || obj.type === "CHECKBOX" || obj.type === "ATTACHMENT") return false + return true + }); + + // Separate prioritized and other tags + const prioritized = data.filter((tag: any) => + priorityOrder.includes(tag.type) + ); + + const others = data + .filter((tag: any) => !priorityOrder.includes(tag.type)) + .sort((a: any, b: any) => a.type.localeCompare(b.type)); + + // Maintain the custom order of priority + const prioritizedSorted = priorityOrder + .map((type) => prioritized.find((tag: any) => tag.type === type)) + .filter(Boolean); // remove undefined in case not found + + + setTagsData([...prioritizedSorted, ...others]); }); }, []); diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index 5718da8..a5f1881 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -190,17 +190,17 @@ const NavTwo: React.FC = ({ } } else { navigate(`/templates`); - toast.success("Template Saved...!", { - position: "top-center", - autoClose: 1500, - hideProgressBar: false, - closeOnClick: false, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "light", - transition: Bounce, - }); + // toast.success("Template Saved...!", { + // position: "top-center", + // autoClose: 1500, + // hideProgressBar: false, + // closeOnClick: false, + // pauseOnHover: true, + // draggable: true, + // progress: undefined, + // theme: "light", + // transition: Bounce, + // }); } }, 1000) } @@ -328,9 +328,9 @@ const NavTwo: React.FC = ({ className="absolute right-0 mt-2 w-48 rounded-md bg-white shadow-sm ring-1 ring-black/5 z-10">
- + */} - + */}
}
diff --git a/src/components/setting/Signature/AddSignatureModal.tsx b/src/components/setting/Signature/AddSignatureModal.tsx index caecc0f..9b75e0d 100644 --- a/src/components/setting/Signature/AddSignatureModal.tsx +++ b/src/components/setting/Signature/AddSignatureModal.tsx @@ -348,7 +348,7 @@ const AddSignatureModal: React.FC = ({

The maximum file size for the image is 300kb. File types supported include .png, .jpg, .jpeg

-
+
{/* Upload 1 - Full Signature */}
+ )} + + {showTemplateDeleteWarnModal.show && ( + handleDeleteClick(showTemplateDeleteWarnModal.templateId)} + /> + )} +
+ ); +}; + +export default TemplateTable; diff --git a/src/components/transactions/TransactionView.tsx b/src/components/transactions/TransactionView.tsx index 406c650..3391886 100644 --- a/src/components/transactions/TransactionView.tsx +++ b/src/components/transactions/TransactionView.tsx @@ -114,7 +114,7 @@ const TransactionView = () => { const handleNavigateToSign = (id: number) => { - signSessionRedirect(accountId, transactionId, id+"" as string) + signSessionRedirect(accountId, transactionId, id + "" as string) } const getStatusBadge = (status: string) => { @@ -202,18 +202,21 @@ const TransactionView = () => { Host: {recipient.hostEmail}

)} + {"message" in recipient &&

+ {recipient.message} +

}
{/* Right section: status and actions */} -
+ {recipient.type.toLowerCase() !== "cc" &&

{recipient.status}

- {transactionDetails.transaction.status === "SENT" && recipient.email === user.email && recipient.status !== "COMPLETED" && ( + {transactionDetails.transaction.status === "SENT" && recipient.email === user.email && recipient.status === "PENDING" && (
+
}

@@ -274,12 +277,12 @@ const TransactionView = () => {

{/* 1 of {docsD?.pages?.length} */} - - downloadDocumentUtils(accountId, transactionId, docsD.id)} size={18} /> - +

diff --git a/src/config/api.tsx b/src/config/api.tsx index fc46c37..7532c7a 100644 --- a/src/config/api.tsx +++ b/src/config/api.tsx @@ -1,10 +1,9 @@ // src/api.js import axios from "axios"; -import { redirect } from "react-router-dom"; // if using react-router -// or use your preferred navigation method const api = axios.create({ baseURL: import.meta.env.VITE_BASE_URI, + maxRedirects: 5, withCredentials: true, headers: { "Content-Type": "application/json", diff --git a/src/config/axiosInstance.tsx b/src/config/axiosInstance.tsx index 74de498..fa32d9c 100644 --- a/src/config/axiosInstance.tsx +++ b/src/config/axiosInstance.tsx @@ -1,31 +1,46 @@ -import axios from "axios"; +// api/axios.ts +import axios, { AxiosInstance } from "axios"; -export const createAxiosInstance = () => { +const defaultHeaders = { + "Content-Type": "application/json", + "ngrok-skip-browser-warning": "69420", +}; - const persistedRoot = localStorage.getItem('persist:root'); - const parsedRoot = JSON.parse(persistedRoot); +const axiosInstance: AxiosInstance = axios.create({ + baseURL: import.meta.env.VITE_BASE_URI, + withCredentials: true, + headers: defaultHeaders, + validateStatus: (status) => status >= 200 && status < 400, +}); - if(!persistedRoot || !("user" in parsedRoot)) { - return axios.create({ - baseURL: import.meta.env.VITE_BASE_URI, - withCredentials: true, - headers: { - "Content-Type": "application/json", - "ngrok-skip-browser-warning": "69420", - }, - }); + +axiosInstance.interceptors.response.use( + (response) => { + // ✅ Handle backend "soft redirects" + if (response?.data?.redirect) { + const redirectUrl = response.data.redirect; + + // Use location.replace to avoid back button returning to invalid page + window.location.replace(redirectUrl); + // OR window.location.href = redirectUrl; if you want history entry + + // Optionally return a rejected promise to stop normal handling + return Promise.reject({ redirected: true }); } - const user = JSON.parse(parsedRoot.user); - const accessToken = user.accessToken; + // Otherwise just continue as normal + return response; + }, + (error) => { + // Optionally handle backend 302 or 3xx here (if returned as error) + if (error.response?.status === 302 && error.response?.headers?.location) { + window.location.replace(error.response.headers.location); + return Promise.reject({ redirected: true }); + } - return axios.create({ - baseURL: import.meta.env.VITE_BASE_URI, - withCredentials: true, - headers: { - "ngrok-skip-browser-warning": "69420", - authorization: accessToken, + // Pass through all other errors + return Promise.reject(error); + } +); - }, - }); -}; +export default axiosInstance; diff --git a/src/store/slices/selectedDocsSlice.ts b/src/store/slices/selectedDocsSlice.ts index 73b2e9e..1075d1d 100644 --- a/src/store/slices/selectedDocsSlice.ts +++ b/src/store/slices/selectedDocsSlice.ts @@ -1,8 +1,4 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; -import axios from "axios"; -import { createAxiosInstance } from "../../config/axiosInstance"; - -const axiosInstance = createAxiosInstance(); export type SignType = "self-sign" | "multiple-sign";