diff --git a/src/App.tsx b/src/App.tsx index 7f24124..9fa7535 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,6 +19,7 @@ import Accounts from "./components/accounts/Accounts"; import Account from "./components/accounts/Account"; import TransactionView from "./components/transactions/TransactionView"; import Templates from "./components/templates/Templates"; +import DocumentCompletionScreen from "./components/process/CompletionScreen/DocumentCompletionScreen"; const App = () => { return ( @@ -65,6 +66,12 @@ const App = () => { } /> + + + + } /> + {/* Links work wothout Login */} } /> } /> diff --git a/src/components/CommonNav.tsx b/src/components/CommonNav.tsx index 49005a1..8a3d33f 100644 --- a/src/components/CommonNav.tsx +++ b/src/components/CommonNav.tsx @@ -1,363 +1,411 @@ 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 CommonNavProps { - accountName?: string | null; - transactionName?: string | null; +interface AccountNavProps { + accountName?: string | null; + transactionName?: string | null; } -// Define the shape of state object interface Account { - id: number; - isDefault: boolean; - name: string; + id: number; + isDefault: boolean; + name: string; } interface UserData { - id: string; - firstName: string; - lastName: string; - email: string; - language: string; - account: Account; + 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; } -const CommonNav: React.FC = ({ accountName = null, transactionName = null }) => { - // const user: UserState = useSelector((state: RootState) => state.user); +// Helper function to dispatch custom storage event +const dispatchSessionStorageChange = (key: string, newValue: any) => { + window.dispatchEvent(new CustomEvent('sessionStorageChange', { + detail: { key, newValue } + })); +}; - 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 CommonNav: React.FC = () => { + const activeAccountId = JSON.parse(sessionStorage.getItem('activeAccountId') || 'null'); + const userDetails = JSON.parse(sessionStorage.getItem('userDetails') || '{}'); + console.log("activeAccountId", activeAccountId); - const [user, setUser] = useState({ - id: "", - firstName: "", - lastName: "", - email: "", - language: "", - account: { - id: 0, - isDefault: false, - name: "", - }, + const [open, setOpen] = useState(false); + const [accountsOpen, setAccountsOpen] = useState(false); + const [loadingAccounts, setLoadingAccounts] = useState(false); + const [accounts, setAccounts] = useState([]); + const [hasModalBeenClosed, setHasModalBeenClosed] = useState(false); + const dropdownRef = useRef(null); + + const [user, setUser] = useState({ + id: 0, + name: "", + email: "", + language: "", + account: { + id: 0, + isDefault: false, + name: "", + }, + }); + + console.log("user", user) + const getAccountDetails = async () => { + try { + 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, + status: item.status, + })); + + setAccounts(cleanedData); + } catch (error) { + console.log(error); + } + }; + + const handleAccountsClick = async () => { + setAccountsOpen(!accountsOpen); + + if (!accountsOpen) { + setLoadingAccounts(true); + + if (accounts.length === 0 || hasModalBeenClosed) { + await getAccountDetails(); + setHasModalBeenClosed(false); + } + + setLoadingAccounts(false); + } + }; + + const handleAccountSwitch = (account: any) => { + console.log("handleAccountSwitch", account); + + // 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, + }, }); - 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 - const cleanedData: AccountDataArray[] = items.map((item: any) => ({ - accountId: item.accountId, - accountName: item.accountName, - role: item.role, - status: item.status, - })); + 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, + }, + })); - setAccounts(cleanedData); - } catch (error) { - console.log(error) - } + setAccountsOpen(false); + setOpen(false); + }; + + + const navigate = useNavigate(); + + const getUserDetails = async () => { + try { + 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); } + }; - - 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 - } - - setLoadingAccounts(false); - } - }; - - const handleAccountSwitch = (account) => { - setUser(prev => ({ - ...prev, - account: { name: account.name, id: account.id } - })); - - // Update accounts active status - setAccounts(prevAccounts => - prevAccounts.map(acc => ({ - ...acc, - isActive: acc.id === account.id - })) - ); - - setAccountsOpen(false); - setOpen(false); - }; - - const navigate = useNavigate() - - 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); - } + useEffect(() => { + if (!activeAccountId) { + getUserDetails(); + } else { + setUser(userDetails); } + }, []); - useEffect(() => { - getUserDetails() - }, []) + const location = useLocation(); + const rawPathnames = location.pathname + .split("/") + .filter((x) => x && isNaN(Number(x))); - const location = useLocation(); + // 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 pathnames = location.pathname - .split("/") - .filter((x) => x && isNaN(Number(x))); // remove numeric IDs + const labelMap: Record = { + accounts: "Accounts", + transactions: "Transactions", + templates: "Templates" + }; - 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 - } - setOpen(false); - setAccountsOpen(false); // Also close accounts dropdown - } - } - - document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); - }, [open]); - - const handleLogout = async () => { - try { - let res = await api.post('/api/auth/logout') - if (res.status === 200) { - setOpen(false); - navigate("/login") - } - } catch (error) { - console.log(error) - } - }; - - // Handle main dropdown toggle - const handleMainDropdownToggle = () => { + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { 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 - } - }; + setOpen(false); + setAccountsOpen(false); + } + } - const formatSegment = (segment: string) => - segment - .replace(/[-_]/g, " ") - .replace(/\b\w/g, (char) => char.toUpperCase()); + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [open]); + const handleLogout = async () => { + try { + let res = await api.post('/api/auth/logout'); + if (res.status === 200) { + sessionStorage.clear(); + setOpen(false); + navigate("/login"); + } + } catch (error) { + console.log(error); + } + }; - const ShimmerRow = () => ( -
-
-
-
-
-
-
-
-
+ const handleMainDropdownToggle = () => { + if (open) { + setHasModalBeenClosed(true); + } + setOpen(!open); + if (!open) { + setAccountsOpen(false); + } + }; + + const formatSegment = (segment: string) => + segment + .replace(/[-_]/g, " ") + .replace(/\b\w/g, (char) => char.toUpperCase()); + + const ShimmerRow = () => ( +
+
+
+
+
+
- ); +
+
+
+ ); + 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.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 fee10b2..ccb0d87 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, @@ -110,6 +110,14 @@ const Account = () => { // Add more cards here if needed ]; }; + + const [activeAccountId, setActiveAccountId] = useState( + JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') + ); + + const [currentEmailId, setCurrentEmailId] = useState('null'); + + console.log("activeAccountId Trans.", activeAccountId) // const { accountId } = useParams<{ accountId: string }>(); const [transactions, setTransactions] = useState([]); const [selectedTransactions, setSelectedTransactions] = useState([]); @@ -121,25 +129,58 @@ 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() - }, []) + 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); + }; + }, []); const getDashboardData = async () => { setIsLoading(true) const payload = { - account: accountID + account: activeAccountId }; try { @@ -154,50 +195,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 +277,7 @@ const Account = () => { return ( <> + isLoading={isLoading} /> {transactions.length > 0 && (
@@ -247,7 +289,8 @@ const Account = () => { - + + @@ -284,6 +327,9 @@ const Account = () => { + 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 */} -
+
+ + {/* 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 abc4e3a..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,30 +42,35 @@ interface AccountOption { const Setting = () => { const { page1 } = useParams<{ page1: string }>(); + console.log("page1", page1) const location = useLocation(); + 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); @@ -201,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() }, []) @@ -257,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) { @@ -290,7 +334,7 @@ const Setting = () => { setCreateAndInviteLoader(true) try { let res; - let toastMesaage ; + let toastMesaage; if (action === 'create') { console.log('Creating account:', data); const payloadAccount = { @@ -308,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", @@ -324,7 +368,7 @@ const Setting = () => { theme: "light", transition: Bounce, }); - + // Refetch account list after successful submission await getAccountList(); } @@ -337,7 +381,7 @@ const Setting = () => { return ( <> - + {/* */} {/* */}
{/* Main Navigation Tabs */} @@ -362,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: {
Active Last Change Expire After (days)SignAction
{tx.expireAfter ?? "-"} + {tx?.createdBy?.email === currentEmailId ? : "-"} +