From 7fffb22e0950f0e736337084ef828f1c67b24489 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Fri, 11 Jul 2025 17:34:26 +0530 Subject: [PATCH 1/4] session storage --- src/components/accounts/Account.tsx | 112 ++++--- src/components/accounts/AccountNav.tsx | 276 ++++++++++-------- src/components/dashboard/SideBar.tsx | 1 + src/components/home/Home.tsx | 115 ++++---- .../processStepTwo/DocumentContainer.tsx | 8 +- src/components/setting/Setting.tsx | 6 +- 6 files changed, 293 insertions(+), 225 deletions(-) diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index fee10b2..be312fb 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -110,6 +110,12 @@ const Account = () => { // Add more cards here if needed ]; }; + + const [activeAccountId, setActiveAccountId] = useState( + JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') + ); + + console.log("activeAccountId Trans.",activeAccountId) // const { accountId } = useParams<{ accountId: string }>(); const [transactions, setTransactions] = useState([]); const [selectedTransactions, setSelectedTransactions] = useState([]); @@ -121,25 +127,52 @@ const Account = () => { const [isModalOpen, setIsModalOpen] = useState(false); - const getUserDetails = async () => { - try { - let res = await api.get('/api/user/details') - let accountIDData = res?.data?.account?.id - setAccountID(accountIDData) - } catch (error) { - console.log(error) - } - } + // const getUserDetails = async () => { + // try { + // let res = await api.get('/api/user/details') + // let accountIDData = res?.data?.account?.id + // setAccountID(accountIDData) + // } catch (error) { + // console.log(error) + // } + // } - useEffect(() => { - getUserDetails() - }, []) + // useEffect(() => { + // getUserDetails() + // }, []) + + // Listen for sessionStorage changes + useEffect(() => { + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'activeAccountId') { + const newActiveAccountId = e.newValue ? JSON.parse(e.newValue) : null; + setActiveAccountId(newActiveAccountId); + } + }; + + // Listen for storage events (works for changes from other tabs) + window.addEventListener('storage', handleStorageChange); + + // Custom event listener for same-tab changes + const handleCustomStorageChange = (e: CustomEvent) => { + if (e.detail.key === 'activeAccountId') { + setActiveAccountId(e.detail.newValue); + } + }; + + window.addEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); + + return () => { + window.removeEventListener('storage', handleStorageChange); + window.removeEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); + }; + }, []); const getDashboardData = async () => { setIsLoading(true) const payload = { - account: accountID + account: activeAccountId }; try { @@ -154,50 +187,51 @@ const Account = () => { } }; - useEffect(() => { - if (accountID) { - getDashboardData() - fetchTransactions(); - } - }, [accountID]); - - const handleOpenModal = () => { - setIsModalOpen(true); - }; - const fetchTransactions = async () => { console.log("fetchTransactions") try { - const response = await api(`/api/accounts/${accountID}/transactions`); + const response = await api(`/api/accounts/${activeAccountId}/transactions`); setTransactions(response.data || []); } catch (error) { console.error("Error fetching transactions:", error); } }; + useEffect(() => { + if (activeAccountId) { + getDashboardData() + fetchTransactions(); + } + }, [activeAccountId]); + + const handleOpenModal = () => { + setIsModalOpen(true); + }; + + // useEffect(() => { // if (accountID) fetchTransactions(); // }, [accountID]); - const getAccountDetails = async () => { - try { - let res = await api.get('/api/user/accounts') - let items = res?.data - setAccountsData(items) - } catch (error) { - console.log(error) - } - } + // const getAccountDetails = async () => { + // try { + // let res = await api.get('/api/user/accounts') + // let items = res?.data + // setAccountsData(items) + // } catch (error) { + // console.log(error) + // } + // } - useEffect(() => { - getAccountDetails() - }, []) + // useEffect(() => { + // getAccountDetails() + // }, []) const navigate = useNavigate(); const handleTransactionClick = (id: number) => { - navigate(`/accounts/${accountID}/transactions/${id}`); + navigate(`/accounts/${activeAccountId}/transactions/${id}`); }; const handleInvite = async (data: { i_accountName: number; email: string; role: string }) => { @@ -235,7 +269,7 @@ const Account = () => { return ( <> + isLoading={isLoading} /> {transactions.length > 0 && (
diff --git a/src/components/accounts/AccountNav.tsx b/src/components/accounts/AccountNav.tsx index 9e6ac4f..ccfc83b 100644 --- a/src/components/accounts/AccountNav.tsx +++ b/src/components/accounts/AccountNav.tsx @@ -1,21 +1,13 @@ import React, { useEffect, useRef, useState } from 'react'; -// import { useSelector } from 'react-redux'; import { Link, useLocation, useNavigate } from 'react-router-dom'; import { FaSignOutAlt, FaUser, FaBuilding, FaChevronDown, FaCheck } from "react-icons/fa"; import api from '../../config/api'; -// import { FaUser, FaSignOutAlt, FaBuilding, FaChevronDown, FaCheck } from 'react-icons/fa'; - -// import { RootState } from '../../store/store'; -// import { UserState } from '../../store/slices/userSlice'; - - interface AccountNavProps { accountName?: string | null; transactionName?: string | null; } -// Define the shape of state object interface Account { id: number; isDefault: boolean; @@ -23,32 +15,45 @@ interface Account { } interface UserData { - id: string; - firstName: string; - lastName: string; + id: number; + name: string; email: string; language: string; account: Account; } interface AccountDataArray { - accountId: number, accountName: string, role: string, status: boolean + userId: number, + userEmail: string, + name: string, + accountId: number; + accountName: string; + role: string; + status: string; } +// Helper function to dispatch custom storage event +const dispatchSessionStorageChange = (key: string, newValue: any) => { + window.dispatchEvent(new CustomEvent('sessionStorageChange', { + detail: { key, newValue } + })); +}; + const AccountNav: React.FC = ({ accountName = null, transactionName = null }) => { - // const user: UserState = useSelector((state: RootState) => state.user); + const activeAccountId = JSON.parse(sessionStorage.getItem('activeAccountId') || 'null'); + const userDetails = JSON.parse(sessionStorage.getItem('userDetails') || '{}'); + console.log("activeAccountId", activeAccountId); const [open, setOpen] = useState(false); const [accountsOpen, setAccountsOpen] = useState(false); const [loadingAccounts, setLoadingAccounts] = useState(false); const [accounts, setAccounts] = useState([]); - const [hasModalBeenClosed, setHasModalBeenClosed] = useState(false); // Track if modal was closed - const dropdownRef = useRef(null); + const [hasModalBeenClosed, setHasModalBeenClosed] = useState(false); + const dropdownRef = useRef(null); const [user, setUser] = useState({ - id: "", - firstName: "", - lastName: "", + id: 0, + name: "", email: "", language: "", account: { @@ -58,13 +63,17 @@ const AccountNav: React.FC = ({ accountName = null, transaction }, }); + console.log("user", user) const getAccountDetails = async () => { try { - let res = await api.get('/api/user/accounts') - let items = res?.data - console.log('items', items) - // Extract only the fields you want + let res = await api.get('/api/user/accounts'); + let items = res?.data; + console.log('items', items); + const cleanedData: AccountDataArray[] = items.map((item: any) => ({ + userId: item.userId, + userEmail: item.userEmail, + name: item.name, accountId: item.accountId, accountName: item.accountName, role: item.role, @@ -73,89 +82,137 @@ const AccountNav: React.FC = ({ accountName = null, transaction setAccounts(cleanedData); } catch (error) { - console.log(error) + console.log(error); } - } - + }; const handleAccountsClick = async () => { setAccountsOpen(!accountsOpen); - + if (!accountsOpen) { setLoadingAccounts(true); - - // Fetch API if: - // 1. No accounts data exists (first time), OR - // 2. Modal was previously closed (need fresh data) + if (accounts.length === 0 || hasModalBeenClosed) { await getAccountDetails(); - setHasModalBeenClosed(false); // Reset the flag after fetching + setHasModalBeenClosed(false); } - + setLoadingAccounts(false); } }; - const handleAccountSwitch = (account) => { - setUser(prev => ({ - ...prev, - account: { name: account.name, id: account.id } - })); + const handleAccountSwitch = (account: any) => { + console.log("handleAccountSwitch", account); - // Update accounts active status - setAccounts(prevAccounts => - prevAccounts.map(acc => ({ - ...acc, - isActive: acc.id === account.id - })) - ); + // Update sessionStorage + sessionStorage.setItem('activeAccountId', JSON.stringify(account.accountId)); + + // Dispatch custom event + dispatchSessionStorageChange('activeAccountId', account.accountId); + + // Update user state + + setUser({ + id: account.userId, + name: account.name, + email: account.userEmail, + language: "", // set it if available + account: { + id: account.accountId, + isDefault: false, // update as needed + name: account.accountName, + }, + }); + + sessionStorage.setItem('userDetails', JSON.stringify({ + id: account.userId, + name: account.name, + email: account.userEmail, + language: "", // set it if available + account: { + id: account.accountId, + isDefault: false, // update as needed + name: account.accountName, + }, + })); setAccountsOpen(false); setOpen(false); }; - const navigate = useNavigate() + + const navigate = useNavigate(); const getUserDetails = async () => { try { - let res = await api.get(`/api/user/details`) - console.log("getuser", res) - setUser(res?.data) + let res = await api.get(`/api/user/details`); + console.log("getuser", res); + const item = res?.data; + if (item) { + const userData = { + id: item?.id, + name : item?.firstName + " " + item?.lastName , + email: item?.email, + language: "", + account: { + id: item?.account.id, + isDefault: false, + name: item?.account.name, + }, + }; + + console.log("userData",userData) + + setUser(userData); + sessionStorage.setItem('userDetails', JSON.stringify(userData)); + sessionStorage.setItem('activeAccountId', JSON.stringify(userData.account.id)); + + // Dispatch event for initial load + dispatchSessionStorageChange('activeAccountId', userData.account.id); + + console.log("Data is saved in sessionStorage"); + } + + } catch (error) { console.error("Error fetching user details:", error); } - } + }; useEffect(() => { - getUserDetails() - }, []) - + if (!activeAccountId) { + getUserDetails(); + } else { + setUser(userDetails); + } + }, []); const location = useLocation(); - const pathnames = location.pathname + const rawPathnames = location.pathname .split("/") - .filter((x) => x && isNaN(Number(x))); // remove numeric IDs + .filter((x) => x && isNaN(Number(x))); + + // Special case: if first segment is 's' and next is 'settings', remove 's' + const pathnames = + rawPathnames[0] === "s" && rawPathnames[1] === "settings" + ? rawPathnames.slice(1) + : rawPathnames; const labelMap: Record = { accounts: "Accounts", transactions: "Transactions", templates: "Templates" - // Add more routes here if needed }; - // const [open, setOpen] = useState(false); - // const dropdownRef = useRef(null); - - // Close dropdown when clicked outside useEffect(() => { function handleClickOutside(event: MouseEvent) { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { if (open) { - setHasModalBeenClosed(true); // Mark modal as closed when clicking outside + setHasModalBeenClosed(true); } setOpen(false); - setAccountsOpen(false); // Also close accounts dropdown + setAccountsOpen(false); } } @@ -165,24 +222,24 @@ const AccountNav: React.FC = ({ accountName = null, transaction const handleLogout = async () => { try { - let res = await api.post('/api/auth/logout') + let res = await api.post('/api/auth/logout'); if (res.status === 200) { + sessionStorage.clear(); setOpen(false); - navigate("/login") + navigate("/login"); } } catch (error) { - console.log(error) + console.log(error); } }; - // Handle main dropdown toggle const handleMainDropdownToggle = () => { if (open) { - setHasModalBeenClosed(true); // Mark as closed when manually closing + setHasModalBeenClosed(true); } setOpen(!open); if (!open) { - setAccountsOpen(false); // Close accounts dropdown when opening main dropdown + setAccountsOpen(false); } }; @@ -191,7 +248,6 @@ const AccountNav: React.FC = ({ accountName = null, transaction .replace(/[-_]/g, " ") .replace(/\b\w/g, (char) => char.toUpperCase()); - const ShimmerRow = () => (
@@ -205,7 +261,6 @@ const AccountNav: React.FC = ({ accountName = null, transaction
); - return (
- - {/* User Dropdown */}
- {/* Avatar Button */} - {/* Enhanced Dropdown */} {open && (
- {/* User Info Section */}
- {(user) ? ( - - {(user?.firstName + " " + user?.lastName).split(' ').map((item) => item.slice(0, 1)).join("").toUpperCase()} + {user ? ( + + {(user?.name?.slice(0, 2)).toUpperCase()} + ) : ( )}
-
{user.firstName + ' ' + user.lastName || '-'}
-
{user.email || '-'}
+
{user?.name || '-'}
+
{user?.email || '-'}
Account #{user?.account?.name || '-'}
@@ -284,7 +333,6 @@ const AccountNav: React.FC = ({ accountName = null, transaction
- {/* Account List Section */}
- {/* Accounts Dropdown */} {accountsOpen && (
{loadingAccounts ? ( @@ -307,42 +354,43 @@ const AccountNav: React.FC = ({ accountName = null, transaction
) : (
- {accounts.length > 0 ? (accounts?.map((account) => ( -
- - ))) : No Account...} - + + )) + ) : ( + No Account... + )}
)}
)}
- {/* Logout Section */} -
+
- - {/* Enhanced Dropdown */} - {open && ( -
- {/* User Info Section */} -
-
-
- {(user) ? ( - - {(user?.firstName + " " + user?.lastName).split(' ').map((item) => item.slice(0, 1)).join("").toUpperCase()} - ) : ( - - )} -
-
-
{user.firstName + ' ' + user.lastName || '-'}
-
{user.email || '-'}
-
- Account #{user?.account?.name || '-'} -
-
-
-
- - {/* Account List Section */} -
- - - {/* Accounts Dropdown */} - {accountsOpen && ( -
- {loadingAccounts ? ( -
- - -
- ) : ( -
- {accounts.length > 0 ? (accounts?.map((account) => ( - - ))) : No Account...} - -
- )} -
- )} -
- - {/* Logout Section */} -
- -
-
- )} -
+
+
+
- ); + + + {open && ( +
+
+
+
+ {user ? ( + + {(user?.name?.slice(0, 2)).toUpperCase()} + + ) : ( + + )} +
+
+
{user?.name || '-'}
+
{user?.email || '-'}
+
+ Account #{user?.account?.name || '-'} +
+
+
+
+ +
+ + + {accountsOpen && ( +
+ {loadingAccounts ? ( +
+ + +
+ ) : ( +
+ {accounts?.length > 0 ? ( + accounts?.map((account) => ( + + )) + ) : ( + No Account... + )} +
+ )} +
+ )} +
+ +
+ +
+
+ )} +
+ + + ); }; export default CommonNav; \ No newline at end of file diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index be312fb..d5c7a6a 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -67,36 +67,36 @@ const Account = () => { textColor: 'text-teal-700', trend: 'up' }, - { - title: 'Waiting for Others', - count: data.waitingForOthers || 0, - icon: , - gradient: 'bg-gradient-to-br from-blue-50 to-blue-100', - shadowColor: 'shadow-blue-200/50', - iconBg: 'bg-blue-100 border-blue-200', - textColor: 'text-blue-700', - trend: 'down' - }, - { - title: 'declined', - count: data.declined || 0, - icon: , - gradient: 'bg-gradient-to-br from-purple-50 to-purple-100', - shadowColor: 'shadow-purple-200/50', - iconBg: 'bg-purple-100 border-purple-200', - textColor: 'text-purple-700', - trend: 'down' - }, - { - title: 'cancelled', - count: data.cancelled || 0, - icon: , - gradient: 'bg-gradient-to-br from-green-50 to-green-100', - shadowColor: 'shadow-green-200/50', - iconBg: 'bg-green-100 border-green-200', - textColor: 'text-green-700', - trend: 'down' - }, + // { + // title: 'Waiting for Others', + // count: data.waitingForOthers || 0, + // icon: , + // gradient: 'bg-gradient-to-br from-blue-50 to-blue-100', + // shadowColor: 'shadow-blue-200/50', + // iconBg: 'bg-blue-100 border-blue-200', + // textColor: 'text-blue-700', + // trend: 'down' + // }, + // { + // title: 'declined', + // count: data.declined || 0, + // icon: , + // gradient: 'bg-gradient-to-br from-purple-50 to-purple-100', + // shadowColor: 'shadow-purple-200/50', + // iconBg: 'bg-purple-100 border-purple-200', + // textColor: 'text-purple-700', + // trend: 'down' + // }, + // { + // title: 'cancelled', + // count: data.cancelled || 0, + // icon: , + // gradient: 'bg-gradient-to-br from-green-50 to-green-100', + // shadowColor: 'shadow-green-200/50', + // iconBg: 'bg-green-100 border-green-200', + // textColor: 'text-green-700', + // trend: 'down' + // }, { title: 'draft', count: data.draft || 0, @@ -115,7 +115,9 @@ const Account = () => { JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') ); - console.log("activeAccountId Trans.",activeAccountId) + const [currentEmailId, setCurrentEmailId] = useState('null'); + + console.log("activeAccountId Trans.", activeAccountId) // const { accountId } = useParams<{ accountId: string }>(); const [transactions, setTransactions] = useState([]); const [selectedTransactions, setSelectedTransactions] = useState([]); @@ -141,32 +143,38 @@ const Account = () => { // getUserDetails() // }, []) + useEffect(() => { + const userDetails = JSON.parse(sessionStorage.getItem('userDetails')); + setCurrentEmailId(userDetails?.email) + }, []); + + // Listen for sessionStorage changes - useEffect(() => { - const handleStorageChange = (e: StorageEvent) => { - if (e.key === 'activeAccountId') { - const newActiveAccountId = e.newValue ? JSON.parse(e.newValue) : null; - setActiveAccountId(newActiveAccountId); - } - }; - - // Listen for storage events (works for changes from other tabs) - window.addEventListener('storage', handleStorageChange); - - // Custom event listener for same-tab changes - const handleCustomStorageChange = (e: CustomEvent) => { - if (e.detail.key === 'activeAccountId') { - setActiveAccountId(e.detail.newValue); - } - }; - - window.addEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); - - return () => { - window.removeEventListener('storage', handleStorageChange); - window.removeEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); - }; - }, []); + useEffect(() => { + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'activeAccountId') { + const newActiveAccountId = e.newValue ? JSON.parse(e.newValue) : null; + setActiveAccountId(newActiveAccountId); + } + }; + + // Listen for storage events (works for changes from other tabs) + window.addEventListener('storage', handleStorageChange); + + // Custom event listener for same-tab changes + const handleCustomStorageChange = (e: CustomEvent) => { + if (e.detail.key === 'activeAccountId') { + setActiveAccountId(e.detail.newValue); + } + }; + + window.addEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); + + return () => { + window.removeEventListener('storage', handleStorageChange); + window.removeEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); + }; + }, []); const getDashboardData = async () => { @@ -281,7 +289,8 @@ const Account = () => {
- + + @@ -318,6 +327,9 @@ const Account = () => { + diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 5865246..b35173f 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -103,32 +103,32 @@ function Dashboard() { }, [documentSignType]) - useEffect(() => { - if (showConfetti) { - const duration = 3000; - const end = Date.now() + duration; + useEffect(() => { + if (showConfetti) { + const duration = 3000; + const end = Date.now() + duration; - const interval = setInterval(() => { - if (Date.now() > end) { - clearInterval(interval); - return; - } + const interval = setInterval(() => { + if (Date.now() > end) { + clearInterval(interval); + return; + } - 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) - origin: { - x: Math.random(), - y: Math.random() * 0.6, - }, - zIndex: 9999, - }); - }, 350); // fire every 150ms - } - }, [showConfetti]); + 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) + origin: { + x: Math.random(), + y: Math.random() * 0.6, + }, + zIndex: 9999, + }); + }, 350); // fire every 150ms + } + }, [showConfetti]); diff --git a/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx b/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx new file mode 100644 index 0000000..17c961c --- /dev/null +++ b/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx @@ -0,0 +1,354 @@ +import React, { useEffect } from "react"; +import { + FaCheck, + FaFileAlt, + FaUsers, + FaEnvelope, + FaClock, + FaDownload, + FaShare, + FaShieldAlt, + FaLock, + FaPen, + FaCheckCircle +} from 'react-icons/fa'; +import { fetchRecipients } from "../../../store/slices/selectedRecipientsSlice"; +import { fetchDocuments } from "../../../store/slices/documentSlice"; +import { useDispatch, useSelector } from "react-redux"; +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"; + +type Signer = { + id: number; + name: string; + email: string; + routingOrder: number; + completed: boolean; +}; + +// Signers List Component +const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => { + return ( +
+
+
+

+ + Signature Status +

+ + {signers.filter(s => s.completed).length} of {signers.length} signed + +
+ +
+ {signers.map((signer) => ( +
+
+ {signer.name.split(" ").map(n => n[0].toUpperCase()).join("")} +
+
+
{signer.name}
+
{signer.email}
+
Order: {signer.routingOrder}
+
+
+ {signer.completed ? ( +
+ + Signed +
+ ) : ( +
+ + Pending +
+ )} +
+
+ ))} +
+
+
+ ); +}; + +// Fixed Signing Order Diagram Component +const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => { + const groupByRoutingOrder = (data: Signer[]) => { + const grouped: Record = {}; + data.forEach((signer) => { + if (!grouped[signer.routingOrder]) { + grouped[signer.routingOrder] = []; + } + grouped[signer.routingOrder].push(signer); + }); + return grouped; + }; + + const getInitials = (name: string) => { + return name + .split(" ") + .map((n) => n[0].toUpperCase()) + .join(""); + }; + + const grouped = groupByRoutingOrder(signers); + const sortedOrders = Object.keys(grouped) + .map(Number) + .sort((a, b) => a - b); + + const currentProcessingOrder = + sortedOrders.find((order) => + grouped[order].some((signer) => !signer.completed) + ) || -1; + + return ( +
+
+

Signing Flow

+
+
+ {/* Mail icon */} +
+
+ +
+

Sent

+
+ + {/* Dotted line */} +
+
+
+ + {/* Signers by routing order */} +
+ {sortedOrders.map((order, index) => ( + +
+
+ Step {order} +
+
+ {grouped[order].map((signer) => { + let bgColor = ""; + let icon = null; + + if (signer.completed) { + bgColor = "bg-[var(--primary)]"; + icon = ; + } else if (order === currentProcessingOrder) { + bgColor = "bg-yellow-500"; + icon = ; + } else { + bgColor = "bg-gray-400"; + icon = ; + } + + return ( +
+
+ {getInitials(signer.name)} + + {icon} + +
+

+ {signer.name.split(" ")[0]} +

+
+ ); + })} +
+
+ + {index < sortedOrders.length - 1 && ( +
+
+
+ )} +
+ ))} +
+
+
+
+
+ ); +}; + +// Main DocumentCompletionScreen Component +const DocumentCompletionScreen = () => { + const documentList = [ + { title: 'Document 1', pages: 12 }, + { title: 'Document 2', pages: 8 }, + { title: 'Document 3', pages: 5 }, + { title: 'Document 4', pages: 22 }, + { title: 'Document 5', pages: 10 }, + { title: 'Document 6', pages: 15 }, + { title: 'Document 7', pages: 7 }, + { title: 'Document 8', pages: 9 }, + { title: 'Document 9', pages: 6 }, + { title: 'Document 10', pages: 11 }, + { title: 'Document 11', pages: 4 }, // Won't be shown due to slice + ]; + + const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>(); + const [showConfetti, setShowConfetti] = React.useState(true) + if (!accountId || !transactionId) { + window.location.href = "/"; + return null; + } + + // 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) + const selectedDocuments = useSelector((state: RootState) => state.documents.documents) + + + useEffect(() => { + selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId })); + selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId })); + }, []) + + useEffect(() => { + if (showConfetti) { + const duration = 3000; + const end = Date.now() + duration; + + const interval = setInterval(() => { + if (Date.now() > end) { + clearInterval(interval); + return; + } + + 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) + origin: { + x: Math.random(), + y: Math.random() * 0.6, + }, + zIndex: 9999, + }); + }, 350); // fire every 150ms + } + }, [showConfetti]); + + return ( + <> + <> + { + showConfetti && ( +
+ {/* */} + +
+ {/* Close Icon */} + + + {/* Success Icon */} + + + {/* Message */} +

Transaction Successful

+

Your transaction has been created and saved successfully.

+
+
+ ) + } + +
+ {/* Top Banner */} + +
+
+
+
+ + + +
+ +
+
+

Awesome! Your document is sent for signature

+
+
+ + + Edit + +
+
+
+ {(selectedRecipients.length > 0 && selectedDocuments.length > 0) ? +
+
+ {/* Header */} +
+ {/* Left Section - Document Details */} +
+ {/* Document Preview Card */} +
+
+ {selectedDocuments?.map((item, index) => ( +
+
+
+ {/* */} + document +
+
+

+ Name : {item.name ?? '--'} +

+
+ Status : {item.status ?? '--'} +
+ +
+
+
+ ))} +
+
+
+ + {/* Right Section - Signer Components */} +
+ {/* Signing Order Diagram */} + + + {/* Signers List */} + +
+
+
+
+ : } +
+ + ); +}; + +export default DocumentCompletionScreen; \ No newline at end of file diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index 19d652a..d6b2268 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -10,7 +10,7 @@ import { MdKeyboardArrowDown } from 'react-icons/md' import { RiResetLeftLine, RiResetRightLine } from 'react-icons/ri' import { fetchRecipients } from '../../../store/slices/selectedRecipientsSlice' import { fetchDocuments } from '../../../store/slices/documentSlice' -import { useParams } from 'react-router-dom' +import { useNavigate, useParams } from 'react-router-dom' interface NavTwoProps { isFields: boolean; @@ -57,6 +57,8 @@ const NavTwo: React.FC = ({ isFields, setIsFields, isSend, setIsSen const [isFitDd, setIsFitDd] = useState(false) const [selFitIndx, setSelFitIndx] = useState(0); + const navigate = useNavigate() + const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients) const selectedDocuments = useSelector((state: RootState) => state.documents.documents) @@ -76,6 +78,7 @@ const NavTwo: React.FC = ({ isFields, setIsFields, isSend, setIsSen setIsSend(false); } setIsSend(!isSend) + navigate(`/document-completion-screen/${accountId}/${transactionId}`) } const handleSetIsFields = (setTo: boolean) => { diff --git a/src/components/process/processStepTwo/rightSideBar/DirectionalControls.tsx b/src/components/process/processStepTwo/rightSideBar/DirectionalControls.tsx index 4644623..95bb714 100644 --- a/src/components/process/processStepTwo/rightSideBar/DirectionalControls.tsx +++ b/src/components/process/processStepTwo/rightSideBar/DirectionalControls.tsx @@ -101,7 +101,7 @@ const DirectionalControls: React.FC = ({ - Right: {Math.round(right)}px + {/* Right: {Math.round(right)}px */} {/* Bottom Row */} @@ -110,7 +110,7 @@ const DirectionalControls: React.FC = ({ - Bottom: {Math.round(bottom)}px + {/* Bottom: {Math.round(bottom)}px */}
diff --git a/src/components/setting/Setting.tsx b/src/components/setting/Setting.tsx index 000d1d0..623cc53 100644 --- a/src/components/setting/Setting.tsx +++ b/src/components/setting/Setting.tsx @@ -19,8 +19,7 @@ interface Account { interface UserData { id: string; - firstName: string; - lastName: string; + name: string; email: string; language: string; account: Account; @@ -43,32 +42,35 @@ interface AccountOption { const Setting = () => { const { page1 } = useParams<{ page1: string }>(); - console.log("page1" , page1) + console.log("page1", page1) const location = useLocation(); - console.log("location",location) + console.log("location", location) const navigate = useNavigate(); + const [activeAccountId, setActiveAccountId] = useState( + JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') + ); + // Refs for subsections within each main tab const subSectionRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}); const scrollContainerRef = useRef(null); const [activeTab, setActiveTab] = useState("userProfile"); const [activeSubSection, setActiveSubSection] = useState(""); - const [user, setUser] = useState({ - id: "", - firstName: "", - lastName: "", - email: "", - language: "", + const [user, setUser] = useState({ + id: '', + name: '', + email: '', + language: '', account: { id: 0, isDefault: false, - name: "", + name: '', }, }); const [createAndInviteLoader, setCreateAndInviteLoader] = useState(false); - + // Dropdown data states const [accountOptions, setAccountOptions] = useState([]); const [accountOptionsLoading, setAccountOptionsLoading] = useState(false); @@ -203,8 +205,8 @@ const Setting = () => { return ; case "create and invite": return ( - { { id: "contactShare", label: "Share Contact", type: "contact" }, ]; - const getUserDetails = async () => { - try { - let res = await api.get(`/api/user/details`) - console.log("getuser", res) - setUser(res?.data) - } catch (error) { - console.error("Error fetching user details:", error); + + // Listen for sessionStorage changes + useEffect(() => { + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'activeAccountId') { + const newActiveAccountId = e.newValue ? JSON.parse(e.newValue) : null; + setActiveAccountId(newActiveAccountId); + } + }; + + // Listen for storage events (works for changes from other tabs) + window.addEventListener('storage', handleStorageChange); + + // Custom event listener for same-tab changes + const handleCustomStorageChange = (e: CustomEvent) => { + if (e.detail.key === 'activeAccountId') { + setActiveAccountId(e.detail.newValue); + } + }; + + window.addEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); + + return () => { + window.removeEventListener('storage', handleStorageChange); + window.removeEventListener('sessionStorageChange', handleCustomStorageChange as EventListener); + }; + }, []); + + // Re-fetch dashboard data when activeAccountId changes + useEffect(() => { + const rawUser = JSON.parse(sessionStorage.getItem('userDetails') || 'null'); + + if (rawUser) { + const userData: UserData = { + id: String(rawUser.id || ''), + name: rawUser.name || '', + email: rawUser.email || '', + language: rawUser.language || '', + account: { + id: rawUser.account?.id || 0, + isDefault: rawUser.account?.isDefault || false, + name: rawUser.account?.name || '', + }, + }; + + setUser(userData); } - } + }, [activeAccountId]); + + useEffect(() => { - getUserDetails() // Fetch account list when component mounts getAccountList() }, []) @@ -259,7 +301,7 @@ const Setting = () => { // Set initial subsection when component mounts or activeTab changes useEffect(() => { const subSections = getSubSections(activeTab); - console.log("subSections",subSections) + console.log("subSections", subSections) const hash = location.hash.replace('#', ''); if (subSections.length > 0) { @@ -292,7 +334,7 @@ const Setting = () => { setCreateAndInviteLoader(true) try { let res; - let toastMesaage ; + let toastMesaage; if (action === 'create') { console.log('Creating account:', data); const payloadAccount = { @@ -310,10 +352,10 @@ const Setting = () => { role: data.role } res = await api.post(`/api/accounts/${data.i_accountName}/invite`, payloadInvite) - console.log("InviteNAme",res) + console.log("InviteNAme", res) toastMesaage = "Invite Successfull...!" } - + if (res?.data) { toast.success(toastMesaage, { position: "top-center", @@ -326,7 +368,7 @@ const Setting = () => { theme: "light", transition: Bounce, }); - + // Refetch account list after successful submission await getAccountList(); } @@ -340,7 +382,7 @@ const Setting = () => { return ( <> {/* */} - + {/* */}
{/* Main Navigation Tabs */}
@@ -364,7 +406,7 @@ const Setting = () => {
{/* Content with Aside Navigation */} -
+
{/* Fixed Aside Navigation - Shows subsections of active tab */} {activeTab === 'userProfile' &&
diff --git a/src/components/setting/Signature/AddSignatureModal.tsx b/src/components/setting/Signature/AddSignatureModal.tsx index a92c172..285a997 100644 --- a/src/components/setting/Signature/AddSignatureModal.tsx +++ b/src/components/setting/Signature/AddSignatureModal.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { AiOutlineUpload, AiOutlineEdit } from 'react-icons/ai'; -import { MdCloudUpload } from 'react-icons/md'; +import { MdCloudUpload } from 'react-icons/md'; import api from '../../../config/api'; import DrawSignatureCanvas from './DrawSignatureCanvas'; import { FaSpinner } from "react-icons/fa"; @@ -54,7 +54,7 @@ const AddSignatureModal: React.FC = ({ getAllSignatures, editingSignature }) => { - const [activeTab, setActiveTab] = useState<'SELECT' | 'DRAW' | 'UPLOAD'>('SELECT'); + const [activeTab, setActiveTab] = useState<'TYPE' | 'DRAW' | 'UPLOAD' | 'SELECT'>('TYPE'); const [fullName, setFullName] = useState(''); const [initials, setInitials] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); @@ -102,9 +102,10 @@ const AddSignatureModal: React.FC = ({ // Navigation items const navigationItems = [ - { id: 'SELECT', label: 'SELECT', icon: }, + { id: 'TYPE', label: 'TYPE', icon: }, { id: 'DRAW', label: 'DRAW', icon: }, { id: 'UPLOAD', label: 'UPLOAD', icon: }, + { id: 'SELECT', label: 'SELECT', icon: }, ]; // Initialize form with editing signature data @@ -112,7 +113,7 @@ const AddSignatureModal: React.FC = ({ if (editingSignature) { setFullName(`${editingSignature.firstName} ${editingSignature.lastName}`); setInitials(`${editingSignature.firstName.slice(0, 1)}${editingSignature.lastName.slice(0, 1)}`); - setActiveTab(editingSignature.signatureType as 'SELECT' | 'DRAW' | 'UPLOAD'); + setActiveTab(editingSignature.signatureType as 'TYPE' | 'DRAW' | 'UPLOAD'); // Set font for SELECT tab const fontOption = signatureOptions.find(option => option.style === editingSignature.font); @@ -262,7 +263,7 @@ const AddSignatureModal: React.FC = ({ const renderContent = () => { switch (activeTab) { - case 'SELECT': + case 'TYPE': return (
@@ -383,6 +384,16 @@ const AddSignatureModal: React.FC = ({
); + + + case 'SELECT': + return ( +
+
+

Select Signature

+
+
+ ); default: return null; } @@ -462,7 +473,7 @@ const AddSignatureModal: React.FC = ({ {navigationItems.map((item) => (
diff --git a/src/components/setting/UserProfile.tsx b/src/components/setting/UserProfile.tsx index ce58abd..95b0412 100644 --- a/src/components/setting/UserProfile.tsx +++ b/src/components/setting/UserProfile.tsx @@ -6,9 +6,8 @@ interface Account { } interface UserData { - id: string; - firstName: string; - lastName: string; + id: number; + name:string email: string; language: string; account: Account; @@ -26,7 +25,7 @@ const UserProfile: React.FC = ({ userData }) => { {/* Profile Picture */}
- {getInitials(userData.firstName, userData.lastName)} + {userData.name.slice(0,2).toUpperCase()}
@@ -36,7 +35,7 @@ const UserProfile: React.FC = ({ userData }) => {
Name
- {userData.firstName} {userData.lastName} + {userData.name}
diff --git a/src/components/transactions/TransactionView.tsx b/src/components/transactions/TransactionView.tsx index 9f1bbe3..5ec0c86 100644 --- a/src/components/transactions/TransactionView.tsx +++ b/src/components/transactions/TransactionView.tsx @@ -64,13 +64,14 @@ const TransactionView = () => {
{/* Header */}

- Complete with Forms Made Easy: { transactionDetails.transaction.name ?? "Untitled"} + Complete with Forms Made Easy: { transactionDetails?.transaction?.subject ?? "Untitled"}

+ {/* new Date(transactionDetails?.transaction?.lastModifiedAt * 1000) */}

From Shanu.R

-

Last Change on 12/02/25

-

Sent on 11/02/25

+

Last Change on {new Date(transactionDetails?.transaction?.lastModifiedAt * 1000).toISOString()}

+

Created at {new Date(transactionDetails?.transaction?.createdAt * 1000).toISOString()}

Draft

@@ -139,7 +140,7 @@ const TransactionView = () => {

Message

-

No Message Found

+

{ transactionDetails?.transaction?.message ?? "No Message Found...!"}

diff --git a/vite.config.ts b/vite.config.ts index 70397db..0472e82 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,7 +12,8 @@ export default defineConfig({ 'officially-powerful-goat.ngrok-free.app', '89f0-49-36-189-120.ngrok-free.app', 'c8ee-49-36-191-130.ngrok-free.app', - 'c76f1251c173.ngrok-free.app' + 'c76f1251c173.ngrok-free.app', + 'enabled-upright-boxer.ngrok-free.app' ], }, build: { From 38370e73f9a17fe10c5f9d58704fb3e0de938002 Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Tue, 15 Jul 2025 15:34:11 +0530 Subject: [PATCH 3/4] completion-screen --- src/components/accounts/Account.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index d5c7a6a..ccb0d87 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -328,7 +328,7 @@ const Account = () => { {tx.expireAfter ?? "-"}
Active Last Change Expire After (days)SignAction
{tx.expireAfter ?? "-"} + {tx?.createdBy?.email === currentEmailId ? : "-"} + - {tx?.createdBy?.email === currentEmailId ? : "-"} + {tx?.createdBy?.email === currentEmailId ? : "-"} From 5c3131f3c3cdf66727345da1e2b0fff9fce1289e Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Tue, 15 Jul 2025 16:12:17 +0530 Subject: [PATCH 4/4] completion-screen-2 --- .../DocumentCompletionScreen.tsx | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx b/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx index 17c961c..fe91fa2 100644 --- a/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx +++ b/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx @@ -32,7 +32,7 @@ type Signer = { // Signers List Component const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => { return ( -
+

@@ -46,7 +46,7 @@ const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => {
{signers.map((signer) => ( -
+
{signer.name.split(" ").map(n => n[0].toUpperCase()).join("")}
@@ -107,10 +107,10 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => { ) || -1; return ( -
+

Signing Flow

-
+
{/* Mail icon */}
@@ -280,23 +280,23 @@ const DocumentCompletionScreen = () => {
{/* Top Banner */} -
+
- - + + -
+ {/*
-
+
*/}
-

Awesome! Your document is sent for signature

+

Awesome! Your document is sent for signature

- - - Edit + + + Edit
@@ -307,12 +307,12 @@ const DocumentCompletionScreen = () => { {/* Header */}
{/* Left Section - Document Details */} -
+
{/* Document Preview Card */} -
+
{selectedDocuments?.map((item, index) => ( -
+
{/* */} @@ -332,15 +332,17 @@ const DocumentCompletionScreen = () => { ))}
+ +
{/* Right Section - Signer Components */} -
+
{/* Signing Order Diagram */} {/* Signers List */} - +