chore: merged main

This commit is contained in:
Catalin Pit
2024-09-11 08:40:23 +03:00
389 changed files with 19630 additions and 4241 deletions

View File

@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Caveat } from 'next/font/google';
import { Trans, msg } from '@lingui/macro';
import {
CalendarDays,
CheckSquare,
@ -21,7 +22,7 @@ import { useFieldArray, useForm } from 'react-hook-form';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { useDocumentElement } from '@documenso/lib/client-only/hooks/use-document-element';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '@documenso/lib/constants/recipient-roles';
import {
type TFieldMetaSchema as FieldMeta,
ZFieldMetaSchema,
@ -111,6 +112,7 @@ export const AddTemplateFieldsFormPartial = ({
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '',
signerToken:
recipients.find((recipient) => recipient.id === field.recipientId)?.token ?? '',
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
})),
},
});
@ -155,6 +157,38 @@ export const AddTemplateFieldsFormPartial = ({
selectedSignerIndex === -1 ? 0 : selectedSignerIndex,
);
const filterFieldsWithEmptyValues = (fields: typeof localFields, fieldType: string) =>
fields
.filter((field) => field.type === fieldType)
.filter((field) => {
if (field.fieldMeta && 'values' in field.fieldMeta) {
return field.fieldMeta.values?.length === 0;
}
return true;
});
const emptyCheckboxFields = useMemo(
() => filterFieldsWithEmptyValues(localFields, FieldType.CHECKBOX),
// eslint-disable-next-line react-hooks/exhaustive-deps
[localFields],
);
const emptyRadioFields = useMemo(
() => filterFieldsWithEmptyValues(localFields, FieldType.RADIO),
// eslint-disable-next-line react-hooks/exhaustive-deps
[localFields],
);
const emptySelectFields = useMemo(
() => filterFieldsWithEmptyValues(localFields, FieldType.DROPDOWN),
// eslint-disable-next-line react-hooks/exhaustive-deps
[localFields],
);
const hasErrors =
emptyCheckboxFields.length > 0 || emptyRadioFields.length > 0 || emptySelectFields.length > 0;
const [isFieldWithinBounds, setIsFieldWithinBounds] = useState(false);
const [coords, setCoords] = useState({
x: 0,
@ -365,8 +399,8 @@ export const AddTemplateFieldsFormPartial = ({
<>
{showAdvancedSettings && currentField ? (
<FieldAdvancedSettings
title="Advanced settings"
description={`Configure the ${FRIENDLY_FIELD_TYPE[currentField.type]} field`}
title={msg`Advanced settings`}
description={msg`Configure the ${FRIENDLY_FIELD_TYPE[currentField.type]} field`}
field={currentField}
fields={localFields}
onAdvancedSettings={handleAdvancedSettings}
@ -460,14 +494,15 @@ export const AddTemplateFieldsFormPartial = ({
<CommandEmpty>
<span className="text-muted-foreground inline-block px-4">
No recipient matching this description was found.
<Trans>No recipient matching this description was found.</Trans>
</span>
</CommandEmpty>
{recipientsByRoleToDisplay.map(([role, roleRecipients], roleIndex) => (
<CommandGroup key={roleIndex}>
<div className="text-muted-foreground mb-1 ml-2 mt-2 text-xs font-medium">
{`${RECIPIENT_ROLES_DESCRIPTION[role].roleName}s`}
{/* Todo: Translations - Add plural translations. */}
{`${RECIPIENT_ROLES_DESCRIPTION_ENG[role].roleName}s`}
</div>
{roleRecipients.length === 0 && (
@ -475,7 +510,7 @@ export const AddTemplateFieldsFormPartial = ({
key={`${role}-empty`}
className="text-muted-foreground/80 px-4 pb-4 pt-2.5 text-center text-xs"
>
No recipients with this role
<Trans>No recipients with this role</Trans>
</div>
)}
@ -542,7 +577,7 @@ export const AddTemplateFieldsFormPartial = ({
fontCaveat.className,
)}
>
Signature
<Trans>Signature</Trans>
</p>
</CardContent>
</Card>
@ -594,7 +629,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<Mail className="h-4 w-4" />
Email
<Trans>Email</Trans>
</p>
</CardContent>
</Card>
@ -620,7 +655,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<User className="h-4 w-4" />
Name
<Trans>Name</Trans>
</p>
</CardContent>
</Card>
@ -646,7 +681,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<CalendarDays className="h-4 w-4" />
Date
<Trans>Date</Trans>
</p>
</CardContent>
</Card>
@ -672,7 +707,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<Type className="h-4 w-4" />
Text
<Trans>Text</Trans>
</p>
</CardContent>
</Card>
@ -698,7 +733,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<Hash className="h-4 w-4" />
Number
<Trans>Number</Trans>
</p>
</CardContent>
</Card>
@ -724,7 +759,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<Disc className="h-4 w-4" />
Radio
<Trans>Radio</Trans>
</p>
</CardContent>
</Card>
@ -750,7 +785,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<CheckSquare className="h-4 w-4" />
Checkbox
<Trans>Checkbox</Trans>
</p>
</CardContent>
</Card>
@ -776,7 +811,7 @@ export const AddTemplateFieldsFormPartial = ({
)}
>
<ChevronDown className="h-4 w-4" />
Dropdown
<Trans>Dropdown</Trans>
</p>
</CardContent>
</Card>
@ -786,17 +821,32 @@ export const AddTemplateFieldsFormPartial = ({
</div>
</DocumentFlowFormContainerContent>
{hasErrors && (
<div className="mt-4">
<ul>
<li className="text-sm text-red-500">
<Trans>
To proceed further, please set at least one value for the{' '}
{emptyCheckboxFields.length > 0
? 'Checkbox'
: emptyRadioFields.length > 0
? 'Radio'
: 'Select'}{' '}
field.
</Trans>
</li>
</ul>
</div>
)}
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={isSubmitting}
disabled={isSubmitting}
goNextLabel="Save Template"
goNextLabel={msg`Save Template`}
disableNextStep={hasErrors}
onGoBackClick={() => {
previousStep();
remove();

View File

@ -5,6 +5,8 @@ import React, { useEffect, useId, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { motion } from 'framer-motion';
import { Link2Icon, Plus, Trash } from 'lucide-react';
import { useSession } from 'next-auth/react';
@ -67,6 +69,8 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
const { toast } = useToast();
const router = useRouter();
const initialId = useId();
const { _ } = useLingui();
const { data: session } = useSession();
const user = session?.user;
@ -290,13 +294,15 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
})}
>
{!showAdvancedSettings && index === 0 && (
<FormLabel required>Email</FormLabel>
<FormLabel required>
<Trans>Email</Trans>
</FormLabel>
)}
<FormControl>
<Input
type="email"
placeholder="Email"
placeholder={_(msg`Email`)}
{...field}
disabled={field.disabled || isSubmitting}
onBlur={() => void handleOnBlur(index)}
@ -318,11 +324,15 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
'col-span-4': showAdvancedSettings,
})}
>
{!showAdvancedSettings && index === 0 && <FormLabel>Name</FormLabel>}
{!showAdvancedSettings && index === 0 && (
<FormLabel>
<Trans>Name</Trans>
</FormLabel>
)}
<FormControl>
<Input
placeholder="Name"
placeholder={_(msg`Name`)}
{...field}
disabled={field.disabled || isSubmitting}
onBlur={() => void handleOnBlur(index)}
@ -382,12 +392,14 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<h3 className="text-foreground text-lg font-semibold">
Direct link receiver
<Trans>Direct link receiver</Trans>
</h3>
<p className="text-muted-foreground mt-1">
This field cannot be modified or deleted. When you share this template's
direct link or add it to your public profile, anyone who accesses it can
input their name and email, and fill in the fields assigned to them.
<Trans>
This field cannot be modified or deleted. When you share this template's
direct link or add it to your public profile, anyone who accesses it can
input their name and email, and fill in the fields assigned to them.
</Trans>
</p>
</TooltipContent>
</Tooltip>
@ -423,7 +435,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
onClick={() => onAddPlaceholderRecipient()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add Placeholder Recipient
<Trans>Add Placeholder Recipient</Trans>
</Button>
<Button
@ -437,7 +449,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
onClick={() => onAddPlaceholderSelfRecipient()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add Myself
<Trans>Add Myself</Trans>
</Button>
</div>
@ -455,7 +467,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
className="text-muted-foreground ml-2 text-sm"
htmlFor="showAdvancedRecipientSettings"
>
Show advanced settings
<Trans>Show advanced settings</Trans>
</label>
</div>
)}
@ -464,11 +476,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={isSubmitting}

View File

@ -3,6 +3,8 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
@ -72,6 +74,8 @@ export const AddTemplateSettingsFormPartial = ({
template,
onSubmit,
}: AddTemplateSettingsFormProps) => {
const { _ } = useLingui();
const { documentAuthOption } = extractDocumentAuthMethods({
documentAuth: template.authOptions,
});
@ -126,7 +130,9 @@ export const AddTemplateSettingsFormPartial = ({
name="title"
render={({ field }) => (
<FormItem>
<FormLabel required>Template title</FormLabel>
<FormLabel required>
<Trans>Template title</Trans>
</FormLabel>
<FormControl>
<Input className="bg-background" {...field} />
@ -142,7 +148,7 @@ export const AddTemplateSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
Document access
<Trans>Document access</Trans>
<DocumentGlobalAuthAccessTooltip />
</FormLabel>
@ -160,7 +166,7 @@ export const AddTemplateSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
Recipient action authentication
<Trans>Recipient action authentication</Trans>
<DocumentGlobalAuthActionTooltip />
</FormLabel>
@ -175,7 +181,7 @@ export const AddTemplateSettingsFormPartial = ({
<Accordion type="multiple">
<AccordionItem value="email-options" className="border-none">
<AccordionTrigger className="text-foreground rounded border px-3 py-2 text-left hover:bg-neutral-200/30 hover:no-underline">
Email Options
<Trans>Email Options</Trans>
</AccordionTrigger>
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-4 text-sm leading-relaxed [&>div]:pb-0">
@ -186,7 +192,9 @@ export const AddTemplateSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel>
Subject <span className="text-muted-foreground">(Optional)</span>
<Trans>
Subject <span className="text-muted-foreground">(Optional)</span>
</Trans>
</FormLabel>
<FormControl>
@ -204,7 +212,9 @@ export const AddTemplateSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel>
Message <span className="text-muted-foreground">(Optional)</span>
<Trans>
Message <span className="text-muted-foreground">(Optional)</span>
</Trans>
</FormLabel>
<FormControl>
@ -225,7 +235,7 @@ export const AddTemplateSettingsFormPartial = ({
<Accordion type="multiple">
<AccordionItem value="advanced-options" className="border-none">
<AccordionTrigger className="text-foreground rounded border px-3 py-2 text-left hover:bg-neutral-200/30 hover:no-underline">
Advanced Options
<Trans>Advanced Options</Trans>
</AccordionTrigger>
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-4 text-sm leading-relaxed">
@ -236,15 +246,17 @@ export const AddTemplateSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
External ID{' '}
<Trans>External ID</Trans>{' '}
<Tooltip>
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground max-w-xs">
Add an external ID to the template. This can be used to identify in
external systems.
<Trans>
Add an external ID to the template. This can be used to identify
in external systems.
</Trans>
</TooltipContent>
</Tooltip>
</FormLabel>
@ -263,7 +275,9 @@ export const AddTemplateSettingsFormPartial = ({
name="meta.dateFormat"
render={({ field }) => (
<FormItem>
<FormLabel>Date Format</FormLabel>
<FormLabel>
<Trans>Date Format</Trans>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
@ -291,7 +305,9 @@ export const AddTemplateSettingsFormPartial = ({
name="meta.timezone"
render={({ field }) => (
<FormItem>
<FormLabel>Time Zone</FormLabel>
<FormLabel>
<Trans>Time Zone</Trans>
</FormLabel>
<FormControl>
<Combobox
@ -313,14 +329,16 @@ export const AddTemplateSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
Redirect URL{' '}
<Trans>Redirect URL</Trans>{' '}
<Tooltip>
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground max-w-xs">
Add a URL to redirect the user to once the document is signed
<Trans>
Add a URL to redirect the user to once the document is signed
</Trans>
</TooltipContent>
</Tooltip>
</FormLabel>
@ -342,11 +360,7 @@ export const AddTemplateSettingsFormPartial = ({
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={form.formState.isSubmitting}