mirror of
https://github.com/documenso/documenso.git
synced 2026-09-29 16:24:30 +10:00
Hides the reject dialog on the V2 signing page sidebar and mobile menu, the embed V1 signing page and the multi-sign embed view when the document's allowDocumentRejection is false. In embeds the reject option is shown only when both the embed's allowDocumentRejection option and the document setting allow it, and the embed V1 footer layout follows the same condition. Exposes the field in the multi-sign document response so the embed view can read it. Documents the relationship between the embed's allowDocumentRejection option and the document setting in the iframe and embedded editor docs, and in the API description of the field. Part of #2561
138 lines
5.3 KiB
TypeScript
138 lines
5.3 KiB
TypeScript
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
|
|
import { Badge } from '@documenso/ui/primitives/badge';
|
|
import { Button } from '@documenso/ui/primitives/button';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from '@documenso/ui/primitives/dropdown-menu';
|
|
import { Separator } from '@documenso/ui/primitives/separator';
|
|
import { Plural, Trans } from '@lingui/react/macro';
|
|
import { EnvelopeType, RecipientRole } from '@prisma/client';
|
|
import { BanIcon, DownloadCloudIcon } from 'lucide-react';
|
|
import { Link } from 'react-router';
|
|
import { match } from 'ts-pattern';
|
|
|
|
import { EnvelopeDownloadDialog } from '~/components/dialogs/envelope-download-dialog';
|
|
import { useEmbedSigningContext } from '~/components/embed/embed-signing-context';
|
|
import { BrandingLogo } from '~/components/general/branding-logo';
|
|
|
|
import { BrandingLogoIcon } from '../branding-logo-icon';
|
|
import { DocumentSigningRejectDialog } from '../document-signing/document-signing-reject-dialog';
|
|
import { useRequiredEnvelopeSigningContext } from '../document-signing/envelope-signing-provider';
|
|
import { EnvelopeSignerCompleteDialog } from './envelope-signing-complete-dialog';
|
|
|
|
export const EnvelopeSignerHeader = () => {
|
|
const { envelopeData, envelope, recipientFieldsRemaining, recipient } = useRequiredEnvelopeSigningContext();
|
|
|
|
const isEmbedSigning = useEmbedSigningContext() !== null;
|
|
const hasCustomBrandingLogo = envelopeData.settings.brandingEnabled && Boolean(envelopeData.settings.brandingLogo);
|
|
|
|
return (
|
|
<nav className="embed--DocumentWidgetHeader flex max-w-screen flex-row justify-between border-border border-b bg-background px-4 py-3 md:px-6">
|
|
{/* Left side - Logo and title */}
|
|
<div className="flex min-w-0 flex-1 items-center space-x-2 md:w-auto md:flex-none">
|
|
{!isEmbedSigning &&
|
|
(hasCustomBrandingLogo ? (
|
|
<img
|
|
src={`/api/branding/logo/team/${envelope.teamId}`}
|
|
alt={`${envelope.team.name}'s Logo`}
|
|
className="h-6 w-auto flex-shrink-0"
|
|
/>
|
|
) : (
|
|
<Link to="/" className="flex-shrink-0">
|
|
<BrandingLogo className="hidden h-6 w-auto md:block" />
|
|
<BrandingLogoIcon className="h-6 w-auto md:hidden" />
|
|
</Link>
|
|
))}
|
|
|
|
<h1 title={envelope.title} className="min-w-0 truncate font-semibold text-base text-foreground md:hidden">
|
|
{envelope.title}
|
|
</h1>
|
|
|
|
{!isEmbedSigning && <Separator orientation="vertical" className="hidden h-6 md:block" />}
|
|
|
|
<div className="hidden items-center space-x-2 md:flex">
|
|
<h1 className="whitespace-nowrap font-medium text-foreground text-sm">{envelope.title}</h1>
|
|
|
|
<Badge>
|
|
{match(recipient.role)
|
|
.with(RecipientRole.VIEWER, () => <Trans>Viewer</Trans>)
|
|
.with(RecipientRole.SIGNER, () => <Trans>Signer</Trans>)
|
|
.with(RecipientRole.APPROVER, () => <Trans>Approver</Trans>)
|
|
.with(RecipientRole.ASSISTANT, () => <Trans>Assistant</Trans>)
|
|
.otherwise(() => null)}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right side - Desktop content */}
|
|
<div className="hidden items-center space-x-2 lg:flex">
|
|
<p className="mr-2 flex-shrink-0 text-muted-foreground text-sm">
|
|
<Plural one="1 Field Remaining" other="# Fields Remaining" value={recipientFieldsRemaining.length} />
|
|
</p>
|
|
|
|
<EnvelopeSignerCompleteDialog />
|
|
</div>
|
|
|
|
{/* Mobile Actions button */}
|
|
<div className="flex-shrink-0 lg:hidden">
|
|
<MobileDropdownMenu />
|
|
</div>
|
|
</nav>
|
|
);
|
|
};
|
|
|
|
const MobileDropdownMenu = () => {
|
|
const { envelope, recipient } = useRequiredEnvelopeSigningContext();
|
|
|
|
const { allowDocumentRejection = true } = useEmbedSigningContext() || {};
|
|
|
|
// Both the document settings and the embed host (if any) must allow rejection.
|
|
const isDocumentRejectionAllowed =
|
|
envelope.type === EnvelopeType.DOCUMENT && envelope.documentMeta.allowDocumentRejection && allowDocumentRejection;
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="outline" size="sm">
|
|
<Trans>Actions</Trans>
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
|
|
<DropdownMenuContent align="end">
|
|
<EnvelopeDownloadDialog
|
|
envelopeId={envelope.id}
|
|
envelopeStatus={envelope.status}
|
|
envelopeItems={envelope.envelopeItems}
|
|
token={recipient.token}
|
|
trigger={
|
|
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
|
|
<div>
|
|
<DownloadCloudIcon className="mr-2 h-4 w-4" />
|
|
<Trans>Download PDF</Trans>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
}
|
|
/>
|
|
|
|
{isDocumentRejectionAllowed && (
|
|
<DocumentSigningRejectDialog
|
|
documentId={mapSecondaryIdToDocumentId(envelope.secondaryId)}
|
|
token={recipient.token}
|
|
trigger={
|
|
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
|
|
<div>
|
|
<BanIcon className="mr-2 h-4 w-4" />
|
|
<Trans>Reject</Trans>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
}
|
|
/>
|
|
)}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
};
|