diff --git a/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx b/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx index e20c88a27..fc1022cfa 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx @@ -118,7 +118,11 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
- + {recipients.length} Recipient(s)
diff --git a/apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx b/apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx index 8a78ca9aa..5c2a64870 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx @@ -92,7 +92,11 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
- + {recipients.length} Recipient(s)
diff --git a/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx b/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx index a43d37af7..c67890dfe 100644 --- a/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx +++ b/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx @@ -114,7 +114,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr Action - {recipient && recipient?.role !== RecipientRole.CC && ( + {!isDraft && recipient && recipient?.role !== RecipientRole.CC && ( {recipient?.role === RecipientRole.VIEWER && ( diff --git a/apps/web/src/app/(dashboard)/documents/data-table.tsx b/apps/web/src/app/(dashboard)/documents/data-table.tsx index ec595641e..c49cdf6ab 100644 --- a/apps/web/src/app/(dashboard)/documents/data-table.tsx +++ b/apps/web/src/app/(dashboard)/documents/data-table.tsx @@ -76,7 +76,12 @@ export const DocumentsDataTable = ({ { header: 'Recipient', accessorKey: 'recipient', - cell: ({ row }) => , + cell: ({ row }) => ( + + ), }, { header: 'Status', diff --git a/apps/web/src/app/(signing)/sign/[token]/page.tsx b/apps/web/src/app/(signing)/sign/[token]/page.tsx index e83f675ce..bd46898af 100644 --- a/apps/web/src/app/(signing)/sign/[token]/page.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/page.tsx @@ -47,7 +47,12 @@ export default async function SigningPage({ params: { token } }: SigningPageProp getRecipientByToken({ token }).catch(() => null), ]); - if (!document || !document.documentData || !recipient) { + if ( + !document || + !document.documentData || + !recipient || + document.status === DocumentStatus.DRAFT + ) { return notFound(); } diff --git a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx index 69dd88d79..cd7cd2305 100644 --- a/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx +++ b/apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx @@ -8,6 +8,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter'; import type { Recipient } from '@documenso/prisma/client'; +import { DocumentStatus } from '@documenso/prisma/client'; import { cn } from '@documenso/ui/lib/utils'; import { useToast } from '@documenso/ui/primitives/use-toast'; @@ -15,18 +16,21 @@ import { StackAvatar } from './stack-avatar'; export type AvatarWithRecipientProps = { recipient: Recipient; + documentStatus: DocumentStatus; }; -export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) { +export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRecipientProps) { const [, copy] = useCopyToClipboard(); const { toast } = useToast(); + const signingToken = documentStatus === DocumentStatus.PENDING ? recipient.token : null; + const onRecipientClick = () => { - if (!recipient.token) { + if (!signingToken) { return; } - void copy(`${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`).then(() => { + void copy(`${NEXT_PUBLIC_WEBAPP_URL()}/sign/${signingToken}`).then(() => { toast({ title: 'Copied to clipboard', description: 'The signing link has been copied to your clipboard.', @@ -37,10 +41,10 @@ export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) { return (
-
-
-

{recipient.email}

-

- {RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName} -

-
+ +
+

{recipient.email}

+

+ {RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName} +

); diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx index 10f7d1e6a..7a269d036 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx @@ -5,7 +5,7 @@ import { useRef, useState } from 'react'; import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter'; -import type { Recipient } from '@documenso/prisma/client'; +import type { DocumentStatus, Recipient } from '@documenso/prisma/client'; import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover'; import { AvatarWithRecipient } from './avatar-with-recipient'; @@ -13,12 +13,14 @@ import { StackAvatar } from './stack-avatar'; import { StackAvatars } from './stack-avatars'; export type StackAvatarsWithTooltipProps = { + documentStatus: DocumentStatus; recipients: Recipient[]; position?: 'top' | 'bottom'; children?: React.ReactNode; }; export const StackAvatarsWithTooltip = ({ + documentStatus, recipients, position, children, @@ -120,7 +122,11 @@ export const StackAvatarsWithTooltip = ({

Waiting

{waitingRecipients.map((recipient: Recipient) => ( - + ))}
)} @@ -129,7 +135,11 @@ export const StackAvatarsWithTooltip = ({

Opened

{openedRecipients.map((recipient: Recipient) => ( - + ))}
)} @@ -138,7 +148,11 @@ export const StackAvatarsWithTooltip = ({

Uncompleted

{uncompletedRecipients.map((recipient: Recipient) => ( - + ))}
)} diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index 8e3b56002..d16b83ea1 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -49,8 +49,8 @@ export const completeDocumentWithToken = async ({ const document = await getDocument({ token, documentId }); - if (document.status === DocumentStatus.COMPLETED) { - throw new Error(`Document ${document.id} has already been completed`); + if (document.status !== DocumentStatus.PENDING) { + throw new Error(`Document ${document.id} must be pending`); } if (document.Recipient.length === 0) { diff --git a/packages/lib/server-only/field/remove-signed-field-with-token.ts b/packages/lib/server-only/field/remove-signed-field-with-token.ts index 6548ae0f1..46d04dd58 100644 --- a/packages/lib/server-only/field/remove-signed-field-with-token.ts +++ b/packages/lib/server-only/field/remove-signed-field-with-token.ts @@ -36,8 +36,8 @@ export const removeSignedFieldWithToken = async ({ throw new Error(`Document not found for field ${field.id}`); } - if (document.status === DocumentStatus.COMPLETED) { - throw new Error(`Document ${document.id} has already been completed`); + if (document.status !== DocumentStatus.PENDING) { + throw new Error(`Document ${document.id} must be pending`); } if (recipient?.signingStatus === SigningStatus.SIGNED) { diff --git a/packages/lib/server-only/field/sign-field-with-token.ts b/packages/lib/server-only/field/sign-field-with-token.ts index b8a5ccf8f..359a5da68 100644 --- a/packages/lib/server-only/field/sign-field-with-token.ts +++ b/packages/lib/server-only/field/sign-field-with-token.ts @@ -58,14 +58,14 @@ export const signFieldWithToken = async ({ throw new Error(`Recipient not found for field ${field.id}`); } - if (document.status === DocumentStatus.COMPLETED) { - throw new Error(`Document ${document.id} has already been completed`); - } - if (document.deletedAt) { throw new Error(`Document ${document.id} has been deleted`); } + if (document.status !== DocumentStatus.PENDING) { + throw new Error(`Document ${document.id} must be pending for signing`); + } + if (recipient?.signingStatus === SigningStatus.SIGNED) { throw new Error(`Recipient ${recipient.id} has already signed`); } diff --git a/packages/prisma/seed/documents.ts b/packages/prisma/seed/documents.ts index 6c1e698c5..2e6462daa 100644 --- a/packages/prisma/seed/documents.ts +++ b/packages/prisma/seed/documents.ts @@ -342,14 +342,15 @@ export const seedPendingDocumentWithFullFields = async ({ }, }); - const latestDocument = updateDocumentOptions - ? await prisma.document.update({ - where: { - id: document.id, - }, - data: updateDocumentOptions, - }) - : document; + const latestDocument = await prisma.document.update({ + where: { + id: document.id, + }, + data: { + ...updateDocumentOptions, + status: DocumentStatus.PENDING, + }, + }); return { document: latestDocument,