15-15-26
This commit is contained in:
parent
42b4c1dd5b
commit
7612f6a3d3
2
.env
2
.env
|
|
@ -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"
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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} />} />
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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={() => {
|
||||
|
|
|
|||
|
|
@ -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`}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */}
|
||||
|
|
|
|||
|
|
@ -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={() => {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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",
|
||||
// });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
],
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue