diff --git a/apps/web/src/app/(dashboard)/documents/[id]/document-page-view-button.tsx b/apps/web/src/app/(dashboard)/documents/[id]/document-page-view-button.tsx index 2593ef0b8..022263285 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/document-page-view-button.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/document-page-view-button.tsx @@ -15,11 +15,11 @@ import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/ import { trpc as trpcClient } from '@documenso/trpc/client'; import { Button } from '@documenso/ui/primitives/button'; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@documenso/ui/primitives/dropdown-menu'; + SplitButton, + SplitButtonAction, + SplitButtonDropdown, + SplitButtonDropdownItem, +} from '@documenso/ui/primitives/split-button'; import { useToast } from '@documenso/ui/primitives/use-toast'; export type DocumentPageViewButtonProps = { @@ -31,7 +31,9 @@ export type DocumentPageViewButtonProps = { team?: Pick; }; -export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps) => { +export const DocumentPageViewButton = ({ + document: activeDocument, +}: DocumentPageViewButtonProps) => { const { data: session } = useSession(); const { toast } = useToast(); const { _ } = useLingui(); @@ -40,33 +42,27 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps return null; } - const recipient = document.recipients.find((recipient) => recipient.email === session.user.email); + const recipient = activeDocument.recipients.find( + (recipient) => recipient.email === session.user.email, + ); const isRecipient = !!recipient; - const isPending = document.status === DocumentStatus.PENDING; - const isComplete = document.status === DocumentStatus.COMPLETED; + const isPending = activeDocument.status === DocumentStatus.PENDING; + const isComplete = activeDocument.status === DocumentStatus.COMPLETED; const isSigned = recipient?.signingStatus === SigningStatus.SIGNED; const role = recipient?.role; - const documentsPath = formatDocumentsPath(document.team?.url); + const documentsPath = formatDocumentsPath(activeDocument.team?.url); - const onDownloadClick = async ({ - includeCertificate = true, - includeAuditLog = true, - }: { - includeCertificate?: boolean; - includeAuditLog?: boolean; - } = {}) => { + const onDownloadClick = async () => { try { const documentWithData = await trpcClient.document.getDocumentById.query( { - documentId: document.id, - includeCertificate, - includeAuditLog, + documentId: activeDocument.id, }, { context: { - teamId: document.team?.id?.toString(), + teamId: activeDocument.team?.id?.toString(), }, }, ); @@ -80,8 +76,6 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps await downloadPDF({ documentData, fileName: documentWithData.title, - includeCertificate, - includeAuditLog, }); } catch (err) { toast({ @@ -92,6 +86,100 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps } }; + const onDownloadAuditLogClick = async () => { + try { + const { url } = await trpcClient.document.downloadAuditLogs.mutate({ + documentId: activeDocument.id, + }); + + const iframe = Object.assign(document.createElement('iframe'), { + src: url, + }); + + Object.assign(iframe.style, { + position: 'fixed', + top: '0', + left: '0', + width: '0', + height: '0', + }); + + const onLoaded = () => { + if (iframe.contentDocument?.readyState === 'complete') { + iframe.contentWindow?.print(); + + iframe.contentWindow?.addEventListener('afterprint', () => { + document.body.removeChild(iframe); + }); + } + }; + + // When the iframe has loaded, print the iframe and remove it from the dom + iframe.addEventListener('load', onLoaded); + + document.body.appendChild(iframe); + + onLoaded(); + } catch (error) { + console.error(error); + + toast({ + title: _(msg`Something went wrong`), + description: _( + msg`Sorry, we were unable to download the audit logs. Please try again later.`, + ), + variant: 'destructive', + }); + } + }; + + const onDownloadSigningCertificateClick = async () => { + try { + const { url } = await trpcClient.document.downloadCertificate.mutate({ + documentId: activeDocument.id, + }); + + const iframe = Object.assign(document.createElement('iframe'), { + src: url, + }); + + Object.assign(iframe.style, { + position: 'fixed', + top: '0', + left: '0', + width: '0', + height: '0', + }); + + const onLoaded = () => { + if (iframe.contentDocument?.readyState === 'complete') { + iframe.contentWindow?.print(); + + iframe.contentWindow?.addEventListener('afterprint', () => { + document.body.removeChild(iframe); + }); + } + }; + + // When the iframe has loaded, print the iframe and remove it from the dom + iframe.addEventListener('load', onLoaded); + + document.body.appendChild(iframe); + + onLoaded(); + } catch (error) { + console.error(error); + + toast({ + title: _(msg`Something went wrong`), + description: _( + msg`Sorry, we were unable to download the certificate. Please try again later.`, + ), + variant: 'destructive', + }); + } + }; + return match({ isRecipient, isPending, @@ -125,50 +213,27 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps )) .with({ isComplete: false }, () => ( )) .with({ isComplete: true }, () => ( - - - - + + void onDownloadClick()}> + + Download + + + void onDownloadAuditLogClick()}> + Only Audit Log + - - void onDownloadClick()}> - Complete Document - - - - void onDownloadClick({ includeCertificate: true, includeAuditLog: false }) - } - > - Without Audit Log - - - - void onDownloadClick({ includeCertificate: false, includeAuditLog: true }) - } - > - Without Certificate - - - - void onDownloadClick({ includeCertificate: false, includeAuditLog: false }) - } - > - Without Certificate & Audit Log - - - + void onDownloadSigningCertificateClick()}> + Only Signing Certificate + + + )) .otherwise(() => null); }; diff --git a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx index 7977aa2c6..ab73da3e7 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx @@ -187,6 +187,8 @@ export const EditDocumentForm = ({ title: data.title, externalId: data.externalId || null, visibility: data.visibility, + includeSigningCertificate: data.includeSigningCertificate, + includeAuditTrailLog: data.includeAuditTrailLog, globalAccessAuth: data.globalAccessAuth ?? null, globalActionAuth: data.globalActionAuth ?? null, }, diff --git a/apps/web/src/components/document/document-history-sheet.tsx b/apps/web/src/components/document/document-history-sheet.tsx index cb607a125..c7faa707b 100644 --- a/apps/web/src/components/document/document-history-sheet.tsx +++ b/apps/web/src/components/document/document-history-sheet.tsx @@ -363,6 +363,40 @@ export const DocumentHistorySheet = ({ ]} /> )) + .with( + { type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SIGNING_CERTIFICATE_UPDATED }, + ({ data }) => ( + + ), + ) + .with( + { type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_AUDIT_TRAIL_UPDATED }, + ({ data }) => ( + + ), + ) .exhaustive()} {isUserDetailsVisible && ( diff --git a/packages/lib/client-only/download-pdf.ts b/packages/lib/client-only/download-pdf.ts index 197262f5c..830e3428a 100644 --- a/packages/lib/client-only/download-pdf.ts +++ b/packages/lib/client-only/download-pdf.ts @@ -6,16 +6,9 @@ import { downloadFile } from './download-file'; type DownloadPDFProps = { documentData: DocumentData; fileName?: string; - includeCertificate?: boolean; - includeAuditLog?: boolean; }; -export const downloadPDF = async ({ - documentData, - fileName, - includeCertificate, - includeAuditLog, -}: DownloadPDFProps) => { +export const downloadPDF = async ({ documentData, fileName }: DownloadPDFProps) => { const bytes = await getFile(documentData); const blob = new Blob([bytes], { @@ -24,18 +17,8 @@ export const downloadPDF = async ({ const baseTitle = (fileName ?? 'document').replace(/\.pdf$/, ''); - let suffix = '_signed'; - - if (includeCertificate && includeAuditLog) { - suffix = suffix + '_with_certificate_and_audit'; - } else if (includeCertificate) { - suffix = suffix + '_with_certificate'; - } else if (includeAuditLog) { - suffix = suffix + '_with_audit'; - } - downloadFile({ - filename: `${baseTitle}${suffix}.pdf`, + filename: `${baseTitle}_signed.pdf`, data: blob, }); }; diff --git a/packages/lib/server-only/document/create-document.ts b/packages/lib/server-only/document/create-document.ts index 5912d7314..daf303adc 100644 --- a/packages/lib/server-only/document/create-document.ts +++ b/packages/lib/server-only/document/create-document.ts @@ -124,6 +124,8 @@ export const createDocument = async ({ team?.teamGlobalSettings?.documentVisibility, userTeamRole ?? TeamMemberRole.MEMBER, ), + includeSigningCertificate: team?.teamGlobalSettings?.includeSigningCertificate ?? true, + includeAuditTrailLog: team?.teamGlobalSettings?.includeAuditTrailLog ?? true, formValues, source: DocumentSource.DOCUMENT, documentMeta: { diff --git a/packages/lib/server-only/document/seal-document.ts b/packages/lib/server-only/document/seal-document.ts index 7be237dbc..9dd2cee2c 100644 --- a/packages/lib/server-only/document/seal-document.ts +++ b/packages/lib/server-only/document/seal-document.ts @@ -111,21 +111,36 @@ export const sealDocument = async ({ // !: Need to write the fields onto the document as a hard copy const pdfData = await getFile(documentData); - const certificateData = - (document.team?.teamGlobalSettings?.includeSigningCertificate ?? true) - ? await getCertificatePdf({ - documentId, - language: document.documentMeta?.language, - }).catch(() => null) - : null; + let includeSigningCertificate; - const auditLogData = - (document.team?.teamGlobalSettings?.includeAuditTrailLog ?? true) - ? await getAuditLogsPdf({ - documentId, - language: document.documentMeta?.language, - }).catch(() => null) - : null; + if (document.teamId) { + includeSigningCertificate = + document.team?.teamGlobalSettings?.includeSigningCertificate ?? true; + } else { + includeSigningCertificate = document.includeSigningCertificate ?? true; + } + + const certificateData = includeSigningCertificate + ? await getCertificatePdf({ + documentId, + language: document.documentMeta?.language, + }).catch(() => null) + : null; + + let includeAuditTrailLog; + + if (document.teamId) { + includeAuditTrailLog = document.team?.teamGlobalSettings?.includeAuditTrailLog ?? true; + } else { + includeAuditTrailLog = document.includeAuditTrailLog ?? true; + } + + const auditLogData = includeAuditTrailLog + ? await getAuditLogsPdf({ + documentId, + language: document.documentMeta?.language, + }).catch(() => null) + : null; const doc = await PDFDocument.load(pdfData); diff --git a/packages/lib/server-only/document/update-document.ts b/packages/lib/server-only/document/update-document.ts index bbacfbe1d..e0735c468 100644 --- a/packages/lib/server-only/document/update-document.ts +++ b/packages/lib/server-only/document/update-document.ts @@ -21,6 +21,8 @@ export type UpdateDocumentOptions = { title?: string; externalId?: string | null; visibility?: DocumentVisibility | null; + includeSigningCertificate?: boolean; + includeAuditTrailLog?: boolean; globalAccessAuth?: TDocumentAccessAuthTypes | null; globalActionAuth?: TDocumentActionAuthTypes | null; }; @@ -156,6 +158,12 @@ export const updateDocument = async ({ documentGlobalActionAuth === undefined || documentGlobalActionAuth === newGlobalActionAuth; const isDocumentVisibilitySame = data.visibility === undefined || data.visibility === document.visibility; + const isIncludeSigningCertificateSame = + data.includeSigningCertificate === undefined || + data.includeSigningCertificate === document.includeSigningCertificate; + const isIncludeAuditTrailLogSame = + data.includeAuditTrailLog === undefined || + data.includeAuditTrailLog === document.includeAuditTrailLog; const auditLogs: CreateDocumentAuditLogDataResponse[] = []; @@ -235,6 +243,34 @@ export const updateDocument = async ({ ); } + if (!isIncludeSigningCertificateSame) { + auditLogs.push( + createDocumentAuditLogData({ + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SIGNING_CERTIFICATE_UPDATED, + documentId, + metadata: requestMetadata, + data: { + from: String(document.includeSigningCertificate), + to: String(data.includeSigningCertificate || false), + }, + }), + ); + } + + if (!isIncludeAuditTrailLogSame) { + auditLogs.push( + createDocumentAuditLogData({ + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_AUDIT_TRAIL_UPDATED, + documentId, + metadata: requestMetadata, + data: { + from: String(document.includeAuditTrailLog), + to: String(data.includeAuditTrailLog || false), + }, + }), + ); + } + // Early return if nothing is required. if (auditLogs.length === 0) { return document; @@ -254,6 +290,8 @@ export const updateDocument = async ({ title: data.title, externalId: data.externalId, visibility: data.visibility as DocumentVisibility, + includeSigningCertificate: data.includeSigningCertificate, + includeAuditTrailLog: data.includeAuditTrailLog, authOptions, }, }); diff --git a/packages/lib/types/document-audit-logs.ts b/packages/lib/types/document-audit-logs.ts index e0b251f5d..77eda6301 100644 --- a/packages/lib/types/document-audit-logs.ts +++ b/packages/lib/types/document-audit-logs.ts @@ -29,7 +29,9 @@ export const ZDocumentAuditLogTypeSchema = z.enum([ 'DOCUMENT_FIELD_INSERTED', // When a field is inserted (signed/approved/etc) by a recipient. 'DOCUMENT_FIELD_UNINSERTED', // When a field is uninserted by a recipient. 'DOCUMENT_FIELD_PREFILLED', // When a field is prefilled by an assistant. - 'DOCUMENT_VISIBILITY_UPDATED', // When the document visibility scope is updated + 'DOCUMENT_VISIBILITY_UPDATED', // When the document visibility scope is updated. + 'DOCUMENT_SIGNING_CERTIFICATE_UPDATED', // When the include signing certificate is updated. + 'DOCUMENT_AUDIT_TRAIL_UPDATED', // When the include audit trail is updated. 'DOCUMENT_GLOBAL_AUTH_ACCESS_UPDATED', // When the global access authentication is updated. 'DOCUMENT_GLOBAL_AUTH_ACTION_UPDATED', // When the global action authentication is updated. 'DOCUMENT_META_UPDATED', // When the document meta data is updated. @@ -397,6 +399,16 @@ export const ZDocumentAuditLogEventDocumentVisibilitySchema = z.object({ data: ZGenericFromToSchema, }); +export const ZDocumentAuditLogEventDocumentSigningCertificateUpdatedSchema = z.object({ + type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SIGNING_CERTIFICATE_UPDATED), + data: ZGenericFromToSchema, +}); + +export const ZDocumentAuditLogEventDocumentAuditTrailUpdatedSchema = z.object({ + type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_AUDIT_TRAIL_UPDATED), + data: ZGenericFromToSchema, +}); + /** * Event: Document global authentication access updated. */ @@ -574,6 +586,8 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and( ZDocumentAuditLogEventDocumentFieldUninsertedSchema, ZDocumentAuditLogEventDocumentFieldPrefilledSchema, ZDocumentAuditLogEventDocumentVisibilitySchema, + ZDocumentAuditLogEventDocumentSigningCertificateUpdatedSchema, + ZDocumentAuditLogEventDocumentAuditTrailUpdatedSchema, ZDocumentAuditLogEventDocumentGlobalAuthAccessUpdatedSchema, ZDocumentAuditLogEventDocumentGlobalAuthActionUpdatedSchema, ZDocumentAuditLogEventDocumentMetaUpdatedSchema, diff --git a/packages/lib/types/document.ts b/packages/lib/types/document.ts index 830842e83..694ad16cd 100644 --- a/packages/lib/types/document.ts +++ b/packages/lib/types/document.ts @@ -18,6 +18,8 @@ import { ZRecipientLiteSchema } from './recipient'; */ export const ZDocumentSchema = DocumentSchema.pick({ visibility: true, + includeSigningCertificate: true, + includeAuditTrailLog: true, status: true, source: true, id: true, @@ -82,6 +84,8 @@ export const ZDocumentLiteSchema = DocumentSchema.pick({ deletedAt: true, teamId: true, templateId: true, + includeSigningCertificate: true, + includeAuditTrailLog: true, }); /** @@ -104,6 +108,8 @@ export const ZDocumentManySchema = DocumentSchema.pick({ deletedAt: true, teamId: true, templateId: true, + includeSigningCertificate: true, + includeAuditTrailLog: true, }).extend({ user: UserSchema.pick({ id: true, diff --git a/packages/lib/utils/document-audit-logs.ts b/packages/lib/utils/document-audit-logs.ts index 44ae23ac0..5bf76dae2 100644 --- a/packages/lib/utils/document-audit-logs.ts +++ b/packages/lib/utils/document-audit-logs.ts @@ -322,6 +322,14 @@ export const formatDocumentAuditLogAction = ( anonymous: msg`Document visibility updated`, identified: msg`${prefix} updated the document visibility`, })) + .with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SIGNING_CERTIFICATE_UPDATED }, () => ({ + anonymous: msg`Document signing certificate updated`, + identified: msg`${prefix} updated the document signing certificate`, + })) + .with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_AUDIT_TRAIL_UPDATED }, () => ({ + anonymous: msg`Document audit trail updated`, + identified: msg`${prefix} updated the document audit trail`, + })) .with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACCESS_UPDATED }, () => ({ anonymous: msg`Document access auth updated`, identified: msg`${prefix} updated the document access auth requirements`, diff --git a/packages/prisma/migrations/20250219075352_add_document_properties/migration.sql b/packages/prisma/migrations/20250219075352_add_document_properties/migration.sql new file mode 100644 index 000000000..b216d46cd --- /dev/null +++ b/packages/prisma/migrations/20250219075352_add_document_properties/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Document" ADD COLUMN "includeAuditTrail" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "includeSigningCertificate" BOOLEAN NOT NULL DEFAULT true; diff --git a/packages/prisma/migrations/20250219100115_update_property_name/migration.sql b/packages/prisma/migrations/20250219100115_update_property_name/migration.sql new file mode 100644 index 000000000..51a89efea --- /dev/null +++ b/packages/prisma/migrations/20250219100115_update_property_name/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `includeAuditTrail` on the `Document` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Document" DROP COLUMN "includeAuditTrail", +ADD COLUMN "includeAuditTrailLog" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 65dcd7973..abc96ea7c 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -304,30 +304,32 @@ enum DocumentVisibility { /// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';", "import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';"]) model Document { - id Int @id @default(autoincrement()) - externalId String? /// @zod.string.describe("A custom external ID you can use to identify the document.") - userId Int /// @zod.number.describe("The ID of the user that created this document.") - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema) - formValues Json? /// [DocumentFormValues] @zod.custom.use(ZDocumentFormValuesSchema) - visibility DocumentVisibility @default(EVERYONE) - title String - status DocumentStatus @default(DRAFT) - recipients Recipient[] - fields Field[] - shareLinks DocumentShareLink[] - documentDataId String - documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) - documentMeta DocumentMeta? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - completedAt DateTime? - deletedAt DateTime? - teamId Int? - team Team? @relation(fields: [teamId], references: [id]) - templateId Int? - template Template? @relation(fields: [templateId], references: [id], onDelete: SetNull) - source DocumentSource + id Int @id @default(autoincrement()) + externalId String? /// @zod.string.describe("A custom external ID you can use to identify the document.") + userId Int /// @zod.number.describe("The ID of the user that created this document.") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema) + formValues Json? /// [DocumentFormValues] @zod.custom.use(ZDocumentFormValuesSchema) + visibility DocumentVisibility @default(EVERYONE) + includeSigningCertificate Boolean @default(true) + includeAuditTrailLog Boolean @default(false) + title String + status DocumentStatus @default(DRAFT) + recipients Recipient[] + fields Field[] + shareLinks DocumentShareLink[] + documentDataId String + documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) + documentMeta DocumentMeta? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + completedAt DateTime? + deletedAt DateTime? + teamId Int? + team Team? @relation(fields: [teamId], references: [id]) + templateId Int? + template Template? @relation(fields: [templateId], references: [id], onDelete: SetNull) + source DocumentSource auditLogs DocumentAuditLog[] diff --git a/packages/trpc/server/document-router/schema.ts b/packages/trpc/server/document-router/schema.ts index 4211d3410..1e4a30edc 100644 --- a/packages/trpc/server/document-router/schema.ts +++ b/packages/trpc/server/document-router/schema.ts @@ -63,6 +63,16 @@ export const ZDocumentVisibilitySchema = z .nativeEnum(DocumentVisibility) .describe('The visibility of the document.'); +export const ZDocumentIncludeSigningCertificateSchema = z + .boolean() + .default(true) + .describe('Whether to include a signing certificate in the document.'); + +export const ZDocumentIncludeAuditTrailSchema = z + .boolean() + .default(true) + .describe('Whether to include an audit trail in the document.'); + export const ZDocumentMetaTimezoneSchema = z .string() .describe( @@ -237,6 +247,8 @@ export const ZUpdateDocumentRequestSchema = z.object({ title: ZDocumentTitleSchema.optional(), externalId: ZDocumentExternalIdSchema.nullish(), visibility: ZDocumentVisibilitySchema.optional(), + includeSigningCertificate: ZDocumentIncludeSigningCertificateSchema.optional(), + includeAuditTrailLog: ZDocumentIncludeAuditTrailSchema.optional(), globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullish(), globalActionAuth: ZDocumentActionAuthTypesSchema.nullish(), }) diff --git a/packages/ui/primitives/document-flow/add-settings.tsx b/packages/ui/primitives/document-flow/add-settings.tsx index 0cc37d5a5..7dce1b5bb 100644 --- a/packages/ui/primitives/document-flow/add-settings.tsx +++ b/packages/ui/primitives/document-flow/add-settings.tsx @@ -42,6 +42,7 @@ import { FormMessage, } from '@documenso/ui/primitives/form/form'; +import { Checkbox } from '../checkbox'; import { Combobox } from '../combobox'; import { Input } from '../input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select'; @@ -92,6 +93,8 @@ export const AddSettingsFormPartial = ({ visibility: document.visibility || '', globalAccessAuth: documentAuthOption?.globalAccessAuth || undefined, globalActionAuth: documentAuthOption?.globalActionAuth || undefined, + includeSigningCertificate: document.includeSigningCertificate ?? true, + includeAuditTrailLog: document.includeAuditTrailLog ?? true, meta: { timezone: TIME_ZONES.find((timezone) => timezone === document.documentMeta?.timezone) ?? @@ -259,6 +262,111 @@ export const AddSettingsFormPartial = ({ /> )} + ( + + + Recipient action authentication + + + + + + + + )} + /> + + + + + Certificates + + + +
+ ( + +
+ + + + + Include signing certificate{' '} + + + + + + + + Including the signing certificate means that the certificate + will be attached to the document. You won't be able to remove + it.
+
+ If you don't include it, you can download it individually. +
+
+
+
+
+ + +
+ )} + /> + + ( + +
+ + + + + Include audit trail{' '} + + + + + + + + Including the audit trail means that the log of all actions will + be attached to the document. You won't be able to remove it.{' '} +
+
+ If you don't include it, you can download it individually. +
+
+
+
+
+ + +
+ )} + /> +
+
+
+
+ {isDocumentEnterprise && ( ['variant']; + size?: React.ComponentProps['size']; +}>({}); + +const SplitButton = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & { + variant?: React.ComponentProps['variant']; + size?: React.ComponentProps['size']; + } +>(({ className, children, variant = 'default', size = 'default', ...props }, ref) => { + return ( + +
+ {children} +
+
+ ); +}); +SplitButton.displayName = 'SplitButton'; + +const SplitButtonAction = React.forwardRef< + HTMLButtonElement, + React.ButtonHTMLAttributes +>(({ className, children, ...props }, ref) => { + const { variant, size } = React.useContext(SplitButtonContext); + return ( + + ); +}); +SplitButtonAction.displayName = 'SplitButtonAction'; + +const SplitButtonDropdown = React.forwardRef>( + ({ children, ...props }, ref) => { + const { variant, size } = React.useContext(SplitButtonContext); + return ( + + + + + + {children} + + + ); + }, +); +SplitButtonDropdown.displayName = 'SplitButtonDropdown'; + +const SplitButtonDropdownItem = DropdownMenuItem; + +export { SplitButton, SplitButtonAction, SplitButtonDropdown, SplitButtonDropdownItem };