jai #16

Merged
jaipandey merged 4 commits from jai into main 2025-08-05 06:44:08 +00:00
7 changed files with 228 additions and 130 deletions
Showing only changes of commit 80894b0438 - Show all commits

View File

@ -19,6 +19,8 @@ import {
FaCalendarAlt
} from 'react-icons/fa';
import TransactionDataTable from "./TransactionDataTable";
import { useSelector } from "react-redux";
import { RootState } from "../../store/store";
interface Transaction {
id: number;
@ -121,6 +123,8 @@ const Account = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const userData = useSelector((state: RootState) => state.user);
// const getUserDetails = async () => {
// try {
// let res = await api.get('/api/user/details')
@ -170,7 +174,7 @@ const Account = () => {
};
try {
const res = await api.get('/api/dashboard', { params: payload });
const res = await api.get(`/api/accounts/${userData.account.id}/dashboard`, { params: payload });
console.log("Dashboard Data:", res.data);
const cardDataArray = mapApiDataToCards(res.data);
setCards(cardDataArray);

View File

@ -14,6 +14,8 @@ import {
FaFilter,
FaCalendarAlt
} from 'react-icons/fa';
import { useSelector } from 'react-redux';
import { RootState } from '../../store/store';
interface CardData {
title: string;
@ -82,6 +84,8 @@ const Home = () => {
const [cards, setCards] = useState<CardData[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const userData = useSelector((state : RootState) => state.user);
console.log("activeAccountId", activeAccountId);
console.log("cards", cards);
@ -94,7 +98,7 @@ const Home = () => {
};
try {
const res = await api.get('/api/dashboard', { params: payload });
const res = await api.get(`/api/accounts/${userData.account.id}/dashboard`, { params: payload });
setDashboardData(res.data);
console.log("Dashboard Data:", res.data);
const cardDataArray = mapApiDataToCards(res.data);

View File

@ -19,7 +19,8 @@ import { Link, useNavigate, useParams } from "react-router-dom";
import Loader from "../../dashboard/Loader";
import confetti from 'canvas-confetti'
import { IoClose } from "react-icons/io5";
import { IoMdHome } from "react-icons/io";
import { IoMdClose, IoMdHome } from "react-icons/io";
import { Bounce, toast } from "react-toastify";
type Signer = {
id: number;
@ -222,6 +223,19 @@ const DocumentCompletionScreen = () => {
}, [])
useEffect(() => {
// toast.dismiss();
// toast.success("Your document has been successfully sent for signature.", {
// position: "top-center",
// autoClose: 1500,
// hideProgressBar: false,
// closeOnClick: false,
// pauseOnHover: true,
// draggable: true,
// progress: undefined,
// theme: "light",
// transition: Bounce,
// });
if (showConfetti) {
const duration = 3000;
const end = Date.now() + duration;
@ -233,76 +247,63 @@ const DocumentCompletionScreen = () => {
}
confetti({
particleCount: 80, // more particles
angle: Math.random() * 360, // fire in random direction
spread: 220, // wider spread
startVelocity: 15, // more power
scalar: 1.2, // bigger pieces (default is 1)
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(),
y: Math.random() * 0.6,
x: Math.random(), // random horizontal position across the top
y: 0,
},
scalar: 1,
zIndex: 9999,
});
}, 350); // fire every 150ms
}, 50); // fire every 50ms for continuous effect
return () => clearInterval(interval);
}
}, [showConfetti]);
return (
<>
<>
{
showConfetti && (
<div className="absolute top-0 left-0 w-full h-full z-[9999] bg-white/80 backdrop-blur-sm flex items-center justify-center">
{/* <Confetti width={width} height={height} /> */}
<div className="relative bg-white bg-opacity-90 backdrop-blur-lg border border-green-200 shadow-xl rounded-md px-8 py-6 w-[90%] max-w-sm text-center animate-fade-in-up">
{/* Close Icon */}
<button
onClick={() => setShowConfetti(false)} // or whatever logic hides the modal
className="absolute top-2 right-2 text-gray-500 hover:text-gray-700"
aria-label="Close"
>
<IoClose className="text-2xl" />
</button>
{/* Success Icon */}
<FaCheckCircle className="text-green-500 text-5xl mb-4 mx-auto drop-shadow-lg" />
{/* Message */}
<h2 className="text-2xl font-semibold text-gray-800 mb-1">Transaction Successful</h2>
<p className="text-gray-600 text-sm">Your transaction has been created and saved successfully.</p>
</div>
</div>
)
}
</>
<div className="min-h-screen bg-[var(--mainViewBg)]">
{/* Top Banner */}
<div className="w-full px-6 py-2 bg-white border-b rounded-sm border-gray-200">
<div className="max-w-[97vw] mx-auto px-2 py-2.5">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Link to='/' className="w-5 h-5 text-green-700 rounded-full flex items-center justify-center">
<IoMdHome className="h-10 w-10" />
<div className='fixed top-0 w-full flex items-center justify-between bg-white shadow z-50 px-4 py-3'>
<div className='flex items-center gap-2'>
<Link to={"/"}>
<IoMdHome
className='text-2xl text-slate-500 cursor-pointer hover:text-[var(--primary)] transition'
/>
</Link>
{/* <div className="w-5 h-5 bg-white text-green-800 rounded-full flex items-center justify-center">
<FaCheck className="text-sm" />
</div> */}
<div>
<h2 className="text-md font-semibold text-gray-900">Awesome! Your document is sent for signature</h2>
</div>
<p className="text-base font-normal text-gray-700 border-l pl-4">🎉 Awesome! The document’s been sent for signature. 🎉</p>
</div>
<div
id="add-tags-btn-step-one"
className='flex items-center gap-3'>
<Link to={`/accounts/${accountId}/transactions/${transactionId}`} className="flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]">
<FaPen className=" text-white text-md" />
<span className="tracking-wide text-white">Edit</span>
</Link>
{/* <button
onClick={() => handleNextClick(false)}
className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition cursor-pointer"
>
{signType === SignTypeEnum.selfSign ? "Sign" : "Next"}
</button> */}
</div>
</div>
</div>
{(selectedRecipients.length > 0 && selectedDocuments.length > 0) ?
<div className="p-3">
<div className="p-3 mt-[55px]">
<div className="max-w-[97vw] mx-auto">
{/* Header */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-2">

View File

@ -196,6 +196,7 @@ const ProcessStepOne = () => {
const recipientsData = selectedRecipients.map((recipient, index) => ({
id: "id" in recipient ? recipient.id : null,
routingOrder: "routingOrder" in recipient && recipient.routingOrder ? recipient.routingOrder : 1,
provideOwnTags: userData.email === recipient.email ? true : false,
type: recipient.type,
firstName: recipient.firstName,
lastName: recipient.lastName,
@ -264,19 +265,16 @@ const ProcessStepOne = () => {
}
} else {
dispatch(addRecipientAsync({ recipientsData: recipientsDataTemp, accountId, transactionId }));
await dispatch(addRecipientAsync({ recipientsData: recipientsDataTemp, accountId, transactionId }));
}
dispatch(showLoader("Processing Message..."));
console.log(`1/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
let res = await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, {
name: templateName,
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(() => {

View File

@ -20,6 +20,7 @@ import Loader from '../../dashboard/Loader';
// import { TRANSACTION_TEMPLATE_TOGGLE } from "../../../store/slices/selectedRecipientsSlice";
import { handleGroupBorderPosition } from '../../../utlity/tagGroupConUtils';
import { hideLoader, showLoader } from '../../../store/slices/loaderSlice';
import { IsSendInterface } from './ProcessStepTwo';
export interface DroppedItemsObj {
[key: string]: DroppedItem[]
@ -74,9 +75,12 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId }));
}, [])
console.log(droppedItems, "line 78")
// FETCHING TAG - START
useEffect(() => {
if (selectedRecipients.length === 0) return;
const fetchTags = async () => {
try {
const requests = selectedRecipients.map((recipient) =>
@ -176,17 +180,22 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
return [recipientId, tags.map(tag => ({ ...tag, update: false }))];
})
)
console.log(droppedItemsUpdateFalse, "Line 182")
setDroppedItems(droppedItemsUpdateFalse);
} catch (error) {
console.error("Error submitting recipient tags:", error);
}
};
// Save Tags - START
useEffect(() => {
if (isSend === null) return;
dispatch(showLoader("Loading ..."))
const handleTransactionProcess = async (save: boolean = false) => {
console.log(droppedItems, "called Line 189");
if (!save && isSend === null) return;
!save && dispatch(showLoader("Loading ..."))
let updatedTags = { ...droppedItems }
@ -291,17 +300,37 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
tagsData = groupByRecipientId(tagsData);
sendTagsToRecipients(tagsData, accountId, transactionId);
console.log(tagsData, "Line 302");
(async () => {
const res = await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { status: "SENT" })
// const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, { status: "SENT" })
console.log(res, "Check123")
dispatch(hideLoader())
})()
await sendTagsToRecipients(tagsData, accountId, transactionId);
// SENDING Transaction - START
// if (!save) {
// const res = await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { status: "SENT" })
// // const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, { status: "SENT" })
// console.log(res, "Check123")
// dispatch(hideLoader())
// }
// SENDING Transaction - END
}
// Save Tags - START
useEffect(() => {
handleTransactionProcess()
}, [isSend])
// Save Tags - END
// useEffect(() => {
// setTimeout(() => {
// setInterval(async () => {
// await handleTransactionProcess(true);
// }, 8000)
// }, 8000)
// }, [])
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // Allows the drop event to work
};
@ -1026,9 +1055,9 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
const IconComponent = typeToIconMap[type] || <FaQuestionCircle />;
const selectedRecipient = selectedRecipients.filter((obj, indx) => obj.id === item.recipientId);
return (
<>
<React.Fragment key={index}>
<div
key={index}
// key={index}
draggable="false"
className="absolute rounded dropped-item"
onClick={() => handleTagClick(key, index)}
@ -1090,6 +1119,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
</div>
<>
{
"group" in item && item.group && "isGroupExist" in item && !item.isGroupExist &&
<>
@ -1102,8 +1132,9 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
</>
}
</>
</React.Fragment>
)
}
@ -1146,7 +1177,11 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
</div >
{selectedTag && <RightSideBar selectedTag={droppedItems[selectedTag.key][selectedTag.index]} setDroppedItems={setDroppedItems} selTagKey={selectedTag.key} selTagIndx={selectedTag.index} DirectionalControlsProps={{
{
console.log(selectedTag, droppedItems, "Line 1175")
}
{selectedTag && selectedTag !== null && "key" in selectedTag && "index" in selectedTag && <RightSideBar selectedTag={droppedItems[selectedTag.key][selectedTag.index]} setDroppedItems={setDroppedItems} selTagKey={selectedTag.key} selTagIndx={selectedTag.index} DirectionalControlsProps={{
onMove: directionalControlsHandler,
onPositionChange: handleDirectPositionChange,
disabled: false,

View File

@ -7,7 +7,7 @@ import { FaCheck, FaSignature } from 'react-icons/fa'
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 { RiResetRightLine } from 'react-icons/ri'
import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
import { fetchDocuments } from '../../../store/slices/documentSlice'
import { useNavigate, useParams } from 'react-router-dom'
@ -66,28 +66,68 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
const [isFitDd, setIsFitDd] = useState<boolean>(false)
const [selFitIndx, setSelFitIndx] = useState<number>(0);
const [selfUserSign, setSelfUserSign] = useState({ sign: false, recipientId: 0 });
const navigate = useNavigate()
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
const userDetails = useSelector((state: RootState) => state.user);
useEffect(() => {
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId }));
if (selectedRecipients.length || selectedDocuments.length) return;
(async () => {
selectedRecipients.length === 0 && await dispatch(fetchRecipients({ accountId, transactionId }));
selectedDocuments.length === 0 && await dispatch(fetchDocuments({ accountId, transactionId }));
})()
}, [])
useEffect(() => {
if (selectedRecipients.length > 0) {
let minRoutingOrder: number = Infinity;
let userEmailPresent = {
present: false,
routingOrder: 0,
recipientId: 0
};
// Getting Minimum RoutingOrder
for (let i = 0; i < selectedRecipients.length; i++) {
const recipient = selectedRecipients[i]
if (recipient.routingOrder < minRoutingOrder) {
minRoutingOrder = recipient.routingOrder;
}
if (recipient.email === userDetails.email) {
userEmailPresent.present = true;
userEmailPresent.routingOrder = recipient.routingOrder;
userEmailPresent.recipientId = recipient.id;
}
}
if (userEmailPresent.present && userEmailPresent.routingOrder === minRoutingOrder) {
setSelfUserSign({ sign: true, recipientId: userEmailPresent.recipientId })
}
}
}, [selectedRecipients])
const [nav2RecToggle, setNav2RecToggle] = useState<boolean>(false);
const [nav2DocToggle, setNav2DocToggle] = useState<boolean>(false);
function handleSend() {
function handleSend(isSignSend: boolean = false) {
// dispatch(showLoader("Loading ..."));
if (isSend === null) {
setIsSend(false);
}
setIsSend(!isSend)
setIsSend(!isSend);
setTimeout(() => {
if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) {
navigate(`/document-completion-screen/${accountId}/${transactionId}`)
if (isSignSend) {
navigate(`./../../../sign/${selfUserSign.recipientId}`);
}
// else {
// navigate(`/document-completion-screen/${accountId}/${transactionId}`)
// }
} else {
navigate(`/templates`);
toast.success("Template Saved...!", {
@ -102,6 +142,7 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
transition: Bounce,
});
}
}, 0)
}
const handleSetIsFields = (setTo: boolean) => {
@ -123,24 +164,24 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
zoom(Number(indx), 'zoom-con')
}, [selFitIndx])
useEffect(() => {
let timeoutId: number;
const timeOutIn = 8000;
// useEffect(() => {
// let timeoutId: number;
// const timeOutIn = 8000;
const updateLabel = () => {
if (document.visibilityState === 'visible') {
setTransactionSaving(true);
setTimeout(() => {
setTransactionSaving(false);
}, 4000)
}
timeoutId = setTimeout(updateLabel, timeOutIn);
};
timeoutId = setTimeout(updateLabel, timeOutIn);
// const updateLabel = () => {
// if (document.visibilityState === 'visible') {
// setTransactionSaving(true);
// setTimeout(() => {
// setTransactionSaving(false);
// }, 4000)
// }
// timeoutId = setTimeout(updateLabel, timeOutIn);
// };
// timeoutId = setTimeout(updateLabel, timeOutIn);
// Cleanup on unmount
return () => clearTimeout(timeoutId);
}, []);
// // Cleanup on unmount
// return () => clearTimeout(timeoutId);
// }, []);
useEffect(() => {
if (!transactionSaving) return;
@ -286,6 +327,15 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
</p>
<FaSignature className='text-white text-md' />
</div>
{transactionTemplateToggle !== TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES && selfUserSign.sign && <div
id='send-and-sign-dash-btn'
onClick={() => handleSend(selfUserSign.sign)}
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]'>
<p className='tracking-wide text-white'>
Sign & Send
</p>
<FaSignature className='text-white text-md' />
</div>}

View File

@ -9,10 +9,16 @@ import { RootState } from '../../../store/store';
import Loader from '../../dashboard/Loader';
import { hideLoader } from '../../../store/slices/loaderSlice';
export interface IsSendInterface {
send: boolean | null;
save: boolean;
}
const ProcessStepTwo = () => {
const [isFields, setIsFields] = useState(true);
const [selectedRecipient, setSelectedRecipient] = useState(0);
const [isSend, setIsSend] = useState<boolean | null>(null);
const globalLoader = useSelector((state: RootState) => state.loader)
const dispatch = useDispatch();