chore: merge main

This commit is contained in:
Catalin Pit
2024-08-29 12:37:19 +03:00
310 changed files with 16193 additions and 3045 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,
Check,
@ -24,7 +25,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
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,
@ -506,8 +507,8 @@ export const AddFieldsFormPartial = ({
<>
{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}
@ -609,14 +610,15 @@ export const AddFieldsFormPartial = ({
<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 && (
@ -624,7 +626,7 @@ export const AddFieldsFormPartial = ({
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>
)}
@ -680,8 +682,10 @@ export const AddFieldsFormPartial = ({
</TooltipTrigger>
<TooltipContent className="text-muted-foreground max-w-xs">
This document has already been sent to this recipient. You can
no longer edit this recipient.
<Trans>
This document has already been sent to this recipient. You
can no longer edit this recipient.
</Trans>
</TooltipContent>
</Tooltip>
)}
@ -717,7 +721,7 @@ export const AddFieldsFormPartial = ({
fontCaveat.className,
)}
>
Signature
<Trans>Signature</Trans>
</p>
</CardContent>
</Card>
@ -769,7 +773,7 @@ export const AddFieldsFormPartial = ({
)}
>
<Mail className="h-4 w-4" />
Email
<Trans>Email</Trans>
</p>
</CardContent>
</Card>
@ -795,7 +799,7 @@ export const AddFieldsFormPartial = ({
)}
>
<User className="h-4 w-4" />
Name
<Trans>Name</Trans>
</p>
</CardContent>
</Card>
@ -821,7 +825,7 @@ export const AddFieldsFormPartial = ({
)}
>
<CalendarDays className="h-4 w-4" />
Date
<Trans>Date</Trans>
</p>
</CardContent>
</Card>
@ -847,7 +851,7 @@ export const AddFieldsFormPartial = ({
)}
>
<Type className="h-4 w-4" />
Text
<Trans>Text</Trans>
</p>
</CardContent>
</Card>
@ -873,7 +877,7 @@ export const AddFieldsFormPartial = ({
)}
>
<Hash className="h-4 w-4" />
Number
<Trans>Number</Trans>
</p>
</CardContent>
</Card>
@ -899,7 +903,7 @@ export const AddFieldsFormPartial = ({
)}
>
<Disc className="h-4 w-4" />
Radio
<Trans>Radio</Trans>
</p>
</CardContent>
</Card>
@ -925,7 +929,7 @@ export const AddFieldsFormPartial = ({
)}
>
<CheckSquare className="h-4 w-4" />
Checkbox
<Trans>Checkbox</Trans>
</p>
</CardContent>
</Card>
@ -951,7 +955,7 @@ export const AddFieldsFormPartial = ({
)}
>
<ChevronDown className="h-4 w-4" />
Dropdown
<Trans>Dropdown</Trans>
</p>
</CardContent>
</Card>
@ -964,23 +968,21 @@ export const AddFieldsFormPartial = ({
<div className="mt-4">
<ul>
<li className="text-sm text-red-500">
To proceed further, please set at least one value for the{' '}
{emptyCheckboxFields.length > 0
? 'Checkbox'
: emptyRadioFields.length > 0
? 'Radio'
: 'Select'}{' '}
field.
<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}
@ -991,7 +993,7 @@ export const AddFieldsFormPartial = ({
remove();
documentFlow.onBackStep?.();
}}
goBackLabel={canRenderBackButtonAsRemove ? 'Remove' : undefined}
goBackLabel={canRenderBackButtonAsRemove ? msg`Remove` : undefined}
onGoNextClick={handleGoNextClick}
/>
</DocumentFlowFormContainerFooter>

View File

@ -3,6 +3,7 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/macro';
import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
@ -140,7 +141,9 @@ export const AddSettingsFormPartial = ({
name="title"
render={({ field }) => (
<FormItem>
<FormLabel required>Title</FormLabel>
<FormLabel required>
<Trans>Title</Trans>
</FormLabel>
<FormControl>
<Input
@ -160,7 +163,7 @@ export const AddSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
Document access
<Trans>Document access</Trans>
<DocumentGlobalAuthAccessTooltip />
</FormLabel>
@ -178,7 +181,7 @@ export const AddSettingsFormPartial = ({
render={({ field }) => (
<FormItem>
<FormLabel className="flex flex-row items-center">
Recipient action authentication
<Trans>Recipient action authentication</Trans>
<DocumentGlobalAuthActionTooltip />
</FormLabel>
@ -193,7 +196,7 @@ export const AddSettingsFormPartial = ({
<Accordion type="multiple" className="mt-6">
<AccordionItem value="advanced-options" className="border-none">
<AccordionTrigger className="text-foreground mb-2 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-2 text-sm leading-relaxed">
@ -204,15 +207,17 @@ export const AddSettingsFormPartial = ({
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 document. This can be used to identify the
document in external systems.
<Trans>
Add an external ID to the document. This can be used to identify
the document in external systems.
</Trans>
</TooltipContent>
</Tooltip>
</FormLabel>
@ -231,7 +236,9 @@ export const AddSettingsFormPartial = ({
name="meta.dateFormat"
render={({ field }) => (
<FormItem>
<FormLabel>Date Format</FormLabel>
<FormLabel>
<Trans>Date Format</Trans>
</FormLabel>
<FormControl>
<Select
@ -263,7 +270,9 @@ export const AddSettingsFormPartial = ({
name="meta.timezone"
render={({ field }) => (
<FormItem>
<FormLabel>Time Zone</FormLabel>
<FormLabel>
<Trans>Time Zone</Trans>
</FormLabel>
<FormControl>
<Combobox
@ -286,14 +295,16 @@ export const AddSettingsFormPartial = ({
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>
@ -315,11 +326,7 @@ export const AddSettingsFormPartial = ({
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={form.formState.isSubmitting}

View File

@ -3,6 +3,7 @@
import { useMemo, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/macro';
import { DateTime } from 'luxon';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
@ -267,7 +268,9 @@ export const AddSignatureFormPartial = ({
name="email"
render={({ field }) => (
<FormItem>
<FormLabel required>Email</FormLabel>
<FormLabel required>
<Trans>Email</Trans>
</FormLabel>
<FormControl>
<Input
className="bg-background"
@ -291,7 +294,9 @@ export const AddSignatureFormPartial = ({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel required={requireName}>Name</FormLabel>
<FormLabel required={requireName}>
<Trans>Name</Trans>
</FormLabel>
<FormControl>
<Input
className="bg-background"
@ -314,7 +319,9 @@ export const AddSignatureFormPartial = ({
name="signature"
render={({ field }) => (
<FormItem>
<FormLabel required={requireSignature}>Signature</FormLabel>
<FormLabel required={requireSignature}>
<Trans>Signature</Trans>
</FormLabel>
<FormControl>
<Card
className={cn('mt-2', {
@ -349,7 +356,9 @@ export const AddSignatureFormPartial = ({
name="customText"
render={({ field }) => (
<FormItem>
<FormLabel required={requireCustomText}>Custom Text</FormLabel>
<FormLabel required={requireCustomText}>
<Trans>Custom Text</Trans>
</FormLabel>
<FormControl>
<Input
className="bg-background"
@ -369,11 +378,7 @@ export const AddSignatureFormPartial = ({
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={form.formState.isSubmitting}
@ -386,7 +391,7 @@ export const AddSignatureFormPartial = ({
{validateUninsertedFields && uninsertedFields[0] && (
<FieldToolTip key={uninsertedFields[0].id} field={uninsertedFields[0]} color="warning">
Click to insert field
<Trans>Click to insert field</Trans>
</FieldToolTip>
)}

View File

@ -3,6 +3,8 @@
import React, { useId, useMemo, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { motion } from 'framer-motion';
import { Plus, Trash } from 'lucide-react';
import { useSession } from 'next-auth/react';
@ -54,9 +56,11 @@ export const AddSignersFormPartial = ({
onSubmit,
isDocumentPdfLoaded,
}: AddSignersFormProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const { remaining } = useLimits();
const { data: session } = useSession();
const user = session?.user;
const initialId = useId();
@ -157,8 +161,8 @@ export const AddSignersFormPartial = ({
if (hasBeenSentToRecipientId(signer.nativeId)) {
toast({
title: 'Cannot remove signer',
description: 'This signer has already received the document.',
title: _(msg`Cannot remove signer`),
description: _(msg`This signer has already received the document.`),
variant: 'destructive',
});
@ -224,13 +228,15 @@ export const AddSignersFormPartial = ({
})}
>
{!showAdvancedSettings && index === 0 && (
<FormLabel required>Email</FormLabel>
<FormLabel required>
<Trans>Email</Trans>
</FormLabel>
)}
<FormControl>
<Input
type="email"
placeholder="Email"
placeholder={_(msg`Email`)}
{...field}
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
onKeyDown={onKeyDown}
@ -256,7 +262,7 @@ export const AddSignersFormPartial = ({
<FormControl>
<Input
placeholder="Name"
placeholder={_(msg`Name`)}
{...field}
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
onKeyDown={onKeyDown}
@ -339,7 +345,7 @@ export const AddSignersFormPartial = ({
onClick={() => onAddSigner()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add Signer
<Trans>Add Signer</Trans>
</Button>
<Button
@ -350,7 +356,7 @@ export const AddSignersFormPartial = ({
onClick={() => onAddSelfSigner()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add myself
<Trans>Add myself</Trans>
</Button>
</div>
@ -368,7 +374,7 @@ export const AddSignersFormPartial = ({
className="text-muted-foreground ml-2 text-sm"
htmlFor="showAdvancedRecipientSettings"
>
Show advanced settings
<Trans>Show advanced settings</Trans>
</label>
</div>
)}
@ -377,11 +383,7 @@ export const AddSignersFormPartial = ({
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={isSubmitting}

View File

@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, msg } from '@lingui/macro';
import { Loader } from 'lucide-react';
import { useForm } from 'react-hook-form';
@ -190,7 +191,9 @@ export const AddSubjectFormPartial = ({
<div className="flex flex-col gap-y-4">
<div>
<Label htmlFor="subject">
Subject <span className="text-muted-foreground">(Optional)</span>
<Trans>
Subject <span className="text-muted-foreground">(Optional)</span>
</Trans>
</Label>
<Input
@ -223,7 +226,9 @@ export const AddSubjectFormPartial = ({
<div>
<Label htmlFor="message">
Message <span className="text-muted-foreground">(Optional)</span>
<Trans>
Message <span className="text-muted-foreground">(Optional)</span>
</Trans>
</Label>
<Textarea
@ -262,16 +267,12 @@ export const AddSubjectFormPartial = ({
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter>
<DocumentFlowFormContainerStep
title={documentFlow.title}
step={currentStep}
maxStep={totalSteps}
/>
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
<DocumentFlowFormContainerActions
loading={isSubmitting}
disabled={isSubmitting}
goNextLabel={document.status === DocumentStatus.DRAFT ? 'Send' : 'Update'}
goNextLabel={document.status === DocumentStatus.DRAFT ? msg`Send` : msg`Update`}
onGoBackClick={previousStep}
onGoNextClick={() => void onFormSubmit()}
/>

View File

@ -3,6 +3,9 @@
import type { HTMLAttributes } from 'react';
import React from 'react';
import type { MessageDescriptor } from '@lingui/core';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { motion } from 'framer-motion';
import { cn } from '../../lib/utils';
@ -33,19 +36,21 @@ export const DocumentFlowFormContainer = ({
};
export type DocumentFlowFormContainerHeaderProps = {
title: string;
description: string;
title: MessageDescriptor;
description: MessageDescriptor;
};
export const DocumentFlowFormContainerHeader = ({
title,
description,
}: DocumentFlowFormContainerHeaderProps) => {
const { _ } = useLingui();
return (
<>
<h3 className="text-foreground text-2xl font-semibold">{title}</h3>
<h3 className="text-foreground text-2xl font-semibold">{_(title)}</h3>
<p className="text-muted-foreground mt-2 text-sm">{description}</p>
<p className="text-muted-foreground mt-2 text-sm">{_(description)}</p>
<hr className="border-border mb-8 mt-4" />
</>
@ -88,7 +93,6 @@ export const DocumentFlowFormContainerFooter = ({
};
export type DocumentFlowFormContainerStepProps = {
title: string;
step: number;
maxStep: number;
};
@ -100,7 +104,9 @@ export const DocumentFlowFormContainerStep = ({
return (
<div>
<p className="text-muted-foreground text-sm">
Step <span>{`${step} of ${maxStep}`}</span>
<Trans>
Step <span>{`${step} of ${maxStep}`}</span>
</Trans>
</p>
<div className="bg-muted relative mt-4 h-[2px] rounded-md">
@ -120,8 +126,8 @@ export const DocumentFlowFormContainerStep = ({
export type DocumentFlowFormContainerActionsProps = {
canGoBack?: boolean;
canGoNext?: boolean;
goNextLabel?: string;
goBackLabel?: string;
goNextLabel?: MessageDescriptor;
goBackLabel?: MessageDescriptor;
onGoBackClick?: () => void;
onGoNextClick?: () => void;
loading?: boolean;
@ -132,14 +138,15 @@ export type DocumentFlowFormContainerActionsProps = {
export const DocumentFlowFormContainerActions = ({
canGoBack = true,
canGoNext = true,
goNextLabel = 'Continue',
goBackLabel = 'Go Back',
goNextLabel = msg`Continue`,
goBackLabel = msg`Go Back`,
onGoBackClick,
onGoNextClick,
loading,
disabled,
disableNextStep = false,
}: DocumentFlowFormContainerActionsProps) => {
const { _ } = useLingui();
return (
<div className="mt-4 flex gap-x-4">
<Button
@ -150,7 +157,7 @@ export const DocumentFlowFormContainerActions = ({
disabled={disabled || loading || !canGoBack || !onGoBackClick}
onClick={onGoBackClick}
>
{goBackLabel}
{_(goBackLabel)}
</Button>
<Button
@ -161,7 +168,7 @@ export const DocumentFlowFormContainerActions = ({
loading={loading}
onClick={onGoNextClick}
>
{goNextLabel}
{_(goNextLabel)}
</Button>
</div>
);

View File

@ -1,3 +1,4 @@
import { Trans } from '@lingui/macro';
import {
CalendarDays,
CheckSquare,
@ -48,7 +49,7 @@ export const FieldIcon = ({
fontCaveatClassName,
)}
>
Signature
<Trans>Signature</Trans>
</div>
);
} else {

View File

@ -5,6 +5,9 @@ import { forwardRef, useEffect, useState } from 'react';
import { useParams } from 'next/navigation';
import { usePathname } from 'next/navigation';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { match } from 'ts-pattern';
import {
@ -37,8 +40,8 @@ import { TextFieldAdvancedSettings } from './field-items-advanced-settings/text-
export type FieldAdvancedSettingsProps = {
teamId?: number;
title: string;
description: string;
title: MessageDescriptor;
description: MessageDescriptor;
field: FieldFormType;
fields: FieldFormType[];
onAdvancedSettings?: () => void;
@ -121,7 +124,9 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
},
ref,
) => {
const { _ } = useLingui();
const { toast } = useToast();
const params = useParams();
const pathname = usePathname();
const id = params?.id;
@ -218,8 +223,8 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
console.error('Failed to save to localStorage:', error);
toast({
title: 'Error',
description: 'Failed to save settings.',
title: _(msg`Error`),
description: _(msg`Failed to save settings.`),
variant: 'destructive',
});
}
@ -288,8 +293,8 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter className="mt-auto">
<DocumentFlowFormContainerActions
goNextLabel="Save"
goBackLabel="Cancel"
goNextLabel={msg`Save`}
goBackLabel={msg`Cancel`}
onGoBackClick={onAdvancedSettings}
onGoNextClick={handleOnGoNextClick}
disableNextStep={errors.length > 0}

View File

@ -2,6 +2,8 @@
import { useEffect, useState } from 'react';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
@ -35,6 +37,8 @@ export const CheckboxFieldAdvancedSettings = ({
handleFieldChange,
handleErrors,
}: CheckboxFieldAdvancedSettingsProps) => {
const { _ } = useLingui();
const [showValidation, setShowValidation] = useState(false);
const [values, setValues] = useState(fieldState.values ?? [{ id: 1, checked: false, value: '' }]);
const [readOnly, setReadOnly] = useState(fieldState.readOnly ?? false);
@ -122,13 +126,15 @@ export const CheckboxFieldAdvancedSettings = ({
<div className="flex flex-col gap-4">
<div className="flex flex-row items-center gap-x-4">
<div className="flex w-2/3 flex-col">
<Label>Validation</Label>
<Label>
<Trans>Validation</Trans>
</Label>
<Select
value={fieldState.validationRule}
onValueChange={(val) => handleToggleChange('validationRule', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectValue placeholder="Select at least" />
<SelectValue placeholder={_(msg`Select at least`)} />
</SelectTrigger>
<SelectContent position="popper">
{checkboxValidationRules.map((item, index) => (
@ -145,7 +151,7 @@ export const CheckboxFieldAdvancedSettings = ({
onValueChange={(val) => handleToggleChange('validationLength', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectValue placeholder="Pick a number" />
<SelectValue placeholder={_(msg`Pick a number`)} />
</SelectTrigger>
<SelectContent position="popper">
{checkboxValidationLength.map((item, index) => (
@ -164,7 +170,9 @@ export const CheckboxFieldAdvancedSettings = ({
checked={fieldState.required}
onCheckedChange={(checked) => handleToggleChange('required', checked)}
/>
<Label>Required field</Label>
<Label>
<Trans>Required field</Trans>
</Label>
</div>
<div className="flex flex-row items-center gap-2">
<Switch
@ -172,7 +180,9 @@ export const CheckboxFieldAdvancedSettings = ({
checked={fieldState.readOnly}
onCheckedChange={(checked) => handleToggleChange('readOnly', checked)}
/>
<Label>Read only</Label>
<Label>
<Trans>Read only</Trans>
</Label>
</div>
</div>
<Button
@ -181,7 +191,9 @@ export const CheckboxFieldAdvancedSettings = ({
onClick={() => setShowValidation((prev) => !prev)}
>
<span className="flex w-full flex-row justify-between">
<span className="flex items-center">Checkbox values</span>
<span className="flex items-center">
<Trans>Checkbox values</Trans>
</span>
{showValidation ? <ChevronUp /> : <ChevronDown />}
</span>
</Button>
@ -215,7 +227,7 @@ export const CheckboxFieldAdvancedSettings = ({
variant="outline"
onClick={addValue}
>
Add another value
<Trans>Add another value</Trans>
</Button>
</div>
)}

View File

@ -2,6 +2,8 @@
import { useEffect, useState } from 'react';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
@ -32,6 +34,8 @@ export const DropdownFieldAdvancedSettings = ({
handleFieldChange,
handleErrors,
}: DropdownFieldAdvancedSettingsProps) => {
const { _ } = useLingui();
const [showValidation, setShowValidation] = useState(false);
const [values, setValues] = useState(fieldState.values ?? [{ value: 'Option 1' }]);
const [readOnly, setReadOnly] = useState(fieldState.readOnly ?? false);
@ -87,7 +91,9 @@ export const DropdownFieldAdvancedSettings = ({
return (
<div className="text-dark flex flex-col gap-4">
<div>
<Label>Select default option</Label>
<Label>
<Trans>Select default option</Trans>
</Label>
<Select
defaultValue={defaultValue}
onValueChange={(val) => {
@ -96,7 +102,7 @@ export const DropdownFieldAdvancedSettings = ({
}}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectValue defaultValue={defaultValue} placeholder="-- Select --" />
<SelectValue defaultValue={defaultValue} placeholder={`-- ${_(msg`Select`)} --`} />
</SelectTrigger>
<SelectContent position="popper">
{values.map((item, index) => (
@ -117,7 +123,9 @@ export const DropdownFieldAdvancedSettings = ({
checked={fieldState.required}
onCheckedChange={(checked) => handleToggleChange('required', checked)}
/>
<Label>Required field</Label>
<Label>
<Trans>Required field</Trans>
</Label>
</div>
<div className="flex flex-row items-center gap-2">
<Switch
@ -125,7 +133,9 @@ export const DropdownFieldAdvancedSettings = ({
checked={fieldState.readOnly}
onCheckedChange={(checked) => handleToggleChange('readOnly', checked)}
/>
<Label>Read only</Label>
<Label>
<Trans>Read only</Trans>
</Label>
</div>
</div>
<Button
@ -134,7 +144,9 @@ export const DropdownFieldAdvancedSettings = ({
onClick={() => setShowValidation((prev) => !prev)}
>
<span className="flex w-full flex-row justify-between">
<span className="flex items-center">Dropdown options</span>
<span className="flex items-center">
<Trans>Dropdown options</Trans>
</span>
{showValidation ? <ChevronUp /> : <ChevronDown />}
</span>
</Button>
@ -162,7 +174,7 @@ export const DropdownFieldAdvancedSettings = ({
variant="outline"
onClick={addValue}
>
Add another option
<Trans>Add another option</Trans>
</Button>
</div>
)}

View File

@ -2,6 +2,8 @@
import { useState } from 'react';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
@ -31,6 +33,8 @@ export const NumberFieldAdvancedSettings = ({
handleFieldChange,
handleErrors,
}: NumberFieldAdvancedSettingsProps) => {
const { _ } = useLingui();
const [showValidation, setShowValidation] = useState(false);
const handleInput = (field: keyof NumberFieldMeta, value: string | boolean) => {
@ -56,43 +60,51 @@ export const NumberFieldAdvancedSettings = ({
return (
<div className="flex flex-col gap-4">
<div>
<Label>Label</Label>
<Label>
<Trans>Label</Trans>
</Label>
<Input
id="label"
className="bg-background mt-2"
placeholder="Label"
placeholder={_(msg`Label`)}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
/>
</div>
<div>
<Label className="mt-4">Placeholder</Label>
<Label className="mt-4">
<Trans>Placeholder</Trans>
</Label>
<Input
id="placeholder"
className="bg-background mt-2"
placeholder="Placeholder"
placeholder={_(msg`Placeholder`)}
value={fieldState.placeholder}
onChange={(e) => handleFieldChange('placeholder', e.target.value)}
/>
</div>
<div>
<Label className="mt-4">Value</Label>
<Label className="mt-4">
<Trans>Value</Trans>
</Label>
<Input
id="value"
className="bg-background mt-2"
placeholder="Value"
placeholder={_(msg`Value`)}
value={fieldState.value}
onChange={(e) => handleInput('value', e.target.value)}
/>
</div>
<div>
<Label>Number format</Label>
<Label>
<Trans>Number format</Trans>
</Label>
<Select
value={fieldState.numberFormat}
onValueChange={(val) => handleInput('numberFormat', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectValue placeholder="Field format" />
<SelectValue placeholder={_(msg`Field format`)} />
</SelectTrigger>
<SelectContent position="popper">
{numberFormatValues.map((item, index) => (
@ -110,7 +122,9 @@ export const NumberFieldAdvancedSettings = ({
checked={fieldState.required}
onCheckedChange={(checked) => handleInput('required', checked)}
/>
<Label>Required field</Label>
<Label>
<Trans>Required field</Trans>
</Label>
</div>
<div className="flex flex-row items-center gap-2">
<Switch
@ -118,7 +132,9 @@ export const NumberFieldAdvancedSettings = ({
checked={fieldState.readOnly}
onCheckedChange={(checked) => handleInput('readOnly', checked)}
/>
<Label>Read only</Label>
<Label>
<Trans>Read only</Trans>
</Label>
</div>
</div>
<Button
@ -127,14 +143,18 @@ export const NumberFieldAdvancedSettings = ({
onClick={() => setShowValidation((prev) => !prev)}
>
<span className="flex w-full flex-row justify-between">
<span className="flex items-center">Validation</span>
<span className="flex items-center">
<Trans>Validation</Trans>
</span>
{showValidation ? <ChevronUp /> : <ChevronDown />}
</span>
</Button>
{showValidation && (
<div className="mb-4 flex flex-row gap-x-4">
<div className="flex flex-col">
<Label className="mt-4">Min</Label>
<Label className="mt-4">
<Trans>Min</Trans>
</Label>
<Input
id="minValue"
className="bg-background mt-2"
@ -144,7 +164,9 @@ export const NumberFieldAdvancedSettings = ({
/>
</div>
<div className="flex flex-col">
<Label className="mt-4">Max</Label>
<Label className="mt-4">
<Trans>Max</Trans>
</Label>
<Input
id="maxValue"
className="bg-background mt-2"

View File

@ -2,6 +2,7 @@
import { useEffect, useState } from 'react';
import { Trans } from '@lingui/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { validateRadioField } from '@documenso/lib/advanced-fields-validation/validate-radio';
@ -107,7 +108,9 @@ export const RadioFieldAdvancedSettings = ({
checked={fieldState.required}
onCheckedChange={(checked) => handleToggleChange('required', checked)}
/>
<Label>Required field</Label>
<Label>
<Trans>Required field</Trans>
</Label>
</div>
<div className="flex flex-row items-center gap-2">
<Switch
@ -115,7 +118,9 @@ export const RadioFieldAdvancedSettings = ({
checked={fieldState.readOnly}
onCheckedChange={(checked) => handleToggleChange('readOnly', checked)}
/>
<Label>Read only</Label>
<Label>
<Trans>Read only</Trans>
</Label>
</div>
</div>
<Button
@ -124,7 +129,9 @@ export const RadioFieldAdvancedSettings = ({
onClick={() => setShowValidation((prev) => !prev)}
>
<span className="flex w-full flex-row justify-between">
<span className="flex items-center">Radio values</span>
<span className="flex items-center">
<Trans>Radio values</Trans>
</span>
{showValidation ? <ChevronUp /> : <ChevronDown />}
</span>
</Button>
@ -157,7 +164,7 @@ export const RadioFieldAdvancedSettings = ({
variant="outline"
onClick={addValue}
>
Add another value
<Trans>Add another value</Trans>
</Button>
</div>
)}

View File

@ -1,3 +1,6 @@
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { validateTextField } from '@documenso/lib/advanced-fields-validation/validate-text';
import { type TTextFieldMeta as TextFieldMeta } from '@documenso/lib/types/field-meta';
import { Input } from '@documenso/ui/primitives/input';
@ -16,6 +19,8 @@ export const TextFieldAdvancedSettings = ({
handleFieldChange,
handleErrors,
}: TextFieldAdvancedSettingsProps) => {
const { _ } = useLingui();
const handleInput = (field: keyof TextFieldMeta, value: string | boolean) => {
const text = field === 'text' ? String(value) : fieldState.text || '';
const limit =
@ -36,45 +41,53 @@ export const TextFieldAdvancedSettings = ({
return (
<div className="flex flex-col gap-4">
<div>
<Label>Label</Label>
<Label>
<Trans>Label</Trans>
</Label>
<Input
id="label"
className="bg-background mt-2"
placeholder="Field label"
placeholder={_(msg`Field label`)}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
/>
</div>
<div>
<Label className="mt-4">Placeholder</Label>
<Label className="mt-4">
<Trans>Placeholder</Trans>
</Label>
<Input
id="placeholder"
className="bg-background mt-2"
placeholder="Field placeholder"
placeholder={_(msg`Field placeholder`)}
value={fieldState.placeholder}
onChange={(e) => handleFieldChange('placeholder', e.target.value)}
/>
</div>
<div>
<Label className="mt-4">Add text</Label>
<Label className="mt-4">
<Trans>Add text</Trans>
</Label>
<Textarea
id="text"
className="bg-background mt-2"
placeholder="Add text to the field"
placeholder={_(msg`Add text to the field`)}
value={fieldState.text}
onChange={(e) => handleInput('text', e.target.value)}
/>
</div>
<div>
<Label>Character Limit</Label>
<Label>
<Trans>Character Limit</Trans>
</Label>
<Input
id="characterLimit"
type="number"
min={0}
className="bg-background mt-2"
placeholder="Field character limit"
placeholder={_(msg`Field character limit`)}
value={fieldState.characterLimit}
onChange={(e) => handleInput('characterLimit', e.target.value)}
/>
@ -87,7 +100,9 @@ export const TextFieldAdvancedSettings = ({
checked={fieldState.required}
onCheckedChange={(checked) => handleInput('required', checked)}
/>
<Label>Required field</Label>
<Label>
<Trans>Required field</Trans>
</Label>
</div>
<div className="flex flex-row items-center gap-2">
<Switch
@ -95,7 +110,9 @@ export const TextFieldAdvancedSettings = ({
checked={fieldState.readOnly}
onCheckedChange={(checked) => handleInput('readOnly', checked)}
/>
<Label>Read only</Label>
<Label>
<Trans>Read only</Trans>
</Label>
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
'use client';
import { Trans } from '@lingui/macro';
import { DialogClose } from '@radix-ui/react-dialog';
import { Button } from '@documenso/ui/primitives/button';
@ -25,18 +26,22 @@ export const MissingSignatureFieldDialog = ({
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="max-w-lg" position="center">
<DialogHeader>
<DialogTitle>No signature field found</DialogTitle>
<DialogTitle>
<Trans>No signature field found</Trans>
</DialogTitle>
<DialogDescription>
<p className="mt-2">
Some signers have not been assigned a signature field. Please assign at least 1
signature field to each signer before proceeding.
<Trans>
Some signers have not been assigned a signature field. Please assign at least 1
signature field to each signer before proceeding.
</Trans>
</p>
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button type="button" variant="secondary">
Close
<Trans>Close</Trans>
</Button>
</DialogClose>
</DialogFooter>

View File

@ -1,5 +1,6 @@
import { useState } from 'react';
import { Trans } from '@lingui/macro';
import { Loader } from 'lucide-react';
import type { ButtonProps } from '../button';
@ -30,16 +31,20 @@ export const SendDocumentActionDialog = ({
<DialogTrigger asChild>
<Button type="button" className={className}>
{loading && <Loader className="text-documenso mr-2 h-5 w-5 animate-spin" />}
Send
<Trans>Send</Trans>
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="text-center text-lg font-semibold">Send Document</DialogTitle>
<DialogTitle className="text-center text-lg font-semibold">
<Trans>Send Document</Trans>
</DialogTitle>
<DialogDescription className="text-center text-base">
You are about to send this document to the recipients. Are you sure you want to
continue?
<Trans>
You are about to send this document to the recipients. Are you sure you want to
continue?
</Trans>
</DialogDescription>
</DialogHeader>
@ -50,13 +55,13 @@ export const SendDocumentActionDialog = ({
variant="secondary"
onClick={() => setOpen(false)}
>
Cancel
<Trans>Cancel</Trans>
</Button>
{/* We would use DialogAction here but it interrupts the submit action */}
<Button className={className} {...props}>
{loading && <Loader className="mr-2 h-5 w-5 animate-spin" />}
Send
<Trans>Send</Trans>
</Button>
</DialogFooter>
</DialogContent>

View File

@ -1,3 +1,4 @@
import type { MessageDescriptor } from '@lingui/core';
import { z } from 'zod';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
@ -58,8 +59,8 @@ export const FRIENDLY_FIELD_TYPE: Record<FieldType, string> = {
};
export interface DocumentFlowStep {
title: string;
description: string;
title: MessageDescriptor;
description: MessageDescriptor;
stepIndex?: number;
onBackStep?: () => unknown;
onNextStep?: () => unknown;