Sign Document
diff --git a/apps/web/src/app/(signing)/sign/[token]/name-field.tsx b/apps/web/src/app/(signing)/sign/[token]/name-field.tsx
index bbe18fb8a..6e661e77a 100644
--- a/apps/web/src/app/(signing)/sign/[token]/name-field.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/name-field.tsx
@@ -6,8 +6,8 @@ import { useRouter } from 'next/navigation';
import { Loader } from 'lucide-react';
-import { Recipient } from '@documenso/prisma/client';
-import { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
+import type { Recipient } from '@documenso/prisma/client';
+import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog';
@@ -98,7 +98,7 @@ export const NameField = ({ field, recipient }: NameFieldProps) => {
};
return (
-
+
{isLoading && (
diff --git a/apps/web/src/app/(signing)/sign/[token]/page.tsx b/apps/web/src/app/(signing)/sign/[token]/page.tsx
index 97babb82f..18b81696e 100644
--- a/apps/web/src/app/(signing)/sign/[token]/page.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/page.tsx
@@ -2,9 +2,12 @@ import { notFound, redirect } from 'next/navigation';
import { match } from 'ts-pattern';
+import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
+import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
+import { getDocumentMetaByDocumentId } from '@documenso/lib/server-only/document/get-document-meta-by-document-id';
import { viewedDocument } from '@documenso/lib/server-only/document/viewed-document';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
@@ -42,6 +45,8 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
viewedDocument({ token }).catch(() => null),
]);
+ const documentMeta = await getDocumentMetaByDocumentId({ id: document!.id }).catch(() => null);
+
if (!document || !document.documentData || !recipient) {
return notFound();
}
@@ -111,7 +116,13 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
))
.with(FieldType.DATE, () => (
-
+
))
.with(FieldType.EMAIL, () => (
diff --git a/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx b/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
index ec3e45fe5..220d3084a 100644
--- a/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
@@ -127,7 +127,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
};
return (
-
+
{isLoading && (
diff --git a/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx b/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx
index 046e5b3df..b4805fa6b 100644
--- a/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx
@@ -2,8 +2,9 @@
import React from 'react';
-import { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
+import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { FieldRootContainer } from '@documenso/ui/components/field/field';
+import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
export type SignatureFieldProps = {
field: FieldWithSignature;
@@ -11,6 +12,8 @@ export type SignatureFieldProps = {
children: React.ReactNode;
onSign?: () => Promise
| void;
onRemove?: () => Promise | void;
+ type?: 'Date' | 'Email' | 'Name' | 'Signature';
+ tooltipText?: string | null;
};
export const SigningFieldContainer = ({
@@ -19,6 +22,8 @@ export const SigningFieldContainer = ({
onSign,
onRemove,
children,
+ type,
+ tooltipText,
}: SignatureFieldProps) => {
const onSignFieldClick = async () => {
if (field.inserted) {
@@ -46,7 +51,22 @@ export const SigningFieldContainer = ({
/>
)}
- {field.inserted && !loading && (
+ {type === 'Date' && field.inserted && !loading && (
+
+
+
+
+
+ {tooltipText && {tooltipText}}
+
+ )}
+
+ {type !== 'Date' && field.inserted && !loading && (
-
+
+
-
+
+
No value found.
-
- {allRoles.map((value: string, i: number) => (
- handleSelect(value)}>
+
+
+ {options.map((option, index) => (
+ onOptionSelected(option)}>
- {value}
+
+ {option}
))}
diff --git a/packages/ui/primitives/command.tsx b/packages/ui/primitives/command.tsx
index 67cd3f487..cbc306c66 100644
--- a/packages/ui/primitives/command.tsx
+++ b/packages/ui/primitives/command.tsx
@@ -2,7 +2,7 @@
import * as React from 'react';
-import { DialogProps } from '@radix-ui/react-dialog';
+import type { DialogProps } from '@radix-ui/react-dialog';
import { Command as CommandPrimitive } from 'cmdk';
import { Search } from 'lucide-react';
diff --git a/packages/ui/primitives/document-flow/add-signature.tsx b/packages/ui/primitives/document-flow/add-signature.tsx
index e4e5d9253..5accdca16 100644
--- a/packages/ui/primitives/document-flow/add-signature.tsx
+++ b/packages/ui/primitives/document-flow/add-signature.tsx
@@ -7,11 +7,13 @@ import { DateTime } from 'luxon';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
+import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
import type { Field } from '@documenso/prisma/client';
import { FieldType } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
+import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/types';
import { FieldToolTip } from '../../components/field/field-tooltip';
import { cn } from '../../lib/utils';
@@ -34,7 +36,6 @@ import {
SinglePlayerModeCustomTextField,
SinglePlayerModeSignatureField,
} from './single-player-mode-fields';
-import type { DocumentFlowStep } from './types';
export type AddSignatureFormProps = {
defaultValues?: TAddSignatureFormSchema;
@@ -140,7 +141,7 @@ export const AddSignatureFormPartial = ({
return match(field.type)
.with(FieldType.DATE, () => ({
...field,
- customText: DateTime.now().toFormat('yyyy-MM-dd hh:mm a'),
+ customText: DateTime.now().toFormat(DEFAULT_DOCUMENT_DATE_FORMAT),
inserted: true,
}))
.with(FieldType.EMAIL, () => ({
diff --git a/packages/ui/primitives/document-flow/add-subject.tsx b/packages/ui/primitives/document-flow/add-subject.tsx
index 881d59c74..d73019732 100644
--- a/packages/ui/primitives/document-flow/add-subject.tsx
+++ b/packages/ui/primitives/document-flow/add-subject.tsx
@@ -1,11 +1,30 @@
'use client';
-import { useForm } from 'react-hook-form';
+import { useEffect } from 'react';
+import { Controller, useForm } from 'react-hook-form';
+
+import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
+import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
import type { Field, Recipient } from '@documenso/prisma/client';
import { DocumentStatus } from '@documenso/prisma/client';
+import { SendStatus } from '@documenso/prisma/client';
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from '@documenso/ui/primitives/accordion';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@documenso/ui/primitives/select';
+import { Combobox } from '../combobox';
import { FormErrorMessage } from '../form/form-error-message';
import { Input } from '../input';
import { Label } from '../label';
@@ -31,20 +50,25 @@ export type AddSubjectFormProps = {
export const AddSubjectFormPartial = ({
documentFlow,
- recipients: _recipients,
- fields: _fields,
+ recipients: recipients,
+ fields: fields,
document,
onSubmit,
}: AddSubjectFormProps) => {
const {
+ control,
register,
handleSubmit,
- formState: { errors, isSubmitting },
+ formState: { errors, isSubmitting, touchedFields },
+ getValues,
+ setValue,
} = useForm({
defaultValues: {
- email: {
+ meta: {
subject: document.documentMeta?.subject ?? '',
message: document.documentMeta?.message ?? '',
+ timezone: document.documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE,
+ dateFormat: document.documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT,
},
},
});
@@ -52,6 +76,20 @@ export const AddSubjectFormPartial = ({
const onFormSubmit = handleSubmit(onSubmit);
const { currentStep, totalSteps, previousStep } = useStep();
+ const hasDateField = fields.find((field) => field.type === 'DATE');
+
+ const documentHasBeenSent = recipients.some(
+ (recipient) => recipient.sendStatus === SendStatus.SENT,
+ );
+
+ // We almost always want to set the timezone to the user's local timezone to avoid confusion
+ // when the document is signed.
+ useEffect(() => {
+ if (!touchedFields.meta?.timezone && !documentHasBeenSent) {
+ setValue('meta.timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);
+ }
+ }, [documentHasBeenSent, setValue, touchedFields.meta?.timezone]);
+
return (
<>
-
+
@@ -86,14 +124,12 @@ export const AddSubjectFormPartial = ({
id="message"
className="bg-background mt-2 h-32 resize-none"
disabled={isSubmitting}
- {...register('email.message')}
+ {...register('meta.message')}
/>
@@ -123,6 +159,67 @@ export const AddSubjectFormPartial = ({
+
+
+
+
+ Advanced Options
+
+
+
+ {hasDateField && (
+
+
+
+ (
+
+ )}
+ />
+
+ )}
+
+ {hasDateField && (
+
+
+
+ (
+ value && onChange(value)}
+ disabled={documentHasBeenSent}
+ />
+ )}
+ />
+
+ )}
+
+
+