This commit is contained in:
Bhagwan Singh 2026-05-15 17:06:32 +05:30
parent 42b4c1dd5b
commit 7612f6a3d3
14 changed files with 232 additions and 142 deletions

2
.env
View File

@ -6,5 +6,7 @@
# VITE_BASE_URI="https://smashing-albacore-engaging.ngrok-free.app"
# VITE_BASE_URI="https://basically-polished-yeti.ngrok-free.app"
# VITE_BASE_URI="https://sacha-countryfied-evolutionistically.ngrok-free.dev"
# VITE_BASE_URI="https://web.signmadeasy.app"
# VITE_SSE_URI="https://web.signmadeasy.app"
VITE_BASE_URI="https://web.signmadeasy.app"
VITE_SSE_URI="https://web.signmadeasy.app"

View File

@ -9,7 +9,7 @@
"build:skip-check": "tsc --noEmit && vite build",
"lint": "eslint .",
"preview": "vite preview",
"deploy": "npm run build:skip-check && scp -i private\\SME-Web.pem -r dist root@100.22.185.166:/var/www/sme"
"deploy": "npm run build:skip-check && pscp -i private\\SME-Web.ppk -r dist root@100.22.185.166:/var/www/sme"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",

View File

@ -114,7 +114,7 @@ const App = () => {
<Route path="/view" element={<DocView />} />
<Route path="/session_expired" element={<SessionExpired />} />
<Route path="/transaction-complete" element={<CompletionScreen />} />
<Route path="/transaction-complete/:recipientId/" element={<CompletionScreen />} />
<Route path="/transaction-declined" element={<CompletionScreen hit="Action Canceled! " title="Maybe Next Time!" message={" Your request has been declined successfully. You can revisit anytime if you change your mind."} textColorSuccess={false} />} />
<Route path="/transaction-reassigned" element={<CompletionScreen hit="Reasssigned Successfully" title=" The transaction has been reassigned!" message={"The transaction has been reassigned to another user"} textColorSuccess={false} />} />

View File

@ -1,6 +1,8 @@
import React, { useEffect } from "react";
import { RiFileList3Line } from "react-icons/ri";
import confetti from 'canvas-confetti'
import handleDownloadDocument from "../../helper/handleDownloadDocumentRecSide";
import { useParams } from "react-router-dom";
interface CompletionScreenProps {
hit?: string;
@ -15,7 +17,7 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
message = "Your document is now complete and securely stored. We’ve emailed you a copy for your reference.",
textColorSuccess = true
}) => {
const { recipientId } = useParams();
useEffect(() => {
@ -60,10 +62,11 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
{/* Header */}
<header className="px-4 py-3 flex justify-center sm:justify-start items-center bg-white shadow-md z-10">
<div className="flex items-center gap-2">
<RiFileList3Line className="text-[var(--mainPrimaryBtn)] text-2xl" />
{/* <RiFileList3Line className="text-[var(--mainPrimaryBtn)] text-2xl" />
<span className="text-md font-semibold text-[var(--mainPrimaryBtn)]">
Sign made easy
</span>
</span> */}
<img class="py-2 w-[140px]" alt="" src="/sme-colored.png"></img>
</div>
</header>
@ -76,6 +79,13 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
{message}
</p>
<p
onClick={() => { handleDownloadDocument(recipientId || "") }}
className="text-white bg-[var(--mainPrimaryBtn)] px-3 py-1 rounded-sm mt-4 cursor-pointer">
Download Signed Documents
</p>
{/* <button className="flex items-center gap-2 mt-6 bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 transition">
<FiDownload />
Download
@ -84,11 +94,10 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
{/* <button className="mt-6 flex items-center gap-2 bg-orange-500 text-white px-6 py-2 rounded-md hover:bg-orange-600 transition">
<FiZap /> Login
</button> */}
<p className="relative z-50 font-semibold mt-4 2xl:mt-6 text-md 2xl:text-xl">
Access your documents anytime — just <a href="/login" className="text-white bg-[var(--mainPrimaryBtn)] px-3 py-1 rounded-sm">Login</a>
<p className="relative z-50 font-semibold mt-2 2xl:mt-4 text-md 2xl:text-xl">
Access your documents anytime — just <a href="/login" className="text-[var(--mainPrimaryBtn)] py-1 border-b-1 border-dashed">Login</a>
</p>
{/* Illustration placeholder */}
<div className="mt-6 2xl:mt-10">
{textColorSuccess ? <img

View File

@ -3,6 +3,8 @@ import { FaSignature, FaDownload, FaUserEdit, FaTimesCircle, FaAngleLeft, FaAngl
import { BsThreeDotsVertical } from "react-icons/bs";
import ClientDownloadDocument from "./ClientDownloadDocument";
import { AiOutlineAudit } from "react-icons/ai";
import { useParams } from "react-router-dom";
import handleDownloadDocument from "../../helper/handleDownloadDocumentRecSide";
interface Props {
transparentTags?: boolean;
@ -30,6 +32,8 @@ export default function SignButton({
const [menuOpen, setMenuOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const { recipientId } = useParams();
const [showMenuDownload, setShowMenuDownload] = useState<{ open: boolean, transactionId: string | null }>({
open: false,
transactionId: null
@ -91,6 +95,18 @@ export default function SignButton({
<span>Download Document</span>
</button>
</li> */}
<li>
<button
onClick={() => {
handleDownloadDocument(recipientId || "")
setMenuOpen(false);
}}
className="flex items-center gap-3 w-full cursor-pointer text-left px-4 py-2 hover:bg-gray-100"
>
<AiOutlineAudit />
<span>Download Documents</span>
</button>
</li>
<li>
<button
onClick={() => {

View File

@ -529,7 +529,7 @@ const SignDocument = () => {
// setShowCompletionScreen(true)
setSelectedTag(null)
// navigate("/transaction-complete")
window.location.href = `/transaction-complete`
window.location.href = `/transaction-complete/${recipientId}`
}
} catch (e) {
dispatch(hideLoader());
@ -1170,7 +1170,7 @@ const SignDocument = () => {
{/* Side details of documents Pages and document Name - END */}
{!transparentTags && focusElement != null && focusElement.documentId == doc.documentId && focusElement.pageIndex == page.index &&
{!transparentTags && focusElement != null && focusElement && Object.keys(focusElement).length > 1 && focusElement.documentId == doc.documentId && focusElement.pageIndex == page.index &&
<div
id="tag-navigation-btn"
className={`hidden absolute right-0 translate-x-[100%] sm:flex justify-center gap-0 items-center z-50 cursor-pointer transition-all duration-300`}
@ -1326,7 +1326,7 @@ const SignDocument = () => {
className="bg-[var(--mainPrimaryBtn)] p-1 text-2xl text-white rounded-md" />
</div> */}
<div>
{!transparentTags && focusElement != null &&
{!transparentTags && focusElement != null && Object.keys(focusElement).length > 1 &&
<div
id="tag-navigation-btn-mb"
className={`sm:hidden flex justify-center gap-0 items-center z-50 cursor-pointer transition-all duration-300`}

View File

@ -5,7 +5,7 @@ import { RootState } from "../../../store/store";
import { useEffect, useRef, useState } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { addRecipientAsync, fetchRecipients, recipientType, removeAllRecipients, removeRecipientAsync, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from "../../../store/slices/selectedRecipientsSlice";
import { addRecipientAsync, fetchRecipients, recipientType, removeAllRecipients, removeRecipientAsync, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientOrder } from "../../../store/slices/selectedRecipientsSlice";
import { TRANSACTION_STATUS_ENUMS, updateTransactionStatus } from "../../../store/slices/transactionSlice";
import { hideLoader, showLoader } from "../../../store/slices/loaderSlice";
import Loader from "../../dashboard/Loader";
@ -415,13 +415,29 @@ const ProcessStepOne = () => {
)
}
function handleSigningOrderChange() {
setSigningOrder(!signingOrder)
if (!signingOrder) {
selectedRecipients.forEach((recipient, index) => {
const id = recipient.id || recipient.uuid || "1"
dispatch(updateRecipientOrder({ id, routingOrder: index + 1 }));
})
} else {
selectedRecipients.forEach((recipient, index) => {
const id = recipient.id || recipient.uuid || "1"
dispatch(updateRecipientOrder({ id, routingOrder: 1 }));
})
}
}
return (
<>
<div className={`relative w-full h-screen bg-[var(--mainViewBg)] ${globalLoader.isLoading ? 'pointer-events-none overflow-hidden' : ''}`}>
{/* Top Navigation - Fixed */}
<div className='fixed top-0 left-0 right-0 w-full bg-white shadow z-50 px-4 py-1.5 lg:py-3'>
<div className='fixed top-0 left-0 right-0 w-full bg-white shadow z-50 px-4 py-1.5 lg:py-3 z-50'>
<div className='flex flex-col sm:flex-row sm:items-center sm:justify-between gap-1 flex-wrap'>
{/* Left Section */}
@ -490,7 +506,7 @@ const ProcessStepOne = () => {
{/* Documents */}
<section>
<h2 className="text-lg font-medium mb-4 pt-3 text-[var(--processStepOneTextHead)]">Documents</h2>
<h2 className="text-lg font-medium mb-4 pt-3 text-[var(--processStepOneTextHead)]">Add documents</h2>
<div id="documents_con" className={`${submitError && submitError.documents && submitError.showError ? "border border-red-500 rounded-sm p-2 ring-2 ring-red-300" : ""}`}>
<SelectedDocuments transactionTemplateToggle={transactionTemplateToggle} />
<p className="text-xs text-right px-2 pt-2 text-red-500">{submitError && submitError.documents && submitError.showError && "At least one document is required."}</p>
@ -500,40 +516,40 @@ const ProcessStepOne = () => {
{/* Recipients */}
<section className="mt-8">
<p className="border-t border-gray-200"></p>
{!isTemplate &&
transactionTemplateToggle != TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES &&
<div className="flex justify-between items-center">
<h2 className=" text-lg font-medium mb-4 pt-6 text-[var(--processStepOneTextHead)]">Signers</h2>
{/* <div className="flex items-center mr-3">
<div className="flex justify-between items-center">
<h2 className=" text-lg font-medium mb-4 pt-6 text-[var(--processStepOneTextHead)]">Add recipients</h2>
{!isTemplate &&
transactionTemplateToggle != TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES &&
<>
{/* <div className="flex items-center mr-3">
<input
checked={signType == SignTypeEnum.selfSign}
onChange={(e) => handleSelfSignCb(e)}
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none"
checked={signType == SignTypeEnum.selfSign}
onChange={(e) => handleSelfSignCb(e)}
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none"
/>
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
I AM THE ONLY SIGNER</label>
</div> */}
I AM THE ONLY SIGNER</label>
</div> */}
</>
}
{/* HERE WE AREEEEEEEE */}
{selectedRecipients.length > 1 && <div className="flex items-center">
<input
// checked={signType == SignTypeEnum.selfSign}
onChange={handleSigningOrderChange}
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none"
/>
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
Set signing order
</label>
</div>}
{/* HERE WE AREEEEEEEE */}
</div>
{/* HERE WE AREEEEEEEE */}
<div className="flex items-center">
<input
// checked={signType == SignTypeEnum.selfSign}
onChange={() => setSigningOrder(!signingOrder)}
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none"
/>
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
Set signing order
</label>
</div>
{/* HERE WE AREEEEEEEE */}
</div>
}
{
(signType != SignTypeEnum.selfSign || showSelfSignWarnModal) &&
<>
@ -553,7 +569,7 @@ const ProcessStepOne = () => {
{/* Email */}
<section className="mt-8">
<h2 className="text-lg font-medium mb-4 pt-6 border-t border-gray-200 text-[var(--processStepOneTextHead)]">Email</h2>
<h2 className="text-lg font-medium mb-4 pt-6 border-t border-gray-200 text-[var(--processStepOneTextHead)]">Add Message</h2>
{/* Subject */}
<div className="border-1 rounded-xl border-gray-300 px-6 py-8">

View File

@ -357,10 +357,10 @@ const RecipientCards = ({
};
return (
<div className="w-full rounded-md bg-slate-50">
<div className="w-full rounded-md">
{recipients.length > 0 ? (
<>
{recipients.length > 1 && (
{/* {recipients.length > 1 && (
<div className="flex sm:flex-row sm:items-center justify-between px-3 sm:px-4 py-2 border-b border-gray-300 gap-2">
<h3 className="text-sm font-semibold text-gray-700">
Recipients ({recipients.length})
@ -373,7 +373,7 @@ const RecipientCards = ({
<FaUsersLine className="text-[var(--primary)] mt-0.5 text-lg" />
</label>
</div>
)}
)} */}
<DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
@ -381,13 +381,14 @@ const RecipientCards = ({
items={recipients.map((r) => r.id || r.uuid)}
strategy={verticalListSortingStrategy}
>
<div className="flex flex-col divide-y divide-gray-200 max-h-[47vh] overflow-y-auto custom-scrollbar">
<div className="flex flex-col max-h-[47vh] overflow-y-auto custom-scrollbar">
{recipients.map((recipient, idx) => (
<SortableRecipient
key={recipient.id || recipient.uuid}
recipient={recipient}
idx={idx}
editRecipientCurrentEmail={editRecipientCurrentEmail}
signingOrder={signingOrder}
>
<div
key={recipient.email + idx}
@ -399,63 +400,73 @@ const RecipientCards = ({
<>
{/* Main Content Section */}
<div className="flex items-start sm:items-center gap-2 sm:gap-3 min-w-0 flex-1 pl-8">
<div className="flex items-start sm:items-center gap-2 sm:gap-3 min-w-0 flex-1">
{/* Order Number */}
{recipients.length > 1 && signingOrder ? (
<div className="relative flex-shrink-0">
<input
type="text"
value={recipient.routingOrder == 0 ? "" : recipient.routingOrder}
onChange={(e) => {
const value = e.target.value;
if (/^\d*$/.test(value)) {
const num = Number(value);
handleOrderChange(recipient.id || recipient.uuid, num);
<div className="">
{recipients.length > 1 && signingOrder ? (
<div className="relative flex-shrink-0">
<input
type="text"
value={recipient.routingOrder == 0 ? "" : recipient.routingOrder}
onChange={(e) => {
const value = e.target.value;
if (/^\d*$/.test(value)) {
const num = Number(value);
handleOrderChange(recipient.id || recipient.uuid, num);
}
}}
onKeyDown={(e) => {
if (!/[0-9]/.test(e.key) && e.key != "Backspace" && e.key != "Tab") {
e.preventDefault();
}
}}
className="w-8 sm:w-10 h-6 text-center rounded text-sm text-gray-600 focus:outline-none focus:ring-[var(--primary)] border border-dashed border-gray-400"
/>
<>
{idx == 0 && recipients.length > 1 && <p className="group">
<>
{/* <BiSolidHelpCircle className="absolute top-[-12px] right-[-10px] text-gray-400 cursor-pointer" />
<span className="absolute top-[2px] left-[48px] hidden group-hover:block w-56 p-2 rounded-lg text-sm bg-white text-slate-600 shadow-lg z-10">
Signing order: Recipients receive emails one after another.
Same order means they all receive at once.
</span> */}
</>
</p>}
</>
</div>
) : (
<>{signingOrder &&
<input
type="number"
disabled
value={recipient.routingOrder}
min={1}
onChange={(e) =>
handleOrderChange(
recipient.id || recipient.uuid,
Number(e.target.value)
)
}
}}
onKeyDown={(e) => {
if (!/[0-9]/.test(e.key) && e.key != "Backspace" && e.key != "Tab") {
e.preventDefault();
}
}}
className="w-8 sm:w-10 h-6 text-center rounded text-sm text-gray-600 focus:outline-none focus:ring-[var(--primary)] border border-dashed border-gray-400"
/>
{idx == 0 && recipients.length > 1 && <p className="group">
<BiSolidHelpCircle className="absolute top-[-12px] right-[-10px] text-gray-400 cursor-pointer" />
{/* Tooltip */}
<span className="absolute top-[2px] left-[48px] hidden group-hover:block w-56 p-2 rounded-lg text-sm bg-white text-slate-600 shadow-lg z-10">
Signing order: Recipients receive emails one after another.
Same order means they all receive at once.
</span>
</p>}
</div>
) : (
<>{signingOrder &&
<input
type="number"
disabled
value={recipient.routingOrder}
min={1}
onChange={(e) =>
handleOrderChange(
recipient.id || recipient.uuid,
Number(e.target.value)
)
}
className="w-6 sm:w-8 text-center rounded text-sm text-gray-700 border-none focus:outline-none focus:ring-[var(--primary)] flex-shrink-0"
/>}
</>
)}
className="w-6 sm:w-8 text-center rounded text-sm text-gray-700 border-none focus:outline-none focus:ring-[var(--primary)] flex-shrink-0 border-2 border-slate-200"
/>}
</>
)}
</div>
{/* Avatar */}
{/* <div className="flex-shrink-0 w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-[var(--primary)] text-white flex items-center justify-center font-semibold text-xs select-none">
{getInitials(`${recipient.firstName} ${recipient.lastName || ""}`)}
</div> */}
{/* Form Fields Section */}
<div className="min-w-0 flex-1">
<div className={`min-w-0 py-4 ${signingOrder ? "pl-8" : "pl-2"} flex-1 border-l-8 ${idx % 2 == 0 ? 'border-[#A5EDFC]' : 'border-[#84EFC3]'}`}>
{/* Role Name and Permissions Row */}
<div className="flex flex-col lg:flex-row lg:items-center gap-2 mb-2">
{<div className="flex lg:w-1/2">
@ -515,9 +526,9 @@ const RecipientCards = ({
</div>
{/* Name and Email Fields */}
<div className="mb-2 w-[92%]">
<div className="mb-2 w-[100%]">
{/* Mobile: Stack vertically, Tablet+: Side by side */}
<div className="flex flex-col sm:flex-row gap-1 sm:gap-2">
<div className="flex flex-col justify-center items-center sm:flex-row gap-1 sm:gap-2">
<div className="flex flex-col sm:flex-row gap-1 sm:gap-2 sm:flex-1">
<div className="w-full sm:w-1/2">
<div className="font-semibold text-gray-800 text-xs">
@ -535,6 +546,26 @@ const RecipientCards = ({
{renderEditableField(recipient, "email", "Recipient Email", "", "email")}
</div>
</div>
{/* Action Buttons - START*/}
{signType != "self-sign" && (
<div className="ml-4">
<button
type="button"
title="Remove recipient"
aria-label={`Remove recipient ${recipient.firstName}`}
onClick={() => handleRemoveRecipient(recipient)}
disabled={
transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES
? false
: !recipient.newOne && !recipient.allowSenderToDelete
}
className="text-gray-400 hover:text-red-600 transition-colors cursor-pointer disabled:opacity-50 p-1"
>
<ImBin size={14} className="sm:w-4 sm:h-4" />
</button>
</div>
)}
{/* Action Buttons - END*/}
</div>
</div>
@ -600,26 +631,6 @@ const RecipientCards = ({
</div>
</div>
</div>
{/* Action Buttons */}
{signType != "self-sign" && (
<div className="lg:ml-2 absolute top-[-8px] right-[-5px] px-1.5 py-1.5">
<button
type="button"
title="Remove recipient"
aria-label={`Remove recipient ${recipient.firstName}`}
onClick={() => handleRemoveRecipient(recipient)}
disabled={
transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES
? false
: !recipient.newOne && !recipient.allowSenderToDelete
}
className="text-gray-400 hover:text-red-600 transition-colors cursor-pointer disabled:opacity-50 p-1"
>
<ImBin size={14} className="sm:w-4 sm:h-4" />
</button>
</div>
)}
</>

View File

@ -4,7 +4,7 @@ import { MdDragIndicator } from "react-icons/md";
import { useSelector } from "react-redux";
import { RootState } from "../../../store/store";
const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children }: any) => {
const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children, signingOrder }: any) => {
const selectedRecipientsLen = useSelector((state: RootState) => state.selectedRecipients.recipients.length);
@ -22,12 +22,12 @@ const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children
transition,
opacity: isDragging ? 0.6 : 1,
};
return (
<div
ref={setNodeRef}
style={style}
className={`relative flex flex-col lg:flex-row lg:items-center justify-between px-2 sm:px-3 lg:px-2 py-3 transition-colors gap-3 lg:gap-0 ${(recipient.uuid || recipient.id) == editRecipientCurrentEmail ? "bg-neutral-100 opacity-40" : ""}
className={`relative flex flex-col lg:flex-row lg:items-center justify-between gap-3 lg:gap-0 my-3 ${(recipient.uuid || recipient.id) == editRecipientCurrentEmail ? "bg-neutral-100 opacity-40" : ""}
${isDragging ? "bg-blue-50 shadow-lg" : ""}
`}
>
@ -37,7 +37,9 @@ const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children
{...listeners}
className="cursor-grab text-gray-500 flex items-center pr-2 absolute top-0 left-0 z-50"
>
<MdDragIndicator className="absolute top-[10px] left-[10px] text-xl text-gray-300" />
{signingOrder && <MdDragIndicator className="absolute top-[59px] left-[70px] text-xl text-gray-300" />}
</div>}
{/* Your card content */}

View File

@ -311,7 +311,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
setShowActionsDropDown(false)
}}
className="cursor-pointer w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Save
Save Changes
</button>
<button
onClick={() => {
@ -319,7 +319,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
setShowActionsDropDown(false);
}}
className="cursor-pointer w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Save & Close
Save & Exit
</button>
<button
onClick={() => {

View File

@ -133,7 +133,7 @@ const TransactionView = () => {
<>
{transactionDetails &&
<>
<div className='mt-2 grid grid-cols-1 lg:grid-cols-12 items-stretch max-w-90vw min-h-[88vh] h-full bg-white rounded-lg shadow-sm mx-3'>
<div className='mt-2 grid grid-cols-1 lg:grid-cols-12 items-stretch max-w-[95vw] min-h-[88vh] h-full bg-white rounded-lg shadow-sm mx-3'>
<div className="lg:col-span-10 w-full px-6 py-4 rounded-tl-sm text-xs border-r-2 border-gray-200 overflow-y-auto">
{/* Header */}
<h1 className='text-lg font-semibold text-gray-800'>
@ -264,12 +264,12 @@ const TransactionView = () => {
<div className='lg:col-span-2 text-xs rounded-sm max-h-[88vh] overflow-y-auto no-scrollbar'>
{documentsData.map((docsD, index) => (
<div className='p-4 flex flex-col gap-2 cursor-pointer' key={docsD.id}>
<div className='p-4 flex flex-col gap-2 cursor-pointer items-center' key={docsD.id}>
<p>{docsD.name}</p>
<div className='border-3 rounded-sm border-slate-300 relative text-xs'>
<div className='border-3 rounded-sm border-slate-300 relative text-xs w-[160px] h-[168px]'>
{docsD?.thumbnail ? (
<img
style={{ width: "200px", height: "160px" }}
style={{ width: "160px", height: "160px" }}
src={`data:image/jpeg;base64,${docsD.thumbnail}`} // Adjust MIME type if needed
alt=""
loading="lazy"

View File

@ -0,0 +1,33 @@
import axiosInstance from "../config/axiosInstance";
const handleDownloadDocument = async (recipientId: string): Promise<void> => {
try {
const response = await axiosInstance.get(
`/api/v1/sign/${recipientId}/documents`,
{
responseType: "blob",
}
);
const blob = new Blob([response.data]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
// IMPORTANT
link.download = "document.zip";
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
} catch (err) {
console.error(err);
}
};
export default handleDownloadDocument;

View File

@ -268,11 +268,11 @@ const selectedRecipientsSlice = createSlice({
})
.addCase(fetchRecipients.rejected, (state, action) => {
console.error("Failed to fetch recipients:", action.payload);
toast.error("Failed to fetch recipients", {
position: "bottom-right",
autoClose: 5000,
theme: "light",
});
// toast.error("Failed to fetch recipients", {
// position: "bottom-right",
// autoClose: 5000,
// theme: "light",
// });
})
.addCase(addRecipientAsync.pending, () => { })
.addCase(addRecipientAsync.fulfilled, (state, action: PayloadAction<Recipient[]>) => {
@ -283,11 +283,11 @@ const selectedRecipientsSlice = createSlice({
})
.addCase(addRecipientAsync.rejected, (state, action) => {
console.error("Failed to add recipients:", action.payload);
toast.error("Failed to add recipients", {
position: "bottom-right",
autoClose: 5000,
theme: "light",
});
// toast.error("Failed to add recipients", {
// position: "bottom-right",
// autoClose: 5000,
// theme: "light",
// });
})
.addCase(removeRecipientAsync.pending, () => { })
.addCase(removeRecipientAsync.fulfilled, (state, action: PayloadAction<string | number>) => {
@ -297,11 +297,11 @@ const selectedRecipientsSlice = createSlice({
})
.addCase(removeRecipientAsync.rejected, (state, action) => {
console.error("Failed to delete recipient:", action.payload);
toast.error("Failed to delete recipient", {
position: "bottom-right",
autoClose: 5000,
theme: "light",
});
// toast.error("Failed to delete recipient", {
// position: "bottom-right",
// autoClose: 5000,
// theme: "light",
// });
});
}
});

View File

@ -16,6 +16,7 @@ export default defineConfig({
'c76f1251c173.ngrok-free.app',
'enabled-upright-boxer.ngrok-free.app',
'basically-polished-yeti.ngrok-free.app',
'grace-antral-shari.ngrok-free.dev',
'10.10.1.248:8083'
],
},