Compare commits
5 Commits
bdaa3dc263
...
3f859a42de
| Author | SHA1 | Date |
|---|---|---|
|
|
3f859a42de | |
|
|
95d4a5ccff | |
|
|
6344d015db | |
|
|
14a0613f09 | |
|
|
9d52147827 |
|
|
@ -41,7 +41,7 @@ const App = () => {
|
||||||
<Route path="/accounts/:accountId/transactions" element={<Account />} />
|
<Route path="/accounts/:accountId/transactions" element={<Account />} />
|
||||||
<Route path="/transactions" element={<Account />} />
|
<Route path="/transactions" element={<Account />} />
|
||||||
<Route path="/accounts/:accountId/transactions/:transactionId" element={<TransactionView />} />
|
<Route path="/accounts/:accountId/transactions/:transactionId" element={<TransactionView />} />
|
||||||
<Route path="/transactions/:filterParam" element={<Transactions />} />
|
{/* <Route path="/transactions/:filterParam" element={<Transactions />} /> */}
|
||||||
<Route path="/templates" element={<Templates />} />
|
<Route path="/templates" element={<Templates />} />
|
||||||
{/* </Route> */}
|
{/* </Route> */}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,7 @@ const CommonNav: React.FC<AccountNavProps> = () => {
|
||||||
<header className="w-full px-6 py-2 flex justify-between items-center bg-white border-b rounded-sm border-gray-200">
|
<header className="w-full px-6 py-2 flex justify-between items-center bg-white border-b rounded-sm border-gray-200">
|
||||||
<nav className="text-md font-medium text-gray-800 capitalize flex items-center flex-wrap">
|
<nav className="text-md font-medium text-gray-800 capitalize flex items-center flex-wrap">
|
||||||
{pathnames.length === 0 ? (
|
{pathnames.length === 0 ? (
|
||||||
<span className="text-gray-900 font-semibold">Dashboard</span>
|
<span className="text-gray-900 font-semibold">Home</span>
|
||||||
) : (
|
) : (
|
||||||
pathnames.map((segment, index) => {
|
pathnames.map((segment, index) => {
|
||||||
const label = labelMap[segment] || formatSegment(segment);
|
const label = labelMap[segment] || formatSegment(segment);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
FaFilter,
|
FaFilter,
|
||||||
FaCalendarAlt
|
FaCalendarAlt
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
|
import TransactionDataTable from "./TransactionDataTable";
|
||||||
|
|
||||||
interface Transaction {
|
interface Transaction {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -28,12 +29,6 @@ interface Transaction {
|
||||||
expireAfter: number | null;
|
expireAfter: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatDate = (timestamp: number) => {
|
|
||||||
return new Date(timestamp * 1000).toLocaleString();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
const mapApiDataToCards = (data: any): CardData[] => {
|
const mapApiDataToCards = (data: any): CardData[] => {
|
||||||
return [
|
return [
|
||||||
|
|
@ -115,12 +110,9 @@ const Account = () => {
|
||||||
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
);
|
);
|
||||||
|
|
||||||
const [currentEmailId, setCurrentEmailId] = useState<string | null>('null');
|
|
||||||
|
|
||||||
console.log("activeAccountId Trans.", activeAccountId)
|
console.log("activeAccountId Trans.", activeAccountId)
|
||||||
// const { accountId } = useParams<{ accountId: string }>();
|
// const { accountId } = useParams<{ accountId: string }>();
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
|
||||||
const [accountsData, setAccountsData] = useState([])
|
const [accountsData, setAccountsData] = useState([])
|
||||||
const [inviteLoading, setInviteLoading] = useState(false);
|
const [inviteLoading, setInviteLoading] = useState(false);
|
||||||
const [cards, setCards] = useState<CardData[]>([]);
|
const [cards, setCards] = useState<CardData[]>([]);
|
||||||
|
|
@ -143,12 +135,6 @@ const Account = () => {
|
||||||
// getUserDetails()
|
// getUserDetails()
|
||||||
// }, [])
|
// }, [])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const userDetails = JSON.parse(sessionStorage.getItem('userDetails'));
|
|
||||||
setCurrentEmailId(userDetails?.email)
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
// Listen for sessionStorage changes
|
// Listen for sessionStorage changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleStorageChange = (e: StorageEvent) => {
|
const handleStorageChange = (e: StorageEvent) => {
|
||||||
|
|
@ -195,20 +181,9 @@ const Account = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchTransactions = async () => {
|
|
||||||
console.log("fetchTransactions")
|
|
||||||
try {
|
|
||||||
const response = await api(`/api/accounts/${activeAccountId}/transactions`);
|
|
||||||
setTransactions(response.data || []);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching transactions:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeAccountId) {
|
if (activeAccountId) {
|
||||||
getDashboardData()
|
getDashboardData()
|
||||||
fetchTransactions();
|
|
||||||
}
|
}
|
||||||
}, [activeAccountId]);
|
}, [activeAccountId]);
|
||||||
|
|
||||||
|
|
@ -276,68 +251,9 @@ const Account = () => {
|
||||||
<>
|
<>
|
||||||
<Cards type="transactions" onOpenModal={handleOpenModal} cards={cards}
|
<Cards type="transactions" onOpenModal={handleOpenModal} cards={cards}
|
||||||
isLoading={isLoading} />
|
isLoading={isLoading} />
|
||||||
{transactions.length > 0 && (
|
|
||||||
<div className="overflow-x-auto py-2">
|
|
||||||
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
|
|
||||||
<thead className="bg-gray-100 text-gray-700 uppercase text-xs tracking-wider">
|
|
||||||
<tr>
|
|
||||||
<th className="px-4 py-3 text-left"></th>
|
|
||||||
<th className="px-4 py-3 text-center">Name</th>
|
|
||||||
<th className="px-4 py-3 text-center">Status</th>
|
|
||||||
<th className="px-4 py-3 text-center">Active</th>
|
|
||||||
<th className="px-4 py-3 text-center">Last Change</th>
|
|
||||||
<th className="px-4 py-3 text-center">Expire After (days)</th>
|
|
||||||
<th className="px-4 py-3 text-center">Sign</th>
|
|
||||||
<th className="px-4 py-3 text-center">Action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="divide-y divide-gray-200">
|
|
||||||
{transactions.map((tx) => {
|
|
||||||
const lastModifiedAt = new Date(tx.lastModifiedAt, "DD/MM/YYYY");
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={tx.id}
|
|
||||||
onClick={() => handleTransactionClick(tx.id)}
|
|
||||||
className="hover:bg-gray-50 cursor-pointer"
|
|
||||||
>
|
|
||||||
<td className="px-4 py-3">
|
|
||||||
<div className="w-5 h-5 rounded border border-slate-300 flex justify-center items-center">
|
|
||||||
{selectedTransactions.includes(tx.id) && (
|
|
||||||
<FaCheck className="text-purple-600 text-sm" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3 text-center">
|
|
||||||
{tx.subject || "Untitled"}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3 text-center">
|
|
||||||
<span className="px-2 py-0.5 rounded-xl text-xs font-medium bg-blue-100 text-blue-700">
|
|
||||||
{tx.status}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3 text-center">
|
|
||||||
{tx.active ? "Yes" : "No"}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td className="px-4 py-3 text-center">
|
<TransactionDataTable />
|
||||||
{formatDate(tx.lastModifiedAt)}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3 text-center">
|
|
||||||
{tx.expireAfter ?? "-"}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3 text-center ">
|
|
||||||
{tx?.createdBy?.email === currentEmailId ? <button className="px-2 py-0.5 rounded-xl text-xs font-medium bg-green-100 text-green-700 cursor-pointer">Sign now</button> : "-"}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-3 text-center">
|
|
||||||
<PiDotsThreeOutlineVerticalFill className="text-lg text-slate-300 hover:text-black mx-auto" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isModalOpen && <AddInviteModal onClose={() => setIsModalOpen(false)} onInvite={handleInvite} inviteLoading={inviteLoading}
|
{isModalOpen && <AddInviteModal onClose={() => setIsModalOpen(false)} onInvite={handleInvite} inviteLoading={inviteLoading}
|
||||||
accountOptions={accountsData} />}
|
accountOptions={accountsData} />}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { PiDotsThreeOutlineVerticalFill } from 'react-icons/pi';
|
||||||
|
import api from '../../config/api';
|
||||||
|
import { Transaction } from '../transactions/Transactions';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const formatDate = (timestamp: number) => {
|
||||||
|
return new Date(timestamp * 1000).toLocaleString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const TransactionDataTable = () => {
|
||||||
|
const [currentEmailId, setCurrentEmailId] = useState<string | null>('null');
|
||||||
|
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
||||||
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(
|
||||||
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchTransactions = async () => {
|
||||||
|
console.log("fetchTransactions")
|
||||||
|
try {
|
||||||
|
const response = await api(`/api/accounts/${activeAccountId}/transactions`);
|
||||||
|
setTransactions(response.data || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching transactions:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeAccountId) {
|
||||||
|
fetchTransactions();
|
||||||
|
}
|
||||||
|
}, [activeAccountId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const userDetails = JSON.parse(sessionStorage.getItem('userDetails'));
|
||||||
|
setCurrentEmailId(userDetails?.email)
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{transactions.length > 0 && (
|
||||||
|
<div className="overflow-x-auto py-2">
|
||||||
|
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
|
||||||
|
<thead className="bg-gray-100 text-gray-700 uppercase text-xs tracking-wider">
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-3 text-left"></th>
|
||||||
|
<th className="px-4 py-3 text-center">Name</th>
|
||||||
|
<th className="px-4 py-3 text-center">Status</th>
|
||||||
|
<th className="px-4 py-3 text-center">Active</th>
|
||||||
|
<th className="px-4 py-3 text-center">Last Change</th>
|
||||||
|
<th className="px-4 py-3 text-center">Expire After (days)</th>
|
||||||
|
<th className="px-4 py-3 text-center">Sign</th>
|
||||||
|
<th className="px-4 py-3 text-center">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
{transactions.map((tx) => {
|
||||||
|
const lastModifiedAt = new Date(tx.lastModifiedAt, "DD/MM/YYYY");
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
key={tx.id}
|
||||||
|
onClick={() => handleTransactionClick(tx.id)}
|
||||||
|
className="hover:bg-gray-50 cursor-pointer"
|
||||||
|
>
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<div className="w-5 h-5 rounded border border-slate-300 flex justify-center items-center">
|
||||||
|
{selectedTransactions.includes(tx.id) && (
|
||||||
|
<FaCheck className="text-purple-600 text-sm" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
{tx.subject || "Untitled"}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
<span className="px-2 py-0.5 rounded-xl text-xs font-medium bg-blue-100 text-blue-700">
|
||||||
|
{tx.status}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
{tx.active ? "Yes" : "No"}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
{formatDate(tx.lastModifiedAt)}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
{tx.expireAfter ?? "-"}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-center ">
|
||||||
|
{tx?.createdBy?.email === currentEmailId ? <button className="px-2 py-0.5 rounded-xl text-xs font-medium bg-green-100 text-green-700 cursor-pointer">Sign now</button> : "-"}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-center">
|
||||||
|
<PiDotsThreeOutlineVerticalFill className="text-lg text-slate-300 hover:text-black mx-auto" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransactionDataTable
|
||||||
|
|
@ -144,7 +144,7 @@ const SignDocument = () => {
|
||||||
const [transaction, setTransaction] = useState<TransactionType | null>(null);
|
const [transaction, setTransaction] = useState<TransactionType | null>(null);
|
||||||
const [recSignStatus, setRecSignStatus] = useState<string | null>(null);
|
const [recSignStatus, setRecSignStatus] = useState<string | null>(null);
|
||||||
const [droppedItems, setDroppedItems] = useState([]);
|
const [droppedItems, setDroppedItems] = useState([]);
|
||||||
const[signTagId , setSignTagId] = useState<number>(0)
|
const [signTagId, setSignTagId] = useState<number>(0)
|
||||||
|
|
||||||
const [stamp, setStamp] = useState<StampTypeObj>({ open: false, blob: "" });
|
const [stamp, setStamp] = useState<StampTypeObj>({ open: false, blob: "" });
|
||||||
|
|
||||||
|
|
@ -183,7 +183,7 @@ const SignDocument = () => {
|
||||||
|
|
||||||
const [signConfObj, setSignConfObj] = useState<SignConfTypeObj | {}>({});
|
const [signConfObj, setSignConfObj] = useState<SignConfTypeObj | {}>({});
|
||||||
|
|
||||||
console.log("signConfObj",signConfObj)
|
console.log("signConfObj", signConfObj)
|
||||||
|
|
||||||
const zoom = (percentage: number, contentClass: string) => {
|
const zoom = (percentage: number, contentClass: string) => {
|
||||||
const content = document.querySelector(`.${contentClass}`);
|
const content = document.querySelector(`.${contentClass}`);
|
||||||
|
|
@ -244,8 +244,8 @@ const SignDocument = () => {
|
||||||
}
|
}
|
||||||
}, [saved])
|
}, [saved])
|
||||||
|
|
||||||
const handleSignClick = (index: number , id:number) => {
|
const handleSignClick = (index: number, id: number) => {
|
||||||
console.log("IDNumber" , id)
|
console.log("IDNumber", id)
|
||||||
setSignTagId(id)
|
setSignTagId(id)
|
||||||
if (saved.save) {
|
if (saved.save) {
|
||||||
if (index in signConfObj) {
|
if (index in signConfObj) {
|
||||||
|
|
@ -303,7 +303,7 @@ const SignDocument = () => {
|
||||||
console.log(res, "Line 288")
|
console.log(res, "Line 288")
|
||||||
|
|
||||||
setSelectedDocuments(res.data.documents)
|
setSelectedDocuments(res.data.documents)
|
||||||
setRecipient(res.data.recipient)
|
setRecipient(res.data.details)
|
||||||
setDroppedItems(res.data.tagJsons)
|
setDroppedItems(res.data.tagJsons)
|
||||||
setTransaction(res.data.transaction)
|
setTransaction(res.data.transaction)
|
||||||
setRecSignStatus(res.data.status)
|
setRecSignStatus(res.data.status)
|
||||||
|
|
@ -516,7 +516,7 @@ const SignDocument = () => {
|
||||||
headers: { "ngrok-skip-browser-warning": "69420" }
|
headers: { "ngrok-skip-browser-warning": "69420" }
|
||||||
});
|
});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
console.log("line 516" , response.data)
|
console.log("line 516", response.data)
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -810,6 +810,10 @@ const SignDocument = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<p className="text-xs text-slate-600">Hi! {recipient && recipient.firstName} {recipient && recipient.lastName}</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Right Side - STARTS */}
|
{/* Right Side - STARTS */}
|
||||||
<div className='flex justify-center items-center'>
|
<div className='flex justify-center items-center'>
|
||||||
|
|
||||||
|
|
@ -846,15 +850,15 @@ const SignDocument = () => {
|
||||||
{(!transparentTags && !hideTags) ? <button
|
{(!transparentTags && !hideTags) ? <button
|
||||||
// onClick={() => handleFormSubmitBtnClick()}
|
// onClick={() => handleFormSubmitBtnClick()}
|
||||||
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)] outline-none'>
|
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)] outline-none'>
|
||||||
<p
|
<p className='tracking-wide text-white'>
|
||||||
className='tracking-wide text-white'>Sign</p>
|
Sign
|
||||||
|
</p>
|
||||||
<FaSignature className='text-white text-md' />
|
<FaSignature className='text-white text-md' />
|
||||||
</button>
|
</button>
|
||||||
:
|
:
|
||||||
<p className="text-xs">Signed Successfully</p>
|
<p className="text-xs">Signed Successfully</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
<p>{recipient && recipient.firstName} {recipient && recipient.lastName}</p>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/* Right Side - ENDS */}
|
{/* Right Side - ENDS */}
|
||||||
|
|
@ -1024,7 +1028,7 @@ const SignDocument = () => {
|
||||||
<div
|
<div
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
onClick={() => handleSignClick(index , item.id)}
|
onClick={() => handleSignClick(index, item.id)}
|
||||||
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none bg-transparent" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none bg-transparent" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
||||||
// style={{ height: item.dimensions.height + "px" }}
|
// style={{ height: item.dimensions.height + "px" }}
|
||||||
>
|
>
|
||||||
|
|
@ -1041,8 +1045,8 @@ const SignDocument = () => {
|
||||||
height: `${item.dimensions.height}px`,
|
height: `${item.dimensions.height}px`,
|
||||||
}}
|
}}
|
||||||
// src={saved.signature as string} />
|
// src={saved.signature as string} />
|
||||||
src={`data:image/png;base64,${saved.signature}`}
|
src={`data:image/png;base64,${saved.signature}`}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>}
|
</div>}
|
||||||
{/* SIGNATURE FIELD - END */}
|
{/* SIGNATURE FIELD - END */}
|
||||||
|
|
@ -1053,7 +1057,7 @@ const SignDocument = () => {
|
||||||
<div
|
<div
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
onClick={() => handleSignClick(index , item.id)}
|
onClick={() => handleSignClick(index, item.id)}
|
||||||
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
||||||
// style={{ height: item.dimensions.height + "px" }}
|
// style={{ height: item.dimensions.height + "px" }}
|
||||||
>
|
>
|
||||||
|
|
@ -1067,8 +1071,8 @@ const SignDocument = () => {
|
||||||
}}
|
}}
|
||||||
className="w-[100%] h-full"
|
className="w-[100%] h-full"
|
||||||
// src={saved.initial as string}
|
// src={saved.initial as string}
|
||||||
src={`data:image/png;base64,${saved.initial}`}
|
src={`data:image/png;base64,${saved.initial}`}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>}
|
</div>}
|
||||||
{/* INITIALS FIELD - END */}
|
{/* INITIALS FIELD - END */}
|
||||||
|
|
@ -1274,7 +1278,13 @@ const SignDocument = () => {
|
||||||
|
|
||||||
|
|
||||||
<div style={{ display: `${openSign ? 'block' : 'none'}` }}>
|
<div style={{ display: `${openSign ? 'block' : 'none'}` }}>
|
||||||
<SignCon recipientId={recipientId as string} setOpenSign={setOpenSign} saved={saved} setSaved={setSaved} signTagId={signTagId}/>
|
{recipient &&
|
||||||
|
<SignCon
|
||||||
|
recipient={recipient}
|
||||||
|
recipientId={recipientId as string}
|
||||||
|
setOpenSign={setOpenSign} saved={saved}
|
||||||
|
setSaved={setSaved}
|
||||||
|
signTagId={signTagId} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: `${stamp.open ? 'block' : 'none'}` }}>
|
<div style={{ display: `${stamp.open ? 'block' : 'none'}` }}>
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,10 @@ const SendForSign: React.FC<Props> = ({ showSideBar }) => {
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 200 || res.status === 201) {
|
if (res.status === 200 || res.status === 201) {
|
||||||
dispatch(updateTransactionStatus(TRANSACTION_STATUS_ENUMS.TRANSACTION_INITIATED));
|
dispatch(updateTransactionStatus(TRANSACTION_STATUS_ENUMS.TRANSACTION_INITIATED));
|
||||||
dispatch(setTransactionId(res.data));
|
dispatch(setTransactionId(res.data.id));
|
||||||
let navigateUrl = `/process-step-one/${defaultAccountId}/${res.data}`;
|
let navigateUrl = `/process-step-one/${defaultAccountId}/${res.data.id}`;
|
||||||
if (signType === SignTypeEnum.selfSign) navigateUrl = `/process-step-one/${defaultAccountId}/${res.data}/self_sign`;
|
if (signType === SignTypeEnum.selfSign) navigateUrl = `/process-step-one/${defaultAccountId}/${res.data.id}/self_sign`;
|
||||||
setTimeout(() => navigate(navigateUrl), 0)
|
setTimeout(() => navigate(navigateUrl), 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => console.log(err))
|
.catch(err => console.log(err))
|
||||||
|
|
@ -81,31 +81,40 @@ const SendForSign: React.FC<Props> = ({ showSideBar }) => {
|
||||||
ref={dropDownRef}
|
ref={dropDownRef}
|
||||||
onBlur={() => setSendForSignDropdown(false)}
|
onBlur={() => setSendForSignDropdown(false)}
|
||||||
>
|
>
|
||||||
<div className={`flex justify-between items-center gap-2 cursor-pointer bg-[var(--primary)] rounded text-[var(--sendForSignTextCol)] ${showSideBar ? "" : "pt-[0.4rem] pr-[0.6rem] pb-[0.4rem] pl-[0.2rem]"}`}
|
<div
|
||||||
onClick={() => setSendForSignDropdown(!sendForSignDropdown)}>
|
className={`flex justify-between items-center gap-2 cursor-pointer bg-[var(--primary)] rounded text-[var(--sendForSignTextCol)] ${showSideBar ? "" : "pt-[0.4rem] pr-[0.6rem] pb-[0.4rem] pl-[0.2rem]"}`}>
|
||||||
<div className={`w-full`}>
|
<div
|
||||||
|
onClick={() => handleStartSectionClick(SignTypeEnum.multipleSign)}
|
||||||
|
className={`w-full`}>
|
||||||
{
|
{
|
||||||
showSideBar ?
|
showSideBar ?
|
||||||
<p className='text-center text-sm'>Send for signature</p>
|
<p
|
||||||
|
className='text-center text-sm'>Send for signature</p>
|
||||||
:
|
:
|
||||||
<IoIosSend className={`pl-2 text-3xl`} />
|
<IoIosSend className={`pl-2 text-3xl`} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{showSideBar && <p className='border-l-1 border-[var(--sendForSignTextCol)] px-2 py-2'>
|
{showSideBar &&
|
||||||
<IoMdArrowDropdown className={`text-2xl ${sendForSignDropdown ? 'rotate-180' : ''} transition-all duration-150`} />
|
<p
|
||||||
</p>}
|
className='border-l-1 border-[var(--sendForSignTextCol)] px-2 py-2'>
|
||||||
|
<IoMdArrowDropdown
|
||||||
|
onClick={() => setSendForSignDropdown(!sendForSignDropdown)}
|
||||||
|
className={`text-2xl ${sendForSignDropdown ? 'rotate-180' : ''} transition-all duration-150`} />
|
||||||
|
</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Drop Down Box */}
|
{/* Drop Down Box */}
|
||||||
{
|
{
|
||||||
sendForSignDropdown &&
|
sendForSignDropdown &&
|
||||||
<div className={`w-[300px] rounded absolute z-40 bg-[var(--sendForSignDropDownBg)] shadow-lg top-[54px] text-left outline-none`}>
|
<div className={`w-[300px] rounded absolute z-40 bg-[var(--sendForSignDropDownBg)] shadow-lg top-[54px] text-left outline-none`}>
|
||||||
<div className='py-3 px-8 leading-6 rounded-t-sm hover:bg-[var(--sendForSignDropDownHover)] cursor-pointer' onClick={() => handleStartSectionClick(SignTypeEnum.selfSign)}>
|
<div className='py-3 px-8 leading-6 rounded-t-sm hover:bg-[var(--sendForSignDropDownHover)] cursor-pointer'
|
||||||
|
onClick={() => handleStartSectionClick(SignTypeEnum.selfSign)}>
|
||||||
<p>Self Sign</p>
|
<p>Self Sign</p>
|
||||||
<p className='text-[var(--greyTextCol)] text-sm'>You're the only signer.</p>
|
<p className='text-[var(--greyTextCol)] text-sm'>You're the only signer.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full h-[1px] opacity-30 bg-[var(--greyTextCol)]'></div>
|
<div className='w-full h-[1px] opacity-30 bg-[var(--greyTextCol)]'></div>
|
||||||
<div className='py-3 px-8 leading-6 rounded-b-sm hover:bg-[var(--sendForSignDropDownHover)] cursor-pointer' onClick={() => handleStartSectionClick(SignTypeEnum.multipleSign)}>
|
<div className='py-3 px-8 leading-6 rounded-b-sm hover:bg-[var(--sendForSignDropDownHover)] cursor-pointer'
|
||||||
|
onClick={() => handleStartSectionClick(SignTypeEnum.multipleSign)}>
|
||||||
<p>Send for Sign</p>
|
<p>Send for Sign</p>
|
||||||
<p className='text-[var(--greyTextCol)] text-sm'>You and other need to sign.</p>
|
<p className='text-[var(--greyTextCol)] text-sm'>You and other need to sign.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import { FiUpload } from 'react-icons/fi'
|
||||||
|
import { uploadDocuments } from '../../store/slices/documentSlice';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { RootState } from '../../store/store';
|
||||||
|
import api from '../../config/api';
|
||||||
|
|
||||||
|
const StartNow = () => {
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
|
|
||||||
|
const userData = useSelector((state: RootState) => state.user);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleUpload = async (filesToUpload: File[]) => {
|
||||||
|
|
||||||
|
const accountId = userData.account?.id;
|
||||||
|
const transactionRes = await api.post(`/api/accounts/${userData.account?.id}/transactions`);
|
||||||
|
const transactionId = transactionRes.data.id
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", filesToUpload[0]);
|
||||||
|
|
||||||
|
dispatch(uploadDocuments({ formData, accountId, transactionId }))
|
||||||
|
.unwrap()
|
||||||
|
.then((res) => {
|
||||||
|
// toast.dismiss();
|
||||||
|
setFiles([]);
|
||||||
|
navigate(`/process-step-one/${accountId}/${transactionId}`);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Upload failed", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const selectedFiles = e.target.files ? Array.from(e.target.files) : [];
|
||||||
|
if (selectedFiles.length > 0) {
|
||||||
|
setFiles((prev) => [...prev, ...selectedFiles]);
|
||||||
|
handleUpload(selectedFiles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
className="group cursor-pointer border-slate-300 rounded-lg bg-white/60 transition-all duration-200 p-4 flex flex-col items-center justify-center gap-2 text-center shadow-sm hover:shadow-md">
|
||||||
|
{/* Upload icon with subtle animation */}
|
||||||
|
<div className="p-2 text-blue-600 group-hover:text-blue-700 transition-colors">
|
||||||
|
<FiUpload className="text-lg" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Compact text */}
|
||||||
|
<p className="text-xs text-gray-600">
|
||||||
|
<span className="text-blue-600 font-medium">Click to upload</span> or drag and drop
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="mt-1 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded shadow-xs transition-colors duration-150 focus:outline-none focus:ring-1 focus:ring-blue-400"
|
||||||
|
>
|
||||||
|
Start Now
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="text-[0.65rem] text-gray-400 mt-0.5">
|
||||||
|
PDF, JPG, PNG (max. 10MB)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
ref={fileInputRef}
|
||||||
|
className="hidden"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
accept=".pdf,.doc,.docx,.jpg,.png,.jpeg,.txt"
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StartNow
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import React, { ReactNode, useEffect } from 'react';
|
import React, { ReactNode, useEffect, useState } from 'react';
|
||||||
import { FiUpload, FiEdit, FiFolder, FiArrowRight } from 'react-icons/fi';
|
import { FiUpload, FiEdit, FiFolder, FiArrowRight } from 'react-icons/fi';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { AppDispatch, RootState } from '../../store/store';
|
import { AppDispatch, RootState } from '../../store/store';
|
||||||
import api from '../../config/api';
|
import api from '../../config/api';
|
||||||
import { setTransactionId, TRANSACTION_STATUS_ENUMS, updateTransactionStatus } from '../../store/slices/transactionSlice';
|
import { setTransactionId, TRANSACTION_STATUS_ENUMS, updateTransactionStatus } from '../../store/slices/transactionSlice';
|
||||||
|
import { Transaction, TransactionResponse } from '../transactions/Transactions';
|
||||||
|
import TransactionsTable from '../transactions/TransactionsTable';
|
||||||
|
import TransactionDataTable from '../accounts/TransactionDataTable';
|
||||||
|
import StartNow from './StartNow';
|
||||||
|
|
||||||
|
|
||||||
export interface StartCard {
|
export interface StartCard {
|
||||||
|
|
@ -17,13 +21,13 @@ export interface StartCard {
|
||||||
}
|
}
|
||||||
|
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
// {
|
||||||
icon: <FiUpload className="w-5 h-5 text-blue-700" />,
|
// icon: <FiUpload className="w-5 h-5 text-blue-700" />,
|
||||||
title: 'Start a Workflow',
|
// title: 'Start a Workflow',
|
||||||
description: 'Begin by uploading a document and tagging fields.',
|
// description: 'Begin by uploading a document and tagging fields.',
|
||||||
buttonText: 'Start Now',
|
// buttonText: 'Start Now',
|
||||||
bg: 'bg-white/60',
|
// bg: 'bg-white/60',
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
icon: <FiEdit className="w-5 h-5 text-green-700" />,
|
icon: <FiEdit className="w-5 h-5 text-green-700" />,
|
||||||
title: 'Sign an Agreement',
|
title: 'Sign an Agreement',
|
||||||
|
|
@ -76,41 +80,56 @@ const StartSection = () => {
|
||||||
// }, [])
|
// }, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="px-4 py-3 w-full">
|
<section className="px-4 pt-2 pb-3 w-full">
|
||||||
<h2 className="text-lg font-semibold text-gray-800 mb-2">Get Started</h2>
|
<h2 className="text-lg font-semibold text-gray-800 mb-2">Get Started</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
{cards.map((card, index) => (
|
|
||||||
<div
|
<StartNow />
|
||||||
onClick={() => handleStartSectionClick(card)}
|
|
||||||
key={index}
|
<div className='col-span-2 grid grid-cols-2 justify-between gap-4'>
|
||||||
className={`${card.bg} cursor-pointer relative border border-gray-200 rounded-xl p-4 shadow-sm hover:shadow-md transition-all h-full flex flex-col justify-between min-h-[220px]`}
|
{cards.map((card, index) => (
|
||||||
>
|
<div
|
||||||
{/* Icon + Text */}
|
onClick={() => handleStartSectionClick(card)}
|
||||||
<div className="flex flex-col gap-3">
|
key={index}
|
||||||
<div className="w-9 h-9 flex items-center justify-center border border-gray-200 rounded-md bg-white">
|
className={`${card.bg} w-full max-h-max cursor-pointer relative border border-gray-200 rounded-xl p-4 shadow-sm hover:shadow-md transition-all flex flex-col justify-between`}
|
||||||
{card.icon}
|
>
|
||||||
|
{/* Icon + Text */}
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="w-9 h-9 flex items-center justify-center border border-gray-200 rounded-md bg-white">
|
||||||
|
{card.icon}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="text-base font-medium text-gray-900">{card.title}</h3>
|
||||||
|
<p className="text-sm text-gray-700">{card.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-base font-medium text-gray-900">{card.title}</h3>
|
{/* Bottom Area */}
|
||||||
<p className="text-sm text-gray-700">{card.description}</p>
|
<div className="flex items-center justify-between mt-2">
|
||||||
</div>
|
{card.buttonText ? (
|
||||||
|
<button className="text-sm cursor-pointer font-medium text-white bg-gradient-to-r from-blue-600 to-blue-500 px-4 py-2 rounded-md shadow hover:opacity-90 transition">
|
||||||
|
{card.buttonText}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Bottom Area */}
|
<FiArrowRight className="w-4 h-4 text-gray-500" />
|
||||||
<div className="flex items-center justify-between mt-2">
|
</div>
|
||||||
{card.buttonText ? (
|
|
||||||
<button className="text-sm cursor-pointer font-medium text-white bg-gradient-to-r from-blue-600 to-blue-500 px-4 py-2 rounded-md shadow hover:opacity-90 transition">
|
|
||||||
{card.buttonText}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FiArrowRight className="w-4 h-4 text-gray-500" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section className='pt-2 pb-3'>
|
||||||
|
<h2 className="mt-3 text-lg font-semibold text-gray-800 mb-2">Recent Activities</h2>
|
||||||
|
<section>
|
||||||
|
<TransactionDataTable />
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react';
|
||||||
import { ContactTypeObj } from './RecipientAddCard'
|
import { ContactTypeObj } from './RecipientAddCard';
|
||||||
import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
|
import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
|
||||||
|
|
||||||
interface ContactDataProps {
|
interface ContactDataProps {
|
||||||
|
|
@ -10,66 +10,127 @@ interface ContactDataProps {
|
||||||
openContactModal: boolean;
|
openContactModal: boolean;
|
||||||
setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setRecipients: React.Dispatch<React.SetStateAction<Recipient>>;
|
setRecipients: React.Dispatch<React.SetStateAction<Recipient>>;
|
||||||
|
topRef?: React.RefObject<HTMLDivElement>;
|
||||||
|
setManualFocusTriggered: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContactModal: React.FC<ContactDataProps> = ({ contactData, inpFirstName, inpLastName, inpEmail, openContactModal, setRecipients, setOpenContactModal }) => {
|
// Helper function to highlight matching text
|
||||||
|
const highlightText = (text: string, searchTerm: string, shouldHighlight: boolean) => {
|
||||||
|
if (!shouldHighlight || !searchTerm.trim()) {
|
||||||
|
return <span>{text}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchTermLower = searchTerm.toLowerCase();
|
||||||
|
const textLower = text.toLowerCase();
|
||||||
|
const index = textLower.indexOf(searchTermLower);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return <span>{text}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeMatch = text.slice(0, index);
|
||||||
|
const match = text.slice(index, index + searchTerm.length);
|
||||||
|
const afterMatch = text.slice(index + searchTerm.length);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{beforeMatch}
|
||||||
|
<span className="bg-yellow-200 font-medium">{match}</span>
|
||||||
|
{afterMatch}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContactModal: React.FC<ContactDataProps> = ({
|
||||||
|
contactData,
|
||||||
|
inpFirstName,
|
||||||
|
inpLastName,
|
||||||
|
inpEmail,
|
||||||
|
openContactModal,
|
||||||
|
setRecipients,
|
||||||
|
setOpenContactModal ,
|
||||||
|
setManualFocusTriggered
|
||||||
|
}) => {
|
||||||
const handleContactClick = (indx: number) => {
|
const handleContactClick = (indx: number) => {
|
||||||
if (!contactData.contacts.length) return;
|
if (!contactData.contacts.length) return;
|
||||||
|
|
||||||
setRecipients((prev) => {
|
setRecipients((prev) => ({
|
||||||
return {
|
...prev, // Preserve other properties like uuid, newOne, etc.
|
||||||
firstName: contactData.contacts[indx].firstName,
|
firstName: contactData.contacts[indx].firstName,
|
||||||
lastName: contactData.contacts[indx].lastName,
|
lastName: contactData.contacts[indx].lastName,
|
||||||
email: contactData.contacts[indx].email,
|
email: contactData.contacts[indx].email,
|
||||||
type: prev.type
|
}));
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setOpenContactModal(false);
|
setOpenContactModal(false);
|
||||||
|
setManualFocusTriggered(false)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (event: React.KeyboardEvent, indx: number) => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault();
|
||||||
|
handleContactClick(indx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!openContactModal || !contactData.contacts.length) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="max-h-60 overflow-y-auto">
|
||||||
{
|
{contactData.contacts.map((contact, indx) => (
|
||||||
contactData.contacts.map((recipient, indx) => {
|
<div
|
||||||
return <div
|
key={contact.id || indx}
|
||||||
tabIndex={1}
|
tabIndex={0}
|
||||||
onClick={() => handleContactClick(indx)}
|
onMouseDown={() => handleContactClick(indx)}
|
||||||
style={{display: `${openContactModal ? "flex" : "none"}`}}
|
onKeyDown={(e) => handleKeyDown(e, indx)}
|
||||||
className='flex items-center px-3 py-2 cursor-pointer hover:bg-slate-100 transiton-all w-full'>
|
className="flex items-center px-3 py-2 cursor-pointer hover:bg-gray-50 transition-colors duration-150 focus:bg-gray-100 focus:outline-none rounded-md"
|
||||||
<div
|
role="button"
|
||||||
key={indx}
|
aria-label={`Select contact ${contact.firstName} ${contact.lastName}`}
|
||||||
className='w-8 h-8 bg-gray-200 rounded-full'></div>
|
>
|
||||||
<div className='ml-2'>
|
{/* Avatar */}
|
||||||
<div className='flex gap-1.5'>
|
<div className="w-8 h-8 bg-[var(--primaryLight2)] rounded-full flex items-center justify-center text-white text-sm font-medium">
|
||||||
<p className='text-sm font-semibold'
|
{(contact.firstName?.[0] || '') + (contact.lastName?.[0] || '')}
|
||||||
style={{ background: `${contactData.type === "firstName" ? `linear-gradient(to right, yellow 0ch, yellow ${inpFirstName.length}ch, white ${inpFirstName.length}ch, white 100%)` : "white"}` }}
|
</div>
|
||||||
>
|
|
||||||
{recipient.firstName}
|
{/* Contact Info */}
|
||||||
</p>
|
<div className="ml-3 flex-1 min-w-0">
|
||||||
<p
|
<div className="flex gap-1.5 items-center">
|
||||||
style={{ background: `${contactData.type === "lastName" ? `linear-gradient(to right, yellow 0ch, yellow ${inpLastName.length}ch, white ${inpLastName.length}ch, white 100%)` : "white"}` }}
|
<p className="text-sm font-semibold text-gray-900 truncate">
|
||||||
className='text-sm font-semibold'>
|
{highlightText(
|
||||||
{recipient.lastName}
|
contact.firstName,
|
||||||
</p>
|
inpFirstName,
|
||||||
</div>
|
contactData.type === "firstName"
|
||||||
<p className='text-xs text-gray-500'
|
)}
|
||||||
style={{ background: `${contactData.type === "email" ? `linear-gradient(to right, yellow 0ch, yellow ${inpEmail.length}ch, white ${inpEmail.length}ch, white 100%)` : "white"}` }}
|
</p>
|
||||||
>
|
<p className="text-sm font-semibold text-gray-900 truncate">
|
||||||
{recipient.email}
|
{highlightText(
|
||||||
|
contact.lastName,
|
||||||
|
inpLastName,
|
||||||
|
contactData.type === "lastName"
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 truncate">
|
||||||
|
{highlightText(
|
||||||
|
contact.email,
|
||||||
|
inpEmail,
|
||||||
|
contactData.type === "email"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
})
|
</div>
|
||||||
}
|
))}
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// return <p
|
{/* No results message */}
|
||||||
// key={indx}
|
{contactData.contacts.length === 0 && (
|
||||||
// className='w-full hover:bg-slate-100 cursor-pointer px-2 py-1'>
|
<div className="px-3 py-4 text-center text-gray-500 text-sm">
|
||||||
// {val.firstName}
|
No contacts found
|
||||||
// </p>
|
</div>
|
||||||
export default ContactModal
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactModal;
|
||||||
|
|
@ -6,7 +6,7 @@ import SelectedDocuments from "./SelectedDocuments";
|
||||||
import api from "../../../config/api";
|
import api from "../../../config/api";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../store/store";
|
import { RootState } from "../../../store/store";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { setImages } from "../../../store/slices/selectedDocsSlice";
|
import { setImages } from "../../../store/slices/selectedDocsSlice";
|
||||||
import { addRecipient, addRecipientAsync, fetchRecipients, recipientType, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientIds } from "../../../store/slices/selectedRecipientsSlice";
|
import { addRecipient, addRecipientAsync, fetchRecipients, recipientType, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientIds } from "../../../store/slices/selectedRecipientsSlice";
|
||||||
|
|
@ -39,8 +39,8 @@ const ProcessStepOne = () => {
|
||||||
dispatch(toggleToTemplates());
|
dispatch(toggleToTemplates());
|
||||||
}
|
}
|
||||||
|
|
||||||
const [editCase , setEditCase] = useState<string>("")
|
const [editCase, setEditCase] = useState<string>("")
|
||||||
const [editRecipient , setEditRecipient] = useState({})
|
const [editRecipient, setEditRecipient] = useState({})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,6 +55,12 @@ const ProcessStepOne = () => {
|
||||||
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
||||||
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
||||||
const userData = useSelector((state: RootState) => state.user)
|
const userData = useSelector((state: RootState) => state.user)
|
||||||
|
// ProcessStepOne.tsx
|
||||||
|
const topRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
// const userData = useSelector((state: RootState) => state.user)
|
||||||
|
|
||||||
|
const [transactionDetails, setTransactionDetails] = useState<any>([]);
|
||||||
|
|
||||||
console.log("selectedRecipients", selectedRecipients)
|
console.log("selectedRecipients", selectedRecipients)
|
||||||
|
|
||||||
|
|
@ -96,7 +102,7 @@ const ProcessStepOne = () => {
|
||||||
formError.subject = true
|
formError.subject = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SignTypeEnum.selfSign === signType) formError.recipients = false;
|
if (SignTypeEnum.selfSign === signType) formError.recipients = false;
|
||||||
|
|
||||||
if (formError.documents || formError.recipients || formError.subject) {
|
if (formError.documents || formError.recipients || formError.subject) {
|
||||||
console.log(selectedDocuments, 'line 193')
|
console.log(selectedDocuments, 'line 193')
|
||||||
|
|
@ -160,11 +166,12 @@ const ProcessStepOne = () => {
|
||||||
language: "en",
|
language: "en",
|
||||||
name: `${recipient.firstName} ${recipient.lastName}`,
|
name: `${recipient.firstName} ${recipient.lastName}`,
|
||||||
roleName: recipient.roleName,
|
roleName: recipient.roleName,
|
||||||
allowSenderToEdit: recipient.allowSenderToEdit ,
|
allowSenderToEdit: recipient.allowSenderToEdit,
|
||||||
allowSenderToDelete: recipient.allowSenderToDelete,
|
allowSenderToDelete: recipient.allowSenderToDelete,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let recipientsDataTemp = recipientsData;
|
let recipientsDataTemp = recipientsData;
|
||||||
|
let recipientId = null;
|
||||||
|
|
||||||
if (signType === SignTypeEnum.selfSign) {
|
if (signType === SignTypeEnum.selfSign) {
|
||||||
recipientsDataTemp = [{
|
recipientsDataTemp = [{
|
||||||
|
|
@ -189,9 +196,18 @@ const ProcessStepOne = () => {
|
||||||
name: `${userData.firstName} ${userData.lastName}`,
|
name: `${userData.firstName} ${userData.lastName}`,
|
||||||
roleName: ""
|
roleName: ""
|
||||||
}]
|
}]
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(addRecipientAsync({ recipientsData: recipientsDataTemp, accountId, transactionId }));
|
try {
|
||||||
|
const basePath = `/api/accounts/${accountId}/transactions/${transactionId}/recipients`;
|
||||||
|
const res = await api.post(basePath, recipientsDataTemp)
|
||||||
|
recipientId = res.data[0].id;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error Creating Self Sign Recipient", error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dispatch(addRecipientAsync({ recipientsData: recipientsDataTemp, accountId, transactionId }));
|
||||||
|
}
|
||||||
dispatch(showLoader("Processing Message..."));
|
dispatch(showLoader("Processing Message..."));
|
||||||
|
|
||||||
console.log(`1/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
|
console.log(`1/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
|
||||||
|
|
@ -213,11 +229,23 @@ const ProcessStepOne = () => {
|
||||||
dispatch(resetDocuments())
|
dispatch(resetDocuments())
|
||||||
dispatch(resetRecipients())
|
dispatch(resetRecipients())
|
||||||
dispatch(hideLoader());
|
dispatch(hideLoader());
|
||||||
if (transactionTemplateToggle === 'templates') {
|
if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES) {
|
||||||
console.log(`/process-step-two/${accountId}/template/${transactionId}`)
|
console.log(`/process-step-two/${accountId}/template/${transactionId}`)
|
||||||
navigate(`/process-step-two/${accountId}/template/${transactionId}`)
|
navigate(`/process-step-two/${accountId}/template/${transactionId}`)
|
||||||
} else {
|
} else {
|
||||||
navigate(`/process-step-two/${accountId}/${transactionId}`);
|
if (signType === SignTypeEnum.selfSign) {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { status: "SENT" })
|
||||||
|
console.log(res, "Check123");
|
||||||
|
navigate(`/sign/${recipientId}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Error Transaction Sent", err.message);
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
} else {
|
||||||
|
navigate(`/process-step-two/${accountId}/${transactionId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
@ -270,15 +298,6 @@ const ProcessStepOne = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-center gap-3'>
|
<div className='flex items-center gap-3'>
|
||||||
<div className="flex items-center" >
|
|
||||||
<input
|
|
||||||
checked={signType === SignTypeEnum.selfSign}
|
|
||||||
onChange={(e) => handleSelfSignCb(e)}
|
|
||||||
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded-sm outline-none"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label htmlFor="default-checkbox" className="ms-2 text-sm text-slate-600">SELF SIGN</label>
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleNextClick(false)}
|
onClick={() => handleNextClick(false)}
|
||||||
className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition"
|
className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition"
|
||||||
|
|
@ -298,17 +317,40 @@ const ProcessStepOne = () => {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Recipients */}
|
{/* Recipients */}
|
||||||
{
|
<section className="mt-8">
|
||||||
signType !== SignTypeEnum.selfSign &&
|
|
||||||
< section className="mt-8">
|
{
|
||||||
<h2 className="text-lg font-medium mb-4 pt-6 border-t text-[var(--processStepOneTextHead)]">Signers</h2>
|
transactionTemplateToggle !== TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES &&
|
||||||
<div className="flex flex-col gap-4 align-start">
|
<div className="flex justify-between items-center">
|
||||||
<RecipientCards transactionTemplateToggle={transactionTemplateToggle} setEditRecipient={setEditRecipient} setEditCase={setEditCase}/>
|
<h2 className="text-lg font-medium mb-4 pt-6 border-t text-[var(--processStepOneTextHead)]">Signers</h2>
|
||||||
{/* {signType !== "self-sign" && <RecipientAddCard />} */}
|
<div className="flex items-center mr-2">
|
||||||
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} editRecipient={editRecipient} editCase={editCase} setEditRecipient={setEditRecipient} setEditCase={setEditCase}/>
|
<input
|
||||||
|
checked={signType === SignTypeEnum.selfSign}
|
||||||
|
onChange={(e) => handleSelfSignCb(e)}
|
||||||
|
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded-sm outline-none"
|
||||||
|
/>
|
||||||
|
<label htmlFor="default-checkbox" className="ms-2 text-sm text-slate-600">SELF SIGN</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
}
|
||||||
}
|
|
||||||
|
{
|
||||||
|
signType !== SignTypeEnum.selfSign &&
|
||||||
|
<div className="flex flex-col gap-4 align-start">
|
||||||
|
<RecipientCards transactionTemplateToggle={transactionTemplateToggle} setEditRecipient={setEditRecipient} setEditCase={setEditCase} />
|
||||||
|
{/* {signType !== "self-sign" && <RecipientAddCard />} */}
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref={topRef}
|
||||||
|
className={`relative w-full bg-[var(--mainViewBg)] ${globalLoader.isLoading ? 'pointer-events-none h-screen overflow-hidden' : ''}`}
|
||||||
|
>
|
||||||
|
|
||||||
|
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} editRecipient={editRecipient} editCase={editCase} setEditRecipient={setEditRecipient} setEditCase={setEditCase} topRef={topRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* Email */}
|
{/* Email */}
|
||||||
<section className="mt-8">
|
<section className="mt-8">
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,11 @@ export interface ContactTypeObj {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,setEditRecipient, setEditCase }) => {
|
const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase, setEditRecipient, setEditCase, topRef }) => {
|
||||||
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(
|
||||||
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
||||||
const [openContactModal, setOpenContactModal] = useState(false);
|
const [openContactModal, setOpenContactModal] = useState(false);
|
||||||
|
|
@ -32,11 +36,31 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const formRef = useRef<HTMLFormElement | null>(null);
|
const formRef = useRef<HTMLFormElement | null>(null);
|
||||||
|
|
||||||
const [recipient, setRecipients] = useState<Recipient>({ uuid:null, newOne:false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false });
|
const [recipient, setRecipients] = useState<Recipient>({ uuid: null, newOne: false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false });
|
||||||
|
|
||||||
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
|
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
|
||||||
|
|
||||||
|
const [cachedContacts, setCachedContacts] = useState<ContactType[]>([]);
|
||||||
|
const [filteredContacts, setFilteredContacts] = useState<ContactType[]>([]);
|
||||||
|
const [activeField, setActiveField] = useState<"firstName" | "lastName" | "email" | null>(null);
|
||||||
|
const [hasFetchedInitial, setHasFetchedInitial] = useState(false);
|
||||||
|
const [lastQuery, setLastQuery] = useState<string | null>(null);
|
||||||
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [manualFocusTriggered, setManualFocusTriggered] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
console.log(recipient)
|
console.log(recipient)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openContactModal) {
|
||||||
|
if (topRef?.current) {
|
||||||
|
topRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
} else {
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [openContactModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editCase === "Edit" && editRecipient) {
|
if (editCase === "Edit" && editRecipient) {
|
||||||
setRecipients({
|
setRecipients({
|
||||||
|
|
@ -51,56 +75,55 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false,
|
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false,
|
||||||
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false,
|
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false,
|
||||||
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
|
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
|
||||||
newOne:editRecipient.newOne ?? false
|
newOne: editRecipient.newOne ?? false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [editCase, editRecipient]);
|
}, [editCase, editRecipient]);
|
||||||
|
|
||||||
|
|
||||||
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
console.log("recipientrecipient",recipient)
|
console.log("recipientrecipient", recipient)
|
||||||
|
|
||||||
if (!recipient.lastName || !recipient.email || !recipient.type) return;
|
if (!recipient.lastName || !recipient.email || !recipient.type) return;
|
||||||
|
|
||||||
// ✅ Email format validation
|
// ✅ Email format validation
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
if (!emailRegex.test(recipient.email)) {
|
if (!emailRegex.test(recipient.email)) {
|
||||||
console.log("Invalid email format");
|
console.log("Invalid email format");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editCase === 'Edit') {
|
if (editCase === 'Edit') {
|
||||||
dispatch(updateRecipient(recipient));
|
dispatch(updateRecipient(recipient));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addRecipient({
|
dispatch(addRecipient({
|
||||||
...recipient,
|
...recipient,
|
||||||
uuid:uuidv4(),
|
uuid: uuidv4(),
|
||||||
newOne:true
|
newOne: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Reset state and form
|
setRecipients({
|
||||||
setRecipients({
|
firstName: "",
|
||||||
firstName: "",
|
lastName: "",
|
||||||
lastName: "",
|
email: "",
|
||||||
email: "",
|
hostName: "",
|
||||||
hostName: "",
|
hostEmail: "",
|
||||||
hostEmail: "",
|
roleName: "",
|
||||||
roleName: "",
|
type: recipientType.SIGNER,
|
||||||
type: recipientType.SIGNER,
|
allowSenderToEdit: false,
|
||||||
allowSenderToEdit: false,
|
allowSenderToDelete: false,
|
||||||
allowSenderToDelete: false,
|
uuid: "",
|
||||||
uuid:"",
|
newOne: false
|
||||||
newOne:false
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// setRecipients(null),
|
// setRecipients(null),
|
||||||
setEditCase("")
|
setEditCase("")
|
||||||
|
setManualFocusTriggered(false)
|
||||||
formRef.current?.reset();
|
formRef.current?.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -155,7 +178,97 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
}, 350)
|
}, 350)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleFocus = async (field: "firstName" | "lastName" | "email") => {
|
||||||
|
setManualFocusTriggered(true);
|
||||||
|
setOpenContactModal(true);
|
||||||
|
setActiveField(field);
|
||||||
|
setLastQuery(null); // Reset to allow fresh input fetch if needed
|
||||||
|
|
||||||
|
if (!hasFetchedInitial) {
|
||||||
|
try {
|
||||||
|
const res = await api.get(`/api/accounts/${activeAccountId}/contacts?pageSize=10`);
|
||||||
|
const contacts = res.data?.content || [];
|
||||||
|
setCachedContacts(contacts);
|
||||||
|
setFilteredContacts(contacts);
|
||||||
|
setContactData({ type: field, contacts });
|
||||||
|
setHasFetchedInitial(true);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Initial contact fetch failed:", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFilteredContacts(cachedContacts);
|
||||||
|
setContactData({ type: field, contacts: cachedContacts });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useDebouncedEffect(() => {
|
||||||
|
console.log("useDebouncedEffect")
|
||||||
|
if (!activeField || !manualFocusTriggered) return;
|
||||||
|
|
||||||
|
const inputValue = recipient[activeField].trim().toLowerCase();
|
||||||
|
|
||||||
|
if (inputValue.length === 0) {
|
||||||
|
setFilteredContacts(cachedContacts);
|
||||||
|
setContactData({ type: activeField, contacts: cachedContacts });
|
||||||
|
setOpenContactModal(true); // Keep showing modal with all options
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local match
|
||||||
|
const localMatches = cachedContacts.filter(contact =>
|
||||||
|
contact[activeField]?.toLowerCase().includes(inputValue)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (localMatches.length > 0) {
|
||||||
|
setFilteredContacts(localMatches);
|
||||||
|
setContactData({ type: activeField, contacts: localMatches });
|
||||||
|
setOpenContactModal(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastQuery !== inputValue) {
|
||||||
|
setLastQuery(inputValue);
|
||||||
|
api
|
||||||
|
.get(`/api/accounts/${activeAccountId}/contacts?pageSize=5&${activeField}=${inputValue}`)
|
||||||
|
.then((res) => {
|
||||||
|
const fetched = res.data?.content || [];
|
||||||
|
setFilteredContacts(fetched);
|
||||||
|
setContactData({ type: activeField, contacts: fetched });
|
||||||
|
setOpenContactModal(true);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
}, [recipient.firstName, recipient.lastName, recipient.email], 300);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (e: MouseEvent | FocusEvent) => {
|
||||||
|
if (
|
||||||
|
containerRef.current &&
|
||||||
|
!containerRef.current.contains(e.target as Node)
|
||||||
|
) {
|
||||||
|
setOpenContactModal(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
document.addEventListener("focusin", handleClickOutside); // Also handles tabbing out
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
document.removeEventListener("focusin", handleClickOutside);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div ref={containerRef}>
|
||||||
<form
|
<form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
onSubmit={handleRecipientAdd}
|
onSubmit={handleRecipientAdd}
|
||||||
|
|
@ -291,7 +404,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
<div className="grid grid-cols-12 gap-4 mt-3">
|
<div className="grid grid-cols-12 gap-4 mt-3">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
onFocus={() => setOpenContactModal(true)}
|
onFocus={() => handleFocus("firstName")}
|
||||||
onBlur={handleContactModalBlur}
|
onBlur={handleContactModalBlur}
|
||||||
value={recipient.firstName}
|
value={recipient.firstName}
|
||||||
placeholder="First Name"
|
placeholder="First Name"
|
||||||
|
|
@ -301,7 +414,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
<input
|
<input
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
onFocus={() => setOpenContactModal(true)}
|
onFocus={() => handleFocus("lastName")}
|
||||||
onBlur={handleContactModalBlur}
|
onBlur={handleContactModalBlur}
|
||||||
value={recipient.lastName}
|
value={recipient.lastName}
|
||||||
placeholder="Last Name"
|
placeholder="Last Name"
|
||||||
|
|
@ -311,7 +424,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
<input
|
<input
|
||||||
required
|
required
|
||||||
type="email"
|
type="email"
|
||||||
onFocus={() => setOpenContactModal(true)}
|
onFocus={() => handleFocus("email")}
|
||||||
onBlur={handleContactModalBlur}
|
onBlur={handleContactModalBlur}
|
||||||
value={recipient.email}
|
value={recipient.email}
|
||||||
placeholder="Recipient Email"
|
placeholder="Recipient Email"
|
||||||
|
|
@ -345,8 +458,8 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{/* Contact Modal */}
|
{/* Contact Modal */}
|
||||||
{contactData.contacts.length > 0 && openContactModal && (
|
{openContactModal && contactData.contacts.length > 0 && (
|
||||||
<div className="absolute z-50 mt-1 w-full max-w-2xl bg-white shadow-lg rounded-md p-3 border border-gray-100">
|
<div className="absolute z-50 mt-1 w-2/3 max-h-[50vh] bg-white shadow-lg rounded-md p-3 border border-gray-100 overflow-y-auto no-scrollbar">
|
||||||
<ContactModal
|
<ContactModal
|
||||||
contactData={contactData}
|
contactData={contactData}
|
||||||
inpFirstName={recipient.firstName}
|
inpFirstName={recipient.firstName}
|
||||||
|
|
@ -355,10 +468,14 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
openContactModal={openContactModal}
|
openContactModal={openContactModal}
|
||||||
setRecipients={setRecipients}
|
setRecipients={setRecipients}
|
||||||
setOpenContactModal={setOpenContactModal}
|
setOpenContactModal={setOpenContactModal}
|
||||||
|
topRef={topRef}
|
||||||
|
setManualFocusTriggered={setManualFocusTriggered}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Signing options + Add button in one line */}
|
{/* Signing options + Add button in one line */}
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||||
<div className="flex flex-wrap gap-6">
|
<div className="flex flex-wrap gap-6">
|
||||||
|
|
@ -385,11 +502,11 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
type="submit"
|
type="submit"
|
||||||
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
||||||
>
|
>
|
||||||
<FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"}
|
<FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,24 @@ const SelectedDocuments = ({transactionTemplateToggle}) => {
|
||||||
{ name: 'Dropbox', icon: <FaDropbox /> },
|
{ name: 'Dropbox', icon: <FaDropbox /> },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleUpload = (filesToUpload: File[]) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", filesToUpload[0])
|
||||||
|
console.log(formData, "Line 59")
|
||||||
|
// filesToUpload.forEach((file) => formData.append("document", file));
|
||||||
|
|
||||||
|
dispatch(uploadDocuments({ formData, accountId, transactionId }))
|
||||||
|
.unwrap()
|
||||||
|
.then((res) => {
|
||||||
|
toast.dismiss();
|
||||||
|
setFiles([]);
|
||||||
|
setModalOpen(false);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Upload failed", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const selectedFiles = e.target.files ? Array.from(e.target.files) : [];
|
const selectedFiles = e.target.files ? Array.from(e.target.files) : [];
|
||||||
if (selectedFiles.length > 0) {
|
if (selectedFiles.length > 0) {
|
||||||
|
|
@ -66,40 +84,6 @@ const SelectedDocuments = ({transactionTemplateToggle}) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpload = (filesToUpload: File[]) => {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file", filesToUpload[0])
|
|
||||||
console.log(formData, "Line 59")
|
|
||||||
// filesToUpload.forEach((file) => formData.append("document", file));
|
|
||||||
|
|
||||||
dispatch(uploadDocuments({ formData, accountId, transactionId }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => {
|
|
||||||
toast.dismiss();
|
|
||||||
// toast.info("📄 Documents uploaded!", {
|
|
||||||
// position: "bottom-right",
|
|
||||||
// autoClose: 5000,
|
|
||||||
// hideProgressBar: false,
|
|
||||||
// closeOnClick: false,
|
|
||||||
// pauseOnHover: true,
|
|
||||||
// draggable: true,
|
|
||||||
// progress: undefined,
|
|
||||||
// theme: "light",
|
|
||||||
// transition: Bounce,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Update selected documents after upload
|
|
||||||
// dispatch(setSelectedDocuments([...documents, ...res]));
|
|
||||||
|
|
||||||
// debugger;
|
|
||||||
setFiles([]);
|
|
||||||
setModalOpen(false);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Upload failed", err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Upload Card First */}
|
{/* Upload Card First */}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ interface SelectStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
|
const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
|
||||||
|
recipient,
|
||||||
setOpenSign,
|
setOpenSign,
|
||||||
recipientId,
|
recipientId,
|
||||||
saved,
|
saved,
|
||||||
|
|
@ -86,7 +87,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
|
||||||
signTagId
|
signTagId
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
console.log("savedModal", saved)
|
console.log("savedModal", recipient)
|
||||||
const [activeTab, setActiveTab] = useState<'TYPED' | 'DRAW' | 'UPLOAD' | 'SAVED'>('TYPED');
|
const [activeTab, setActiveTab] = useState<'TYPED' | 'DRAW' | 'UPLOAD' | 'SAVED'>('TYPED');
|
||||||
const [fullName, setFullName] = useState('');
|
const [fullName, setFullName] = useState('');
|
||||||
const [initials, setInitials] = useState('');
|
const [initials, setInitials] = useState('');
|
||||||
|
|
@ -274,8 +275,8 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
|
||||||
|
|
||||||
if (res?.data) {
|
if (res?.data) {
|
||||||
setSignatures({
|
setSignatures({
|
||||||
fullSignature:"",
|
fullSignature: "",
|
||||||
initials:"",
|
initials: "",
|
||||||
})
|
})
|
||||||
setSaved({
|
setSaved({
|
||||||
saveType: res.data.signatureType ?? null,
|
saveType: res.data.signatureType ?? null,
|
||||||
|
|
@ -506,17 +507,16 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
|
||||||
|
|
||||||
const getUserDetails = async () => {
|
const getUserDetails = async () => {
|
||||||
try {
|
try {
|
||||||
let res = await axios.get(`/api/user/details`, {
|
// let res = await axios.get(`/api/user/details`, {
|
||||||
headers: {
|
// headers: {
|
||||||
"ngrok-skip-browser-warning": "69420",
|
// "ngrok-skip-browser-warning": "69420",
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
console.log("getuser", res);
|
// console.log("getuser", res);
|
||||||
let currUser = res?.data;
|
// let currUser = res?.data;
|
||||||
setUser(currUser);
|
setUser(recipient);
|
||||||
|
setFullName(recipient.firstName + " " + recipient.lastName);
|
||||||
setFullName(currUser.firstName + " " + currUser.lastName);
|
setInitials(recipient.firstName.slice(0, 1) + recipient.lastName.slice(0, 1));
|
||||||
setInitials(currUser.firstName.slice(0, 1) + currUser.lastName.slice(0, 1));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching user details:", error);
|
console.error("Error fetching user details:", error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import TransactionTopNav from './TransactionTopNav';
|
import TransactionTopNav from './TransactionTopNav';
|
||||||
import api from '../../config/api';
|
import api from '../../config/api';
|
||||||
|
import TransactionsTable from './TransactionsTable';
|
||||||
|
|
||||||
interface Document {
|
interface Document {
|
||||||
documentId: string;
|
documentId: string;
|
||||||
|
|
@ -19,7 +20,7 @@ interface Recipient {
|
||||||
signedDateTime?: string;
|
signedDateTime?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Transaction {
|
export interface Transaction {
|
||||||
transactionId: string;
|
transactionId: string;
|
||||||
name: string;
|
name: string;
|
||||||
createAt: string;
|
createAt: string;
|
||||||
|
|
@ -30,7 +31,7 @@ interface Transaction {
|
||||||
recipients: Recipient[];
|
recipients: Recipient[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransactionResponse {
|
export interface TransactionResponse {
|
||||||
content: Transaction[];
|
content: Transaction[];
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
|
|
@ -53,70 +54,7 @@ const Transactions = () => {
|
||||||
<div>
|
<div>
|
||||||
{filterParam && <TransactionTopNav filterType={filterParam} />}
|
{filterParam && <TransactionTopNav filterType={filterParam} />}
|
||||||
{/* <h2 className="text-cl font-semibold text-gray-800 mb-2.5">Transactions</h2> */}
|
{/* <h2 className="text-cl font-semibold text-gray-800 mb-2.5">Transactions</h2> */}
|
||||||
|
{/* <TransactionsTable transactionData={transactionData} /> */}
|
||||||
{
|
|
||||||
|
|
||||||
transactionData.length > 0 ?
|
|
||||||
<div className="overflow-x-auto bg-white shadow-md rounded-lg border border-gray-200">
|
|
||||||
<table className="min-w-full text-sm text-gray-700">
|
|
||||||
<thead className="bg-gray-100 text-left text-xs uppercase tracking-wider text-gray-600">
|
|
||||||
<tr>
|
|
||||||
<th className="px-6 py-3">Name</th>
|
|
||||||
<th className="px-6 py-3">Created At</th>
|
|
||||||
<th className="px-6 py-3">Status</th>
|
|
||||||
<th className="px-6 py-3">Documents</th>
|
|
||||||
<th className="px-6 py-3">Recipients</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{transactionData.map((tx) => (
|
|
||||||
<tr key={tx.transactionId} className="border-t hover:bg-gray-50 transition duration-150">
|
|
||||||
<td className="px-6 py-4 font-medium text-gray-800">
|
|
||||||
{tx.name?.trim() || 'N/A'}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4">
|
|
||||||
{new Date(tx.createAt).toLocaleString()}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4">
|
|
||||||
<span className="inline-block px-2 py-1 text-xs font-semibold rounded bg-blue-100 text-blue-800">
|
|
||||||
{tx.status}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4">
|
|
||||||
<ul className="list-disc list-inside space-y-1">
|
|
||||||
{tx.documents.map((doc) => (
|
|
||||||
<li key={doc.documentId}>{doc.name}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 space-y-2">
|
|
||||||
{tx.recipients.map((rec) => (
|
|
||||||
<div key={rec.id} className="text-sm">
|
|
||||||
<div className="font-semibold">
|
|
||||||
{rec.firstName} {rec.lastName}
|
|
||||||
</div>
|
|
||||||
<div className="text-gray-600">Status: {rec.status}</div>
|
|
||||||
{rec.signedDateTime && (
|
|
||||||
<div className="text-green-600">
|
|
||||||
Signed: {new Date(rec.signedDateTime).toLocaleString()}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div> :
|
|
||||||
<div className='flex justify-center items-center mt-6 text-slate-400 gap-2'>
|
|
||||||
<hr className='w-full border-dashed' />
|
|
||||||
<p className='whitespace-nowrap text-sm'>No Data Found</p>
|
|
||||||
<hr className='w-full border-dashed' />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Transaction } from './Transactions'
|
||||||
|
|
||||||
|
interface TransactionsTableInterface {
|
||||||
|
transactionData: Transaction[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionsTable : React.FC<TransactionsTableInterface>= ({transactionData}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
|
||||||
|
transactionData.length > 0 ?
|
||||||
|
<div className="overflow-x-auto bg-white shadow-md rounded-lg border border-gray-200">
|
||||||
|
<table className="min-w-full text-sm text-gray-700">
|
||||||
|
<thead className="bg-gray-100 text-left text-xs uppercase tracking-wider text-gray-600">
|
||||||
|
<tr>
|
||||||
|
<th className="px-6 py-3">Name</th>
|
||||||
|
<th className="px-6 py-3">Created At</th>
|
||||||
|
<th className="px-6 py-3">Status</th>
|
||||||
|
<th className="px-6 py-3">Documents</th>
|
||||||
|
<th className="px-6 py-3">Recipients</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{transactionData.map((tx) => (
|
||||||
|
<tr key={tx.transactionId} className="border-t hover:bg-gray-50 transition duration-150">
|
||||||
|
<td className="px-6 py-4 font-medium text-gray-800">
|
||||||
|
{tx.name?.trim() || 'N/A'}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
{new Date(tx.createAt).toLocaleString()}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<span className="inline-block px-2 py-1 text-xs font-semibold rounded bg-blue-100 text-blue-800">
|
||||||
|
{tx.status}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<ul className="list-disc list-inside space-y-1">
|
||||||
|
{tx.documents.map((doc) => (
|
||||||
|
<li key={doc.documentId}>{doc.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 space-y-2">
|
||||||
|
{tx.recipients.map((rec) => (
|
||||||
|
<div key={rec.id} className="text-sm">
|
||||||
|
<div className="font-semibold">
|
||||||
|
{rec.firstName} {rec.lastName}
|
||||||
|
</div>
|
||||||
|
<div className="text-gray-600">Status: {rec.status}</div>
|
||||||
|
{rec.signedDateTime && (
|
||||||
|
<div className="text-green-600">
|
||||||
|
Signed: {new Date(rec.signedDateTime).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div> :
|
||||||
|
<div className='flex justify-center items-center mt-6 text-slate-400 gap-2'>
|
||||||
|
<hr className='w-full border-dashed' />
|
||||||
|
<p className='whitespace-nowrap text-sm'>No Data Found</p>
|
||||||
|
<hr className='w-full border-dashed' />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransactionsTable
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary: #41644A;
|
--primary: #3D74B6;
|
||||||
--primaryLight: #ffffff;
|
--primaryLight: #ffffff;
|
||||||
--primaryLight2: #4b7455;
|
--primaryLight2: #6a92c2;
|
||||||
--primaryLight3: #dae9de;
|
--primaryLight3: #dae9de;
|
||||||
--dashBoardBg: #ffffff;
|
--dashBoardBg: #ffffff;
|
||||||
--mainViewBg: #edeaeb;
|
--mainViewBg: #edeaeb;
|
||||||
--highlightBg1: #edeaeb;
|
--highlightBg1: #edeaeb;
|
||||||
--menuTextCol: #98a0a8;
|
--menuTextCol: #98a0a8;
|
||||||
--menuTextColActive: #41644A;
|
--menuTextColActive: #3D74B6;
|
||||||
--menuBgColActive: #edeaeb;
|
--menuBgColActive: #edeaeb;
|
||||||
--menuTextHoverCol: #e7e7e7;
|
--menuTextHoverCol: #e7e7e7;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
--lightGreyCol: #e1e1e1;
|
--lightGreyCol: #e1e1e1;
|
||||||
--compBgCol: #ffffff;
|
--compBgCol: #ffffff;
|
||||||
|
|
||||||
--avatarBgCol: #41644A;
|
--avatarBgCol: #3D74B6;
|
||||||
--avatarTextCol: #ffffff;
|
--avatarTextCol: #ffffff;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -240,8 +240,6 @@ export const uploadDocuments = createAsyncThunk<
|
||||||
|
|
||||||
console.log(`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`)
|
console.log(`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`,
|
`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`,
|
||||||
formData,
|
formData,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue