completion-screen new v1

This commit is contained in:
JaiPandey425 2025-08-08 11:13:16 +05:30
parent 002c8e6110
commit cd2b8d3aff
2 changed files with 249 additions and 132 deletions

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { FaCheck, FaLock, FaClock, FaEnvelope, FaTimes } from "react-icons/fa"; import { FaCheck, FaLock, FaClock, FaEnvelope, FaTimes, FaUserTimes } from "react-icons/fa";
import { IoCloseOutline } from "react-icons/io5"; import { IoCloseOutline } from "react-icons/io5";
type Signer = { type Signer = {
@ -48,8 +48,10 @@ const groupByRoutingOrder = (data: Signer[]) => {
}; };
const getInitials = (name: string) => { const getInitials = (name: string) => {
if (!name.trim()) return "";
return name return name
.split(" ") .split(" ")
.filter(n => n)
.map((n) => n[0].toUpperCase()) .map((n) => n[0].toUpperCase())
.join(""); .join("");
}; };
@ -63,6 +65,8 @@ const SigningOrderDiagram : React.FC<Signer[]> = (signers) => {
.map(Number) .map(Number)
.sort((a, b) => a - b); .sort((a, b) => a - b);
console.log("sortedOrders",sortedOrders,grouped)
const currentProcessingOrder = const currentProcessingOrder =
sortedOrders.find((order) => sortedOrders.find((order) =>
grouped[order].some((signer) => !signer.completed) grouped[order].some((signer) => !signer.completed)
@ -109,14 +113,14 @@ const SigningOrderDiagram : React.FC<Signer[]> = (signers) => {
<div key={signer.id} className="relative"> <div key={signer.id} className="relative">
<div <div
className={`w-10 h-10 rounded-full flex items-center justify-center text-white text-sm font-semibold ${bgColor}`} className={`w-10 h-10 rounded-full flex items-center justify-center text-white text-sm font-semibold ${bgColor}`}
title={signer.name} title={signer?.name}
> >
{getInitials(signer.name)} {(signer?.name !== '' || signer?.roleName) ? getInitials(signer?.name || signer?.roleName) : ""}
<span className="absolute bottom-[12px] right-[-12px] p-1 rounded-full bg-white shadow"> <span className="absolute bottom-[12px] right-[-12px] p-1 rounded-full bg-white shadow">
{icon} {icon}
</span> </span>
</div> </div>
<p className="mt-1 text-xs text-gray-600 text-center">{signer.name.split(" ")[0]}</p> <p className="mt-1 text-xs text-gray-600 text-center">{(signer?.name?.split(" ")[0] || signer?.roleName)}</p>
</div> </div>
); );
})} })}

View File

@ -1,17 +1,29 @@
import React, { useEffect, useState } from "react"; import React, { useState, useEffect } from 'react';
import { import {
FaArrowLeft,
FaCheck, FaCheck,
FaFileAlt, FaExternalLinkAlt,
FaEye,
FaEyeSlash,
FaRedo,
FaImage,
FaUsers, FaUsers,
FaFileAlt,
FaEnvelope, FaEnvelope,
FaClock, FaCog,
FaShieldAlt,
FaBell,
FaDownload, FaDownload,
FaShare, FaShare,
FaShieldAlt, FaEdit,
FaLock, FaSave,
FaPen, FaClock,
FaCheckCircle FaLock
} from 'react-icons/fa'; } from 'react-icons/fa';
import { RootState } from '../../../store/store';
import api from '../../../config/api';
import { fetchRecipients } from "../../../store/slices/selectedRecipientsSlice"; import { fetchRecipients } from "../../../store/slices/selectedRecipientsSlice";
import { fetchDocuments } from "../../../store/slices/documentSlice"; import { fetchDocuments } from "../../../store/slices/documentSlice";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
@ -21,7 +33,7 @@ import confetti from 'canvas-confetti'
import { IoClose } from "react-icons/io5"; import { IoClose } from "react-icons/io5";
import { IoMdClose, IoMdHome } from "react-icons/io"; import { IoMdClose, IoMdHome } from "react-icons/io";
import { Bounce, toast } from "react-toastify"; import { Bounce, toast } from "react-toastify";
import api from "../../../config/api";
type Signer = { type Signer = {
id: number; id: number;
@ -34,7 +46,7 @@ type Signer = {
// Signers List Component // Signers List Component
const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => { const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => {
return ( return (
<div className="bg-white/60 rounded-md shadow-sm border border-gray-100"> <div className="bg-white/60 rounded-md shadow-sm border border-gray-100 ">
<div className="p-6"> <div className="p-6">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-800 flex items-center gap-2"> <h3 className="text-lg font-semibold text-gray-800 flex items-center gap-2">
@ -112,7 +124,7 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => {
<div className="bg-white/60 rounded-md shadow-sm border border-gray-100"> <div className="bg-white/60 rounded-md shadow-sm border border-gray-100">
<div className="p-6"> <div className="p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">Signing Flow</h3> <h3 className="text-lg font-semibold text-gray-800 mb-4">Signing Flow</h3>
<div className="overflow-x-auto overflow-y-auto h-[69vh] no-scrollbar flex justify-center items-center"> <div className="overflow-x-auto overflow-y-auto h-[22vh] no-scrollbar flex justify-center items-center">
<div className="flex justify-center items-center gap-4 min-w-max"> <div className="flex justify-center items-center gap-4 min-w-max">
{/* Mail icon */} {/* Mail icon */}
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
@ -163,7 +175,7 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => {
</span> </span>
</div> </div>
<p className="mt-1 text-xs text-gray-600 text-center max-w-16 truncate"> <p className="mt-1 text-xs text-gray-600 text-center max-w-16 truncate">
{signer.name.split(" ")[0]} {signer.name.split(" ")[0] || signer.firstName.split(0,1)}
</p> </p>
</div> </div>
); );
@ -185,22 +197,7 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => {
</div> </div>
); );
}; };
const DocumentSubmissionOptions = () => {
// Main DocumentCompletionScreen Component
const DocumentCompletionScreen = () => {
const documentList = [
{ title: 'Document 1', pages: 12 },
{ title: 'Document 2', pages: 8 },
{ title: 'Document 3', pages: 5 },
{ title: 'Document 4', pages: 22 },
{ title: 'Document 5', pages: 10 },
{ title: 'Document 6', pages: 15 },
{ title: 'Document 7', pages: 7 },
{ title: 'Document 8', pages: 9 },
{ title: 'Document 9', pages: 6 },
{ title: 'Document 10', pages: 11 },
{ title: 'Document 11', pages: 4 }, // Won't be shown due to slice
];
const location = useLocation(); const location = useLocation();
const [status, setStatus] = useState(null); const [status, setStatus] = useState(null);
console.log("location.pathname", location.pathname, status) console.log("location.pathname", location.pathname, status)
@ -218,6 +215,8 @@ const DocumentCompletionScreen = () => {
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients); const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents) const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
console.log('Line 218', selectedDocuments)
const [transactionData,setTransactionData] = useState({})
useEffect(() => { useEffect(() => {
@ -277,7 +276,10 @@ const DocumentCompletionScreen = () => {
// /api/accounts/{accountId}/transactions/{trxId} // /api/accounts/{accountId}/transactions/{trxId}
try { try {
const res = await api.get(`/api/accounts/${accountId}/transactions/${transactionId}`); const res = await api.get(`/api/accounts/${accountId}/transactions/${transactionId}`);
if (res?.data) {
setStatus(res?.data?.status); setStatus(res?.data?.status);
setTransactionData(res?.data)
}
} catch (error) { } catch (error) {
console.error("Failed to fetch transaction:", error); console.error("Failed to fetch transaction:", error);
} }
@ -318,114 +320,225 @@ const DocumentCompletionScreen = () => {
}, [status]); }, [status]);
// useEffect(() => { // useEffect(() => {
// if (status === 'SENT') { // if (status === 'SENT') {
// // Step 1: Push a dummy state to trap the user // // Step 1: Push a dummy state to trap the user
// window.history.pushState(null, '', window.location.href); // window.history.pushState(null, '', window.location.href);
// // Step 2: Intercept back button // // Step 2: Intercept back button
// const handlePopState = (event) => { // const handlePopState = (event) => {
// // Push the same dummy state again to block back // // Push the same dummy state again to block back
// window.history.pushState(null, '', window.location.href); // window.history.pushState(null, '', window.location.href);
// }; // };
// window.addEventListener('popstate', handlePopState); // window.addEventListener('popstate', handlePopState);
// return () => {
// window.removeEventListener('popstate', handlePopState);
// };
// }
// }, [status]);
// return () => {
// window.removeEventListener('popstate', handlePopState);
// };
// }
// }, [status]);
const [selectedOption, setSelectedOption] = useState('success');
const [isVisible, setIsVisible] = useState(false);
const [previewData, setPreviewData] = useState({
documentName: "Contract Agreement.pdf",
submissionCount: 1,
signerName: "Sumit Kumar",
signerEmail: "sumit.kumar@hicglobalsolutions.com",
submissionTime: new Date().toLocaleString()
});
useEffect(() => {
setIsVisible(true);
}, []);
const options = [
{
id: 'success',
icon: FaCheck,
title: 'Success screen',
description: 'Display confirmation message after successful document submission',
color: 'bg-green-50 border-green-200 hover:bg-green-100',
iconColor: 'text-green-600',
isPremium: false
},
{
id: 'redirect',
icon: FaExternalLinkAlt,
title: 'Signing Flow',
description: 'Navigate user to view their transaction details',
color: 'bg-blue-50 border-blue-200 hover:bg-blue-100',
iconColor: 'text-blue-600',
isPremium: true
},
{
id: 'public',
icon: FaEye,
title: 'Signature status',
description: 'Show transaction status on public dashboard',
color: 'bg-purple-50 border-purple-200 hover:bg-purple-100',
iconColor: 'text-purple-600',
isPremium: true
},
];
const getPreviewContent = () => {
switch (selectedOption) {
case 'success':
return (
<div className="text-center space-y-6">
<div className="w-24 h-24 bg-[var(--primary)] rounded-full flex items-center justify-center mx-auto shadow-lg animate-bounce">
<FaCheck className="text-white text-3xl" />
</div>
<div>
<h3 className="text-2xl font-bold text-gray-800 mb-2">Document Send Successfully!</h3>
{selectedDocuments?.map((docs) => (
<p className="text-gray-600">Your document "{docs.name}" has been sent for signature.</p>
) )}
</div>
<div className="bg-gray-50 rounded-lg p-4 space-y-2">
<div className="flex justify-between text-sm">
<span className="text-gray-500">createdBy:</span>
<span className="font-medium">{transactionData?.createdBy?.name}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-500">Email:</span>
<span className="font-medium">{transactionData?.createdBy?.email}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-500">createdAt:</span>
<span className="font-medium">{transactionData.createdAt}</span>
</div>
</div>
</div>
);
case 'redirect':
return ( return (
<> <>
<div className="min-h-screen bg-[var(--mainViewBg)]">
{/* Top Banner */} <SigningOrderDiagram signers={selectedRecipients} />
<div className='fixed top-0 w-full flex items-center justify-between bg-white shadow z-50 px-4 py-3'> </>
<div className='flex items-center gap-2'> );
<Link to={"/"}> case 'public':
return (
<>
<SignersList signers={selectedRecipients} />
</>
);
default:
return null;
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-blue-50 p-6">
<div className={`max-w-6xl mx-auto transition-all duration-700 ${isVisible ? 'translate-y-0 opacity-100' : 'translate-y-8 opacity-0'}`}>
{/* Header */}
<div className="flex items-center space-x-4 mb-5">
<button className="flex items-center space-x-2 text-gray-600 hover:text-[var(--primary)] transition-colors duration-200 group">
<IoMdHome <IoMdHome
className='text-2xl text-slate-500 cursor-pointer hover:text-[var(--primary)] transition' className='text-2xl text-slate-500 cursor-pointer hover:text-[var(--primary)] transition'
/> />
</Link> </button>
<p className="text-base font-normal text-gray-700 border-l pl-4">🎉 Awesome! The document’s been sent for signature. 🎉</p> <div className="h-8 w-px bg-gray-300"></div>
<h1 className="text-2xl font-bold text-gray-800">🎉 Awesome! The document’s been sent for signature. 🎉</h1>
</div> </div>
<div {/* <div className='border mb-2 border-gray-500'></div> */}
id="add-tags-btn-step-one"
className='flex items-center gap-3'>
<Link to={`/accounts/${accountId}/transactions/${transactionId}`} className="flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-3 ">
{/* <FaPen className=" text-white text-md" /> */} {/* Left Panel - Configuration */}
<span className="tracking-wide text-white">View Transaction</span> <div className="space-y-6 ">
</Link>
{/* <button {/* Action Selection */}
onClick={() => handleNextClick(false)} <div className="bg-white rounded-xl shadow-sm border border-gray-200 min-h-[81vh] ">
className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition cursor-pointer" <div className="p-6">
> <h3 className="text-lg font-semibold text-gray-800 mb-4">
{signType === SignTypeEnum.selfSign ? "Sign" : "Next"} Choose an action after a signer completes the document.
</button> */}
</div>
</div>
{(selectedRecipients.length > 0 && selectedDocuments.length > 0) ?
<div className="p-3 mt-[55px]">
<div className="max-w-[97vw] mx-auto">
{/* Header */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-2">
{/* Left Section - Document Details */}
<div className="lg:col-span-6 space-y-2">
{/* Document Preview Card */}
<div className="bg-white/60 rounded-md shadow-sm border border-gray-100 overflow-hidden max-h-[88vh] no-scrollbar overflow-y-auto">
<div className="p-3 ">
{selectedDocuments?.map((item, index) => (
<div key={item.id} className="flex items-start justify-between mb-2 px-2 py-3">
<div className="flex items-center gap-4">
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
{/* <FaFileAlt className="text-blue-600 text-xl" /> */}
<img src={item.pages[0].src} alt="document" className="h-10 w-10" />
</div>
<div>
<h3 className="text-md font-semibold text-gray-800">
Name : {item.name ?? '--'}
</h3> </h3>
<div className="text-gray-600 text-sm">
Status : {item.status ?? '--'}
</div>
<div className="space-y-3">
{options.map((option) => {
const Icon = option.icon;
const isSelected = selectedOption === option.id;
return (
<div
key={option.id}
onClick={() => setSelectedOption(option.id)}
className={`
relative cursor-pointer p-4 rounded-xl border-2 transition-all duration-200 group
${isSelected
? 'border-[var(--primary)] bg-blue-50 shadow-md scale-[1.02]'
: option.color + ' border-2'
}
hover:shadow-md hover:scale-[1.01] transform
`}
>
<div className="flex items-start space-x-4">
<div className={`
w-12 h-12 rounded-full flex items-center justify-center transition-all duration-200
${isSelected
? 'bg-[var(--primary)] text-white shadow-lg'
: 'bg-white ' + option.iconColor + ' group-hover:scale-110'
}
`}>
<Icon className="text-lg" />
</div> </div>
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-2">
<h4 className={`font-semibold transition-colors duration-200 ${isSelected ? 'text-[var(--primary)]' : 'text-gray-800'
}`}>
{option.title}
</h4>
{option.isPremium && (
<span className="text-purple-500 text-sm">★</span>
)}
</div> </div>
</div> <p className="text-sm text-gray-600 mt-1">
))} {option.description}
</p>
</div> </div>
</div> </div>
<div><SignersList signers={selectedRecipients} /></div> {/* Selection indicator */}
{isSelected && (
<div className="absolute top-4 right-4">
<div className="w-6 h-6 bg-[var(--primary)] rounded-full flex items-center justify-center">
<FaCheck className="text-white text-xs" />
</div>
</div>
)}
</div>
);
})}
</div>
</div>
</div> </div>
{/* Right Section - Signer Components */} {/* Custom Image Section */}
<div className="lg:col-span-6 space-y-1.5"> </div>
{/* Signing Order Diagram */}
<SigningOrderDiagram signers={selectedRecipients} />
{/* Signers List */} {/* Right Panel - Preview */}
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden sticky top-6">
<div className="bg-gradient-to-r from-gray-50 to-blue-50 px-6 py-2 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-800">Live Preview</h3>
<p className="text-sm text-gray-500">See how your users will experience the completion</p>
</div>
<div className="p-3 min-h-[70vh] flex justify-center">
<div className="w-full transition-all duration-500 transform">
{getPreviewContent()}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
: <Loader />}
</div> </div>
</>
); );
}; };
export default DocumentCompletionScreen; export default DocumentSubmissionOptions;