import { useEffect, useState } from "react"; import { useCreateShareLink, useRevokeShareLink, useShareLinks, } from "../hooks/useShares"; import { LucideIcon } from "../lib/iconData"; interface ShareModalProps { isOpen: boolean; onClose: () => void; setupId: number; currentVisibility: "private" | "link" | "public"; onVisibilityChange: (visibility: "private" | "link" | "public") => void; } const VISIBILITY_OPTIONS = [ { value: "private" as const, icon: "lock", label: "Private", description: "Only you can access", color: "gray", border: "border-gray-200", selectedBorder: "border-gray-300 bg-gray-50", iconColor: "text-gray-500", }, { value: "link" as const, icon: "link", label: "Link sharing", description: "Anyone with the link", color: "blue", border: "border-gray-200", selectedBorder: "border-blue-200 bg-blue-50", iconColor: "text-blue-600", }, { value: "public" as const, icon: "globe", label: "Public", description: "Visible on your profile", color: "green", border: "border-gray-200", selectedBorder: "border-green-200 bg-green-50", iconColor: "text-green-700", }, ] as const; const EXPIRATION_OPTIONS = [ { value: 7, label: "7 days" }, { value: 14, label: "14 days" }, { value: 30, label: "30 days" }, { value: null, label: "No expiration" }, ] as const; export function ShareModal({ isOpen, onClose, setupId, currentVisibility, onVisibilityChange, }: ShareModalProps) { const { data: shareLinks } = useShareLinks(isOpen ? setupId : null); const createShareLink = useCreateShareLink(setupId); const revokeShareLink = useRevokeShareLink(setupId); const [expiresInDays, setExpiresInDays] = useState(14); const [copiedId, setCopiedId] = useState(null); const [justCreatedToken, setJustCreatedToken] = useState(null); // Handle Escape key useEffect(() => { if (!isOpen) return; function handleKeyDown(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [isOpen, onClose]); if (!isOpen) return null; const activeLinks = shareLinks?.filter((link) => !link.revokedAt) ?? []; const showLinksSection = true; const switchingToPrivateWithLinks = currentVisibility !== "private" && activeLinks.length > 0; function handleCreateLink() { createShareLink.mutate( { expiresInDays }, { onSuccess: (share) => { const url = `${window.location.origin}/s/${share.token}`; navigator.clipboard.writeText(url).catch(() => {}); setJustCreatedToken(share.token); setTimeout(() => setJustCreatedToken(null), 2000); }, }, ); } function handleCopy(token: string, shareId: number) { const url = `${window.location.origin}/s/${token}`; navigator.clipboard.writeText(url).catch(() => {}); setCopiedId(shareId); setTimeout(() => setCopiedId(null), 2000); } function handleVisibilityChange( newVisibility: "private" | "link" | "public", ) { if (newVisibility !== currentVisibility) { onVisibilityChange(newVisibility); } } function formatExpiration(expiresAt: string | null) { if (!expiresAt) return "No expiration"; const date = new Date(expiresAt); const now = new Date(); if (date < now) return "Expired"; const days = Math.ceil( (date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24), ); if (days === 0) return "Expires today"; if (days === 1) return "Expires tomorrow"; return `Expires in ${days} days`; } return (
{/* Overlay */}
{}} /> {/* Modal */}
{/* Header */}

Share Setup

{/* Visibility Picker */}
{VISIBILITY_OPTIONS.map((option) => ( ))}
{/* Deactivation Warning */} {currentVisibility === "private" && switchingToPrivateWithLinks && (

Switching to private will deactivate all share links. They can be reactivated by switching back.

)} {/* Share Links Section */} {showLinksSection && (
Share Links
{/* Create Link Row */}
{/* Active Links List */} {activeLinks.length > 0 ? (
{activeLinks.map((link) => (
/s/{link.token.slice(0, 12)}...
{formatExpiration(link.expiresAt)}
))}
) : (

No share links yet

)}
)}
); }