import { useCallback, useEffect, useState } from 'react'; import { Trans, useLingui } from '@lingui/react/macro'; import { SigningStatus } from '@prisma/client'; import type { Field, Recipient } from '@prisma/client'; import { ClockIcon, EyeOffIcon } from 'lucide-react'; import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect'; import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; import { isTemplateRecipientEmailPlaceholder } from '../../../lib/constants/template'; import { extractInitials } from '../../../lib/utils/recipient-formatter'; import { SignatureIcon } from '../../icons/signature'; import { cn } from '../../lib/utils'; import { Avatar, AvatarFallback } from '../../primitives/avatar'; import { Badge } from '../../primitives/badge'; import { FRIENDLY_FIELD_TYPE } from '../../primitives/document-flow/types'; import { PopoverHover } from '../../primitives/popover'; interface EnvelopeRecipientFieldTooltipProps { field: Pick< Field, 'id' | 'inserted' | 'positionX' | 'positionY' | 'width' | 'height' | 'page' | 'type' > & { recipient: Pick; }; showFieldStatus?: boolean; showRecipientTooltip?: boolean; showRecipientColors?: boolean; } const getRecipientDisplayText = (recipient: { name: string; email: string }) => { if (recipient.name && !isTemplateRecipientEmailPlaceholder(recipient.email)) { return `${recipient.name} (${recipient.email})`; } if (recipient.name && isTemplateRecipientEmailPlaceholder(recipient.email)) { return recipient.name; } return recipient.email; }; /** * Renders a tooltip for a given field. */ export function EnvelopeRecipientFieldTooltip({ field, showFieldStatus = false, showRecipientTooltip = false, showRecipientColors = false, }: EnvelopeRecipientFieldTooltipProps) { const { t } = useLingui(); const [hideField, setHideField] = useState(!showRecipientTooltip); const [coords, setCoords] = useState({ x: 0, y: 0, height: 0, width: 0, }); const calculateCoords = useCallback(() => { const $page = document.querySelector( `${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`, ); if (!$page) { return; } const { height, width } = getBoundingClientRect($page); const fieldHeight = (Number(field.height) / 100) * height; const fieldWidth = (Number(field.width) / 100) * width; const fieldX = (Number(field.positionX) / 100) * width + Number(fieldWidth); const fieldY = (Number(field.positionY) / 100) * height; setCoords({ x: fieldX, y: fieldY, height: fieldHeight, width: fieldWidth, }); }, [field.height, field.page, field.positionX, field.positionY, field.width]); useEffect(() => { calculateCoords(); }, [calculateCoords]); useEffect(() => { const onResize = () => { calculateCoords(); }; window.addEventListener('resize', onResize); return () => { window.removeEventListener('resize', onResize); }; }, [calculateCoords]); useEffect(() => { const $page = document.querySelector( `${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`, ); if (!$page) { return; } const observer = new ResizeObserver(() => { calculateCoords(); }); observer.observe($page); return () => { observer.disconnect(); }; }, [calculateCoords, field.page]); if (hideField) { return null; } return (
{extractInitials(field.recipient.name || field.recipient.email)} } contentProps={{ className: 'relative flex mb-4 w-fit flex-col p-4 text-sm', }} > {showFieldStatus && ( {field.recipient.signingStatus === SigningStatus.SIGNED ? ( <> Signed ) : ( <> Pending )} )}

{t(FRIENDLY_FIELD_TYPE[field.type])} field

{getRecipientDisplayText(field.recipient)}

); }