EZOFISOCR / frontend /src /components /ShareLinkModal.jsx
Seth
update
89a3828
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { X, Copy, Check, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
export default function ShareLinkModal({ isOpen, onClose, shareLink, isLoading }) {
const [copied, setCopied] = useState(false);
useEffect(() => {
if (!isOpen) {
setCopied(false);
}
}, [isOpen]);
const handleCopy = async () => {
if (!shareLink) return;
try {
await navigator.clipboard.writeText(shareLink);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = shareLink;
textArea.style.position = "fixed";
textArea.style.opacity = "0";
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (fallbackErr) {
console.error("Failed to copy:", fallbackErr);
}
document.body.removeChild(textArea);
}
};
if (!isOpen) return null;
return (
<AnimatePresence>
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
onClick={onClose}
/>
{/* Modal */}
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
className="relative z-10 w-full max-w-md mx-4 bg-white rounded-2xl shadow-2xl overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="px-6 py-4 border-b border-slate-200 flex items-center justify-between">
<h2 className="text-xl font-semibold text-slate-900">Copy Share Link</h2>
<button
onClick={onClose}
disabled={isLoading}
className="p-2 rounded-lg hover:bg-slate-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<X className="h-5 w-5 text-slate-500" />
</button>
</div>
{/* Content */}
<div className="px-6 py-6">
{isLoading ? (
<div className="text-center py-8">
<Loader2 className="h-8 w-8 mx-auto mb-4 text-indigo-600 animate-spin" />
<p className="text-sm text-slate-600">Generating share link...</p>
</div>
) : shareLink ? (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-2">
Share Link
</label>
<div className="flex gap-2">
<Input
type="text"
value={shareLink}
readOnly
className="flex-1 h-12 rounded-xl border-slate-200 bg-slate-50 text-sm font-mono"
/>
<Button
onClick={handleCopy}
className="h-12 px-4 rounded-xl bg-gradient-to-r from-indigo-600 to-violet-600 hover:from-indigo-700 hover:to-violet-700"
>
{copied ? (
<>
<Check className="h-4 w-4 mr-2" />
Copied!
</>
) : (
<>
<Copy className="h-4 w-4 mr-2" />
Copy
</>
)}
</Button>
</div>
</div>
<p className="text-xs text-slate-500">
Share this link with anyone you want to give access to this extraction. They'll need to sign in to view it.
</p>
</div>
) : (
<div className="text-center py-8">
<p className="text-sm text-slate-600">No share link available</p>
</div>
)}
<div className="pt-4 mt-6 border-t border-slate-200">
<Button
type="button"
variant="outline"
onClick={onClose}
disabled={isLoading}
className="w-full h-11 rounded-xl"
>
Close
</Button>
</div>
</div>
</motion.div>
</div>
</AnimatePresence>
);
}