Tech4biz-channel/Channel-Frontend/src/components/ui/DocumentPreviewModal.tsx

811 lines
35 KiB
TypeScript

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { motion } from "framer-motion";
import {
X,
ZoomIn,
ZoomOut,
RotateCcw,
Download,
Printer,
ShieldCheck,
AlertTriangle,
CheckCircle,
ChevronLeft,
ChevronRight,
FileText,
Maximize,
Minimize,
} from "lucide-react";
import { Button } from "./Button";
interface Acceptance {
id: string;
signatureHash: string | null;
documentUrl: string | null;
signatureBase64: string | null;
ipAddress: string;
acceptedAt: string;
document: {
id: string;
type: string;
version: string;
content: string;
};
}
interface DocumentPreviewModalProps {
isOpen: boolean;
onClose: () => void;
partnerId: string;
partnerEmail: string;
partnerCreatedAt: string;
acceptances: Acceptance[];
verifiedDocs: { nda: boolean; msa: boolean };
onVerify: (docType: "NDA" | "MSA") => void;
onApprovePartner: () => void;
isApproving: boolean;
}
export const DocumentPreviewModal: React.FC<DocumentPreviewModalProps> = ({
isOpen,
onClose,
partnerId: _partnerId,
partnerEmail,
partnerCreatedAt: _partnerCreatedAt,
acceptances,
verifiedDocs,
onVerify,
onApprovePartner,
isApproving,
}) => {
const [currentTab, setCurrentTab] = useState<"NDA" | "MSA">("NDA");
const [zoom, setZoom] = useState(100);
const [fitWidth, setFitWidth] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [isFullScreen, setIsFullScreen] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const ndaAcceptance = acceptances.find((a) => a.document.type === "NDA");
const msaAcceptance = acceptances.find((a) => a.document.type === "MSA");
const activeAcceptance = currentTab === "NDA" ? ndaAcceptance : msaAcceptance;
const totalPages = activeAcceptance?.documentUrl ? 1 : 2;
// Prevent background page scrolling when modal is open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [isOpen]);
// Handle auto-opening on the signature page (Page 2) for digitally signed documents
useEffect(() => {
if (isOpen && activeAcceptance) {
if (!activeAcceptance.documentUrl && activeAcceptance.signatureHash) {
setCurrentPage(2);
} else {
setCurrentPage(1);
}
}
}, [currentTab, isOpen, activeAcceptance]);
// Full Screen logic
const toggleFullScreen = () => {
if (!modalRef.current) return;
if (!isFullScreen) {
if (modalRef.current.requestFullscreen) {
modalRef.current.requestFullscreen().catch(() => {
setIsFullScreen(true);
});
} else {
setIsFullScreen(true);
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen().catch(() => {});
}
setIsFullScreen(false);
}
};
useEffect(() => {
const handleFullscreenChange = () => {
setIsFullScreen(!!document.fullscreenElement);
};
document.addEventListener("fullscreenchange", handleFullscreenChange);
return () =>
document.removeEventListener("fullscreenchange", handleFullscreenChange);
}, []);
if (!isOpen) return null;
const handleZoomIn = () => {
setFitWidth(false);
setZoom((prev) => Math.min(200, prev + 25));
};
const handleZoomOut = () => {
setFitWidth(false);
setZoom((prev) => Math.max(50, prev - 25));
};
const handleZoomReset = () => {
setFitWidth(false);
setZoom(100);
};
const toggleFitWidth = () => {
setFitWidth(!fitWidth);
};
const handlePrint = () => {
const printContent = document.getElementById("printable-doc-content");
if (!printContent) return;
const windowUrl = "about:blank";
const uniqueName = new Date().getTime();
const printWindow = window.open(
windowUrl,
uniqueName.toString(),
"left=50000,top=50000,width=0,height=0",
);
if (!printWindow) return;
printWindow.document.write(`
<html>
<head>
<title>Print Document - ${currentTab}</title>
<style>
body { font-family: sans-serif; padding: 40px; color: #1b1b1b; }
h1 { font-size: 24px; font-weight: bold; margin-bottom: 20px; text-align: center; }
p { font-size: 14px; line-height: 1.6; white-space: pre-line; }
.sig-box { margin-top: 40px; padding: 20px; border: 2px dashed #ccc; text-align: center; max-width: 400px; margin-left: auto; margin-right: auto; }
.sig-title { font-weight: bold; color: #10b981; margin-bottom: 10px; }
.hash { font-family: monospace; font-size: 11px; word-break: break-all; }
</style>
</head>
<body>
<h1>${currentTab === "NDA" ? "Non-Disclosure Agreement (NDA)" : "Master Services Agreement (MSA)"}</h1>
<p>${activeAcceptance?.document.content || ""}</p>
<div class="sig-box">
<div class="sig-title">Digitally Signed & Verified</div>
<div>Signed By: ${partnerEmail}</div>
<div class="hash">Verification Hash: ${activeAcceptance?.signatureHash || "N/A"}</div>
<div>IP Address: ${activeAcceptance?.ipAddress || "N/A"}</div>
<div>Date Signed: ${activeAcceptance ? new Date(activeAcceptance.acceptedAt).toLocaleString() : ""}</div>
</div>
</body>
</html>
`);
printWindow.document.close();
printWindow.focus();
printWindow.print();
printWindow.close();
};
const handleDownloadText = () => {
if (!activeAcceptance) return;
const element = document.createElement("a");
const file = new Blob(
[
`${currentTab === "NDA" ? "Non-Disclosure Agreement (NDA)" : "Master Services Agreement (MSA)"}\n\n`,
activeAcceptance.document.content,
`\n\n=== DIGITAL SIGNATURE ===\n`,
`Signed By: ${partnerEmail}\n`,
`Verification Hash: ${activeAcceptance.signatureHash || "N/A"}\n`,
`IP Address: ${activeAcceptance.ipAddress}\n`,
`Signed On: ${new Date(activeAcceptance.acceptedAt).toLocaleString()}\n`,
],
{ type: "text/plain" },
);
element.href = URL.createObjectURL(file);
element.download = `${currentTab}_Agreement_${partnerEmail.split("@")[0]}.txt`;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const fileHost = (
import.meta.env.VITE_API_URL || "http://localhost:5000/api/v1"
).replace("/api/v1", "");
const isBothVerified = verifiedDocs.nda && verifiedDocs.msa;
const isCurrentVerified =
currentTab === "NDA" ? verifiedDocs.nda : verifiedDocs.msa;
// Responsive classes based on screen size
const modalContainerClasses = isFullScreen
? "fixed inset-0 w-screen h-screen bg-ink-0 z-[9999] flex flex-col overflow-hidden"
: "relative w-full h-full sm:w-[90vw] sm:h-[88vh] lg:w-[78vw] lg:h-[86vh] bg-ink-0 border border-ink-200 sm:rounded-2xl shadow-2xl flex flex-col overflow-hidden z-[9999]";
return ReactDOM.createPortal(
<div className="fixed inset-0 z-[9999] flex justify-center items-center p-0 sm:p-4 md:p-6 overflow-hidden">
{/* Full-screen Backdrop overlay */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-ink-950/60 backdrop-blur-md z-[9998]"
/>
{/* Modal Box */}
<motion.div
ref={modalRef}
initial={{ opacity: 0, scale: 0.97, y: 15 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.97, y: 15 }}
transition={{ type: "spring", damping: 26, stiffness: 340 }}
className={modalContainerClasses}
>
{/* Sticky Header */}
<div className="px-5 py-4 border-b border-ink-200 bg-ink-0 flex flex-col md:flex-row md:items-center justify-between gap-3 shrink-0">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-ink-900 to-ink-800 flex items-center justify-center text-ink-0 shrink-0">
<FileText className="w-5 h-5" />
</div>
<div className="min-w-0">
<h3 className="text-sm font-extrabold text-ink-900 tracking-tight truncate">
{currentTab === "NDA"
? "Mutual Non-Disclosure Agreement (NDA)"
: "Master Services Agreement (MSA)"}
</h3>
<p className="text-xs text-ink-500 font-semibold truncate">
Partner: <span className="text-ink-900">{partnerEmail}</span>
</p>
</div>
</div>
{/* Switcher tabs */}
<div className="flex items-center gap-2 bg-ink-50 p-1 rounded-xl border border-ink-200 self-start md:self-auto">
<button
onClick={() => {
setCurrentTab("NDA");
setCurrentPage(1);
}}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer ${
currentTab === "NDA"
? "bg-ink-0 text-ink-900 shadow-sm border border-ink-200"
: "text-ink-500 hover:text-ink-900"
}`}
>
NDA Agreement {verifiedDocs.nda && "✓"}
</button>
<button
onClick={() => {
setCurrentTab("MSA");
setCurrentPage(1);
}}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer ${
currentTab === "MSA"
? "bg-ink-0 text-ink-900 shadow-sm border border-ink-200"
: "text-ink-500 hover:text-ink-900"
}`}
>
MSA Agreement {verifiedDocs.msa && "✓"}
</button>
</div>
<div className="flex items-center gap-1.5 self-end md:self-auto">
<button
onClick={toggleFullScreen}
className="p-2 rounded-lg text-ink-500 hover:text-ink-900 hover:bg-ink-50 transition-all cursor-pointer"
title={isFullScreen ? "Exit Fullscreen" : "Fullscreen"}
>
{isFullScreen ? (
<Minimize className="w-4 h-4" />
) : (
<Maximize className="w-4 h-4" />
)}
</button>
<button
onClick={onClose}
className="p-2 rounded-lg text-ink-400 hover:text-ink-900 hover:bg-ink-50 transition-all cursor-pointer"
title="Close Modal"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
{/* Warning Banner if Unsigned */}
{!activeAcceptance && (
<div className="bg-amber-50 border-b border-amber-200 px-5 py-2.5 flex items-center gap-3 shrink-0">
<AlertTriangle className="w-4.5 h-4.5 text-amber-600 shrink-0" />
<p className="text-xs font-bold text-amber-800">
Attention: This document has not been submitted or signed by the
partner.
</p>
</div>
)}
{/* Professional Document Viewer Toolbar */}
<div className="px-5 py-2.5 bg-ink-50 border-b border-ink-200 flex flex-wrap items-center justify-between gap-3 shrink-0">
{/* Page navigation controls */}
<div className="flex items-center gap-1.5">
<button
disabled={currentPage <= 1 || !activeAcceptance}
onClick={() => setCurrentPage((prev) => Math.max(1, prev - 1))}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer shadow-sm"
title="Previous Page"
>
<ChevronLeft className="w-4 h-4" />
</button>
<span className="text-xs font-bold text-ink-600 min-w-[70px] text-center">
Page {activeAcceptance ? currentPage : 0} of{" "}
{activeAcceptance ? totalPages : 0}
</span>
<button
disabled={currentPage >= totalPages || !activeAcceptance}
onClick={() =>
setCurrentPage((prev) => Math.min(totalPages, prev + 1))
}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer shadow-sm"
title="Next Page"
>
<ChevronRight className="w-4 h-4" />
</button>
</div>
{/* Zoom controls */}
<div className="flex items-center gap-1.5">
<button
onClick={handleZoomOut}
disabled={
!activeAcceptance || activeAcceptance.documentUrl !== null
}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer shadow-sm"
title="Zoom Out"
>
<ZoomOut className="w-4 h-4" />
</button>
<span className="text-xs font-bold text-ink-600 min-w-[45px] text-center">
{zoom}%
</span>
<button
onClick={handleZoomIn}
disabled={
!activeAcceptance || activeAcceptance.documentUrl !== null
}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer shadow-sm"
title="Zoom In"
>
<ZoomIn className="w-4 h-4" />
</button>
<button
onClick={handleZoomReset}
disabled={
!activeAcceptance || activeAcceptance.documentUrl !== null
}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer shadow-sm"
title="Reset Zoom"
>
<RotateCcw className="w-4 h-4" />
</button>
<button
onClick={toggleFitWidth}
disabled={
!activeAcceptance || activeAcceptance.documentUrl !== null
}
className={`px-2.5 py-1.5 rounded-lg border text-xs font-bold cursor-pointer shadow-sm transition-all ${
fitWidth
? "bg-ink-900 text-ink-0 border-transparent"
: "bg-ink-0 border-ink-200 text-ink-700 hover:bg-ink-50"
}`}
title="Fit Width"
>
Fit Width
</button>
</div>
{/* Download & Print Actions */}
<div className="flex items-center gap-2">
<button
onClick={handlePrint}
disabled={!activeAcceptance}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer flex items-center gap-1.5 text-xs font-bold shadow-sm"
title="Print"
>
<Printer className="w-4 h-4" />
<span className="hidden sm:inline">Print</span>
</button>
<button
onClick={
activeAcceptance?.documentUrl ? undefined : handleDownloadText
}
disabled={!activeAcceptance}
className="p-1.5 rounded-lg border border-ink-200 hover:bg-ink-0 disabled:opacity-40 text-ink-700 bg-ink-0 cursor-pointer flex items-center gap-1.5 text-xs font-bold shadow-sm"
title="Download File"
>
{activeAcceptance?.documentUrl ? (
<a
href={`${fileHost}${activeAcceptance.documentUrl}`}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5"
>
<Download className="w-4 h-4" />
<span className="hidden sm:inline">Download</span>
</a>
) : (
<>
<Download className="w-4 h-4" />
<span className="hidden sm:inline">Download</span>
</>
)}
</button>
</div>
</div>
{/* Modal Main Body (Split Pane) */}
<div className="flex-1 flex flex-col md:flex-row overflow-hidden min-h-0">
{/* Left Pane: Centered Document Viewer */}
<div className="flex-1 flex flex-col bg-ink-100 overflow-hidden relative p-4 sm:p-6 justify-center items-center">
<div
ref={containerRef}
className="w-full h-full overflow-y-auto flex justify-center items-start scrollbar-thin rounded-lg"
>
{activeAcceptance ? (
activeAcceptance.documentUrl ? (
// PDF / Uploaded Document Viewer centered
<div className="w-full h-full max-w-4xl bg-ink-0 shadow-lg rounded-xl overflow-hidden border border-ink-200 flex justify-center items-center">
{activeAcceptance.documentUrl
.toLowerCase()
.endsWith(".pdf") ? (
<iframe
src={`${fileHost}${activeAcceptance.documentUrl}#toolbar=0`}
title="Document PDF"
className="w-full h-full border-0 bg-white"
/>
) : (
<div className="w-full h-full flex items-center justify-center p-4 bg-ink-50 overflow-auto">
<img
src={`${fileHost}${activeAcceptance.documentUrl}`}
alt="Signed legal document upload"
className="max-w-full max-h-full object-contain shadow-md rounded border border-ink-200"
/>
</div>
)}
</div>
) : (
// Digitally Signed text template (Standard A4 letter styled)
<div
id="printable-doc-content"
className="w-full bg-ink-0 p-8 sm:p-12 my-2 rounded-xl shadow-lg border border-ink-200 font-serif leading-relaxed text-ink-800 transition-all duration-150 select-text flex flex-col"
style={
fitWidth
? { width: "100%", maxWidth: "100%", fontSize: "15px" }
: {
width: "100%",
maxWidth: "720px",
fontSize: `${14 * (zoom / 100)}px`,
}
}
>
{currentPage === 1 ? (
// Page 1: Agreement text
<div className="flex-1">
<div className="text-center mb-8 not-italic font-sans">
<h4 className="text-base sm:text-lg font-extrabold text-ink-900 tracking-tight">
{currentTab === "NDA"
? "MUTUAL NON-DISCLOSURE AGREEMENT"
: "MASTER SERVICES AGREEMENT"}
</h4>
<div className="w-16 h-1 bg-ink-900 mx-auto my-3" />
<p className="text-[10px] text-ink-400 font-bold uppercase tracking-wider">
Version {activeAcceptance.document.version}
</p>
</div>
<div className="whitespace-pre-line prose max-w-none text-xs sm:text-sm">
{activeAcceptance.document.content}
</div>
<div className="mt-8 pt-4 border-t border-ink-150 text-center not-italic font-sans text-xs text-ink-400 font-semibold">
--- Page 1 of 2 (Standard Terms) ---
</div>
</div>
) : (
// Page 2: Actual signature rendering + metadata
<div className="flex-1 flex flex-col justify-between">
<div>
<div className="text-center mb-6 not-italic font-sans">
<h4 className="text-base sm:text-lg font-extrabold text-ink-900 tracking-tight">
SIGNATURE &amp; ACCORD SIGNING PAGE
</h4>
<p className="text-[10px] text-ink-400 font-bold uppercase tracking-wider mt-1">
Agreement Version{" "}
{activeAcceptance.document.version}
</p>
</div>
<p className="text-xs sm:text-sm mb-6 text-ink-600 font-sans italic">
IN WITNESS WHEREOF, the parties hereto have caused
this Agreement to be executed by their digital
signatures as of the Acceptance Date specified
below.
</p>
</div>
{/* ── Actual Signature Rendering ── */}
<div className="my-auto max-w-lg mx-auto w-full space-y-4">
{/* Signature image box */}
<div className="border-2 border-ink-200 rounded-xl overflow-hidden bg-white shadow-sm">
<div className="px-4 pt-3 pb-1 border-b border-ink-100 flex items-center justify-between">
<span className="text-[10px] font-bold text-ink-400 uppercase tracking-widest">
Client Signature
</span>
<div className="flex items-center gap-1 text-emerald-600">
<ShieldCheck className="w-3.5 h-3.5" />
<span className="text-[10px] font-bold uppercase tracking-wider">
Verified
</span>
</div>
</div>
<div className="p-4 min-h-[100px] flex items-center justify-center bg-white">
{activeAcceptance.signatureBase64 ? (
// Render the exact drawn signature as-is — no modification
<img
src={activeAcceptance.signatureBase64}
alt="Client drawn signature"
className="max-w-full max-h-[160px] object-contain"
style={{ imageRendering: "crisp-edges" }}
/>
) : (
// Upload-based signing — no drawn image stored
<div className="text-center py-4">
<ShieldCheck className="w-8 h-8 text-emerald-500 mx-auto mb-2" />
<p className="text-xs text-ink-500 font-semibold">
Signed via document upload
</p>
<p className="text-[10px] text-ink-400 mt-0.5">
See uploaded file in viewer
</p>
</div>
)}
</div>
{/* Signature baseline line */}
<div className="px-6 pb-3">
<div className="border-b-2 border-ink-300 w-full" />
<p className="text-[9px] text-ink-400 font-bold uppercase tracking-widest mt-1 text-center">
Authorized Digital Signature
</p>
</div>
</div>
{/* Verification metadata strip */}
<div className="grid grid-cols-2 gap-x-4 gap-y-2 text-[10px] sm:text-xs font-sans bg-ink-50 border border-ink-200 rounded-xl p-4">
<div>
<p className="font-bold text-ink-400 uppercase tracking-wider text-[9px]">
Signed By
</p>
<p className="font-bold text-ink-900 mt-0.5 truncate">
{partnerEmail}
</p>
</div>
<div>
<p className="font-bold text-ink-400 uppercase tracking-wider text-[9px]">
Accepted On
</p>
<p className="font-semibold text-ink-900 mt-0.5">
{new Date(
activeAcceptance.acceptedAt,
).toLocaleString()}
</p>
</div>
<div>
<p className="font-bold text-ink-400 uppercase tracking-wider text-[9px]">
IP Address
</p>
<p className="font-semibold font-mono text-ink-900 mt-0.5">
{activeAcceptance.ipAddress}
</p>
</div>
<div>
<p className="font-bold text-ink-400 uppercase tracking-wider text-[9px]">
Document Version
</p>
<p className="font-semibold text-ink-900 mt-0.5">
v{activeAcceptance.document.version}
</p>
</div>
{activeAcceptance.signatureHash && (
<div className="col-span-2">
<p className="font-bold text-ink-400 uppercase tracking-wider text-[9px]">
Verification Hash (SHA-256)
</p>
<p className="font-mono text-[9px] text-ink-500 bg-ink-100 p-1.5 rounded border border-ink-200 break-all mt-0.5">
{activeAcceptance.signatureHash}
</p>
</div>
)}
</div>
</div>
<div className="mt-8 pt-4 border-t border-ink-150 text-center not-italic font-sans text-xs text-ink-400 font-semibold">
--- Page 2 of 2 (Signature &amp; Seals) ---
</div>
</div>
)}
</div>
)
) : (
<div className="w-full max-w-md my-auto flex flex-col items-center justify-center text-center p-8 bg-ink-0 rounded-2xl border border-ink-200 shadow-lg">
<AlertTriangle className="w-12 h-12 text-ink-400 mb-3" />
<h4 className="text-base font-extrabold text-ink-900">
Document Unavailable
</h4>
<p className="text-xs text-ink-500 mt-1.5 max-w-xs leading-relaxed font-semibold">
The partner has not yet submitted or signed the {currentTab}{" "}
document.
</p>
</div>
)}
</div>
</div>
{/* Right Pane: Document Details & Metadata */}
<div className="w-full md:w-80 border-t md:border-t-0 md:border-l border-ink-200 bg-ink-0 flex flex-col overflow-y-auto shrink-0 p-5 space-y-5">
<div>
<h4 className="text-xs font-bold uppercase tracking-wider text-ink-400 mb-3">
Onboarding Details
</h4>
<div className="space-y-3">
<div className="flex justify-between items-center text-xs">
<span className="font-semibold text-ink-500">
Document Category
</span>
<span className="font-bold text-ink-900 bg-ink-100 px-2 py-0.5 rounded border border-ink-200">
{currentTab}
</span>
</div>
<div className="flex justify-between items-center text-xs">
<span className="font-semibold text-ink-500">
Submission State
</span>
{activeAcceptance ? (
<span className="font-bold text-emerald-700 bg-emerald-500/10 px-2 py-0.5 rounded border border-emerald-500/20">
Signed
</span>
) : (
<span className="font-bold text-amber-700 bg-amber-500/10 px-2 py-0.5 rounded border border-amber-500/20">
Pending
</span>
)}
</div>
<div className="flex justify-between items-center text-xs">
<span className="font-semibold text-ink-500">
Digital Signature
</span>
{activeAcceptance ? (
<span className="font-bold text-emerald-700 bg-emerald-500/10 px-2 py-0.5 rounded border border-emerald-500/20">
Verified
</span>
) : (
<span className="font-bold text-red-650 bg-red-500/10 px-2 py-0.5 rounded border border-red-500/20">
Not Verified
</span>
)}
</div>
</div>
</div>
<div className="border-t border-ink-100 pt-4">
<h4 className="text-xs font-bold uppercase tracking-wider text-ink-400 mb-3">
Legal Verification
</h4>
{activeAcceptance ? (
<div className="space-y-3 text-xs">
<div>
<p className="font-semibold text-ink-400 text-[10px] uppercase">
Signed By
</p>
<p className="font-bold text-ink-900 truncate mt-0.5">
{partnerEmail}
</p>
</div>
<div>
<p className="font-semibold text-ink-400 text-[10px] uppercase">
Signed On
</p>
<p className="font-bold text-ink-900 mt-0.5">
{new Date(activeAcceptance.acceptedAt).toLocaleString()}
</p>
</div>
<div>
<p className="font-semibold text-ink-400 text-[10px] uppercase">
IP Address
</p>
<p className="font-bold text-ink-900 mt-0.5 font-mono">
{activeAcceptance.ipAddress}
</p>
</div>
<div>
<p className="font-semibold text-ink-400 text-[10px] uppercase">
Version
</p>
<p className="font-bold text-ink-900 mt-0.5">
v{activeAcceptance.document.version}
</p>
</div>
{activeAcceptance.signatureHash && (
<div>
<p className="font-semibold text-ink-400 text-[10px] uppercase">
Verification Hash
</p>
<p className="font-mono text-[10px] text-ink-650 bg-ink-50 p-1.5 rounded border border-ink-150 break-all mt-0.5">
{activeAcceptance.signatureHash}
</p>
</div>
)}
</div>
) : (
<p className="text-xs text-ink-400 italic">
No verification metadata available.
</p>
)}
</div>
<div className="mt-auto pt-4 border-t border-ink-100 space-y-2">
<Button
onClick={() => onVerify(currentTab)}
disabled={!activeAcceptance}
variant={isCurrentVerified ? "secondary" : "primary"}
className="w-full flex justify-center items-center gap-1.5"
>
{isCurrentVerified ? (
<>
<div className="flex gap-2 items-center">
<CheckCircle className="w-4 h-4 text-emerald-600" />
<span>Document Verified</span>
</div>
</>
) : (
<span>Verify {currentTab} Signature</span>
)}
</Button>
<p className="text-[10px] text-ink-400 text-center font-medium leading-normal">
Marking this verified unlocks approval options for the
administrator.
</p>
</div>
</div>
</div>
{/* Sticky Footer */}
<div className="px-6 py-4 border-t border-ink-200 bg-ink-50 flex flex-col sm:flex-row justify-between items-center gap-4 shrink-0">
<div className="text-xs font-semibold text-ink-500">
{verifiedDocs.nda && verifiedDocs.msa ? (
<span className="text-emerald-700 font-bold flex items-center gap-1.5">
<CheckCircle className="w-4.5 h-4.5" /> Both agreements
verified. Access approval unlocked.
</span>
) : (
<span className="flex items-center gap-1.5">
<AlertTriangle className="w-4 h-4 text-amber-500" /> Please
review and verify both the NDA and MSA documents.
</span>
)}
</div>
<div className="flex items-center gap-2 w-full sm:w-auto justify-end">
<Button variant="secondary" onClick={onClose}>
Cancel
</Button>
<Button
onClick={onApprovePartner}
disabled={!isBothVerified || isApproving}
variant="primary"
className="font-bold shadow-md hover:shadow-lg transition-shadow"
>
{isApproving ? "Approving Partner..." : "Approve Partner Access"}
</Button>
</div>
</div>
</motion.div>
</div>,
document.body,
);
};