fix: lint project (#2693)

This commit is contained in:
David Nguyen
2026-05-08 16:04:22 +10:00
committed by GitHub
parent edbf65969b
commit 8671f269e8
1414 changed files with 12867 additions and 24335 deletions
@@ -6,11 +6,7 @@ type AnimateGenericFadeInOutProps = {
motionKey?: string;
};
export const AnimateGenericFadeInOut = ({
children,
className,
motionKey,
}: AnimateGenericFadeInOutProps) => {
export const AnimateGenericFadeInOut = ({ children, className, motionKey }: AnimateGenericFadeInOutProps) => {
return (
<motion.section
key={motionKey}
+1 -1
View File
@@ -6,5 +6,5 @@ type ClientOnlyProps = {
};
export const ClientOnly = ({ children, fallback = null }: ClientOnlyProps) => {
return useHydrated() ? <>{children()}</> : <>{fallback}</>;
return useHydrated() ? children() : fallback;
};
@@ -1,11 +1,8 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { AnimatePresence } from 'framer-motion';
import { CheckSquareIcon, CopyIcon } from 'lucide-react';
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { Button } from '@documenso/ui/primitives/button';
import { AnimatePresence, motion } from 'framer-motion';
import { CheckSquareIcon, CopyIcon } from 'lucide-react';
import { useState } from 'react';
import { cn } from '../../lib/utils';
@@ -66,11 +63,7 @@ export const CopyTextButton = ({
)}
>
<div className="absolute">
{copiedTimeout ? (
<CheckSquareIcon className="h-3.5 w-3.5" />
) : (
<CopyIcon className="h-3.5 w-3.5" />
)}
{copiedTimeout ? <CheckSquareIcon className="h-3.5 w-3.5" /> : <CopyIcon className="h-3.5 w-3.5" />}
</div>
</div>
</motion.div>
@@ -1,17 +1,10 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { CheckIcon } from 'lucide-react';
import { SUPPORTED_LANGUAGES } from '@documenso/lib/constants/i18n';
import { dynamicActivate } from '@documenso/lib/utils/i18n';
import { cn } from '@documenso/ui/lib/utils';
import {
CommandDialog,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@documenso/ui/primitives/command';
import { CommandDialog, CommandGroup, CommandInput, CommandItem, CommandList } from '@documenso/ui/primitives/command';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { CheckIcon } from 'lucide-react';
type LanguageSwitcherDialogProps = {
open: boolean;
@@ -48,12 +41,7 @@ export const LanguageSwitcherDialog = ({ open, setOpen }: LanguageSwitcherDialog
value={_(language.full)}
onSelect={async () => setLanguage(language.short)}
>
<CheckIcon
className={cn(
'mr-2 h-4 w-4',
i18n.locale === language.short ? 'opacity-100' : 'opacity-0',
)}
/>
<CheckIcon className={cn('mr-2 h-4 w-4', i18n.locale === language.short ? 'opacity-100' : 'opacity-0')} />
{_(language.full)}
</CommandItem>
))}
+2 -8
View File
@@ -1,7 +1,6 @@
import { useCallback, useMemo, useRef, useState } from 'react';
import { CheckIcon, CopyIcon } from 'lucide-react';
import { DateTime } from 'luxon';
import { useCallback, useMemo, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
import { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover';
@@ -86,12 +85,7 @@ export const LocalTime = ({ date, className }: LocalTimeProps) => {
isCopied={copiedField === 'Local'}
onCopy={() => void onCopy('Local', local)}
/>
<TimeRow
label="UTC"
value={utc}
isCopied={copiedField === 'UTC'}
onCopy={() => void onCopy('UTC', utc)}
/>
<TimeRow label="UTC" value={utc} isCopied={copiedField === 'UTC'} onCopy={() => void onCopy('UTC', utc)} />
<TimeRow
label="Unix"
value={unix}
@@ -1,9 +1,8 @@
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import type { TDocumentEmailSettings } from '@documenso/lib/types/document-email';
import { DocumentEmailEvents } from '@documenso/lib/types/document-email';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Checkbox } from '../../primitives/checkbox';
@@ -16,11 +15,7 @@ type DocumentEmailCheckboxesProps = {
className?: string;
};
export const DocumentEmailCheckboxes = ({
value,
onChange,
className,
}: DocumentEmailCheckboxesProps) => {
export const DocumentEmailCheckboxes = ({ value, onChange, className }: DocumentEmailCheckboxesProps) => {
return (
<div className={cn('space-y-3', className)}>
<div className="flex flex-row items-center">
@@ -28,13 +23,11 @@ export const DocumentEmailCheckboxes = ({
id={DocumentEmailEvents.RecipientSigned}
className="h-5 w-5"
checked={value.recipientSigned}
onCheckedChange={(checked) =>
onChange({ ...value, [DocumentEmailEvents.RecipientSigned]: Boolean(checked) })
}
onCheckedChange={(checked) => onChange({ ...value, [DocumentEmailEvents.RecipientSigned]: Boolean(checked) })}
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.RecipientSigned}
>
<Trans>Email the owner when a recipient signs</Trans>
@@ -52,9 +45,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This email is sent to the document owner when a recipient has signed the document.
</Trans>
<Trans>This email is sent to the document owner when a recipient has signed the document.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -72,7 +63,7 @@ export const DocumentEmailCheckboxes = ({
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.RecipientSigningRequest}
>
<Trans>Email recipients with a signing request</Trans>
@@ -90,9 +81,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This email is sent to the recipient requesting them to sign the document.
</Trans>
<Trans>This email is sent to the recipient requesting them to sign the document.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -110,7 +99,7 @@ export const DocumentEmailCheckboxes = ({
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.RecipientRemoved}
>
<Trans>Email recipients when they're removed from a pending document</Trans>
@@ -128,9 +117,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This email is sent to the recipient if they are removed from a pending document.
</Trans>
<Trans>This email is sent to the recipient if they are removed from a pending document.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -142,13 +129,11 @@ export const DocumentEmailCheckboxes = ({
id={DocumentEmailEvents.DocumentPending}
className="h-5 w-5"
checked={value.documentPending}
onCheckedChange={(checked) =>
onChange({ ...value, [DocumentEmailEvents.DocumentPending]: Boolean(checked) })
}
onCheckedChange={(checked) => onChange({ ...value, [DocumentEmailEvents.DocumentPending]: Boolean(checked) })}
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.DocumentPending}
>
<Trans>Email the signer if the document is still pending</Trans>
@@ -167,8 +152,8 @@ export const DocumentEmailCheckboxes = ({
<p>
<Trans>
This email will be sent to the recipient who has just signed the document, if
there are still other recipients who have not signed yet.
This email will be sent to the recipient who has just signed the document, if there are still other
recipients who have not signed yet.
</Trans>
</p>
</TooltipContent>
@@ -187,7 +172,7 @@ export const DocumentEmailCheckboxes = ({
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.DocumentCompleted}
>
<Trans>Email recipients when the document is completed</Trans>
@@ -205,9 +190,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This will be sent to all recipients once the document has been fully completed.
</Trans>
<Trans>This will be sent to all recipients once the document has been fully completed.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -219,13 +202,11 @@ export const DocumentEmailCheckboxes = ({
id={DocumentEmailEvents.DocumentDeleted}
className="h-5 w-5"
checked={value.documentDeleted}
onCheckedChange={(checked) =>
onChange({ ...value, [DocumentEmailEvents.DocumentDeleted]: Boolean(checked) })
}
onCheckedChange={(checked) => onChange({ ...value, [DocumentEmailEvents.DocumentDeleted]: Boolean(checked) })}
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.DocumentDeleted}
>
<Trans>Email recipients when a pending document is deleted</Trans>
@@ -243,9 +224,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This will be sent to all recipients if a pending document has been deleted.
</Trans>
<Trans>This will be sent to all recipients if a pending document has been deleted.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -263,7 +242,7 @@ export const DocumentEmailCheckboxes = ({
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.OwnerDocumentCompleted}
>
<Trans>Email the owner when the document is completed</Trans>
@@ -281,10 +260,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This will be sent to the document owner once the document has been fully
completed.
</Trans>
<Trans>This will be sent to the document owner once the document has been fully completed.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -302,7 +278,7 @@ export const DocumentEmailCheckboxes = ({
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.OwnerDocumentCreated}
>
<Trans>Email the owner when a document is created from a direct template</Trans>
@@ -321,8 +297,8 @@ export const DocumentEmailCheckboxes = ({
<p>
<Trans>
This email is sent to the document owner when a recipient creates a document via a
direct template link.
This email is sent to the document owner when a recipient creates a document via a direct template
link.
</Trans>
</p>
</TooltipContent>
@@ -341,7 +317,7 @@ export const DocumentEmailCheckboxes = ({
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={DocumentEmailEvents.OwnerRecipientExpired}
>
<Trans>Send recipient expired email to the owner</Trans>
@@ -359,10 +335,7 @@ export const DocumentEmailCheckboxes = ({
</h2>
<p>
<Trans>
This will be sent to the document owner when a recipient's signing window has
expired.
</Trans>
<Trans>This will be sent to the document owner when a recipient's signing window has expired.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -1,14 +1,11 @@
import React from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
import { MultiSelect, type Option } from '@documenso/ui/primitives/multiselect';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
export interface DocumentGlobalAuthAccessSelectProps {
value?: string[];
@@ -41,15 +38,11 @@ export const DocumentGlobalAuthAccessSelect = ({
// Convert string array to Option array for MultiSelect
const selectedOptions =
(value
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
(value?.map((val) => authOptions.find((option) => option.value === val)).filter(Boolean) as Option[]) || [];
// Convert default value to Option array
const defaultOptions =
(defaultValue
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
(defaultValue?.map((val) => authOptions.find((option) => option.value === val)).filter(Boolean) as Option[]) || [];
const handleChange = (options: Option[]) => {
const values = options.map((option) => option.value);
@@ -102,8 +95,7 @@ export const DocumentGlobalAuthAccessTooltip = () => (
</li>
<li>
<Trans>
<strong>No restrictions</strong> - The document can be accessed directly by the URL sent
to the recipient
<strong>No restrictions</strong> - The document can be accessed directly by the URL sent to the recipient
</Trans>
</li>
</ul>
@@ -1,12 +1,11 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
import { DocumentActionAuth, DocumentAuth } from '@documenso/lib/types/document-auth';
import { MultiSelect, type Option } from '@documenso/ui/primitives/multiselect';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
export interface DocumentGlobalAuthActionSelectProps {
value?: string[];
@@ -41,15 +40,11 @@ export const DocumentGlobalAuthActionSelect = ({
// Convert string array to Option array for MultiSelect
const selectedOptions =
(value
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
(value?.map((val) => authOptions.find((option) => option.value === val)).filter(Boolean) as Option[]) || [];
// Convert default value to Option array
const defaultOptions =
(defaultValue
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
(defaultValue?.map((val) => authOptions.find((option) => option.value === val)).filter(Boolean) as Option[]) || [];
const handleChange = (options: Option[]) => {
const values = options.map((option) => option.value);
@@ -85,36 +80,33 @@ export const DocumentGlobalAuthActionTooltip = () => (
</h2>
<p>
<Trans>
The authentication methods required for recipients to sign the signature field.
</Trans>
<Trans>The authentication methods required for recipients to sign the signature field.</Trans>
</p>
<p>
<Trans>
These can be overriden by setting the authentication requirements directly on each
recipient in the next step. Multiple methods can be selected.
These can be overriden by setting the authentication requirements directly on each recipient in the next step.
Multiple methods can be selected.
</Trans>
</p>
<ul className="ml-3.5 list-outside list-disc space-y-0.5 py-2">
<li>
<Trans>
<strong>Require passkey</strong> - The recipient must have an account and passkey
configured via their settings
<strong>Require passkey</strong> - The recipient must have an account and passkey configured via their
settings
</Trans>
</li>
<li>
<Trans>
<strong>Require 2FA</strong> - The recipient must have an account and 2FA enabled via
their settings
<strong>Require 2FA</strong> - The recipient must have an account and 2FA enabled via their settings
</Trans>
</li>
<li>
<Trans>
<strong>Require password</strong> - The recipient must have an account and password
configured via their settings, the password will be verified during signing
<strong>Require password</strong> - The recipient must have an account and password configured via their
settings, the password will be verified during signing
</Trans>
</li>
@@ -1,11 +1,3 @@
import { useState } from 'react';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentMeta, Field } from '@prisma/client';
import { SigningStatus } from '@prisma/client';
import { Clock, EyeOffIcon } from 'lucide-react';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { isTemplateRecipientEmailPlaceholder } from '@documenso/lib/constants/template';
import type { TRecipientLite } from '@documenso/lib/types/recipient';
@@ -18,6 +10,12 @@ import { Badge } from '@documenso/ui/primitives/badge';
import { FRIENDLY_FIELD_TYPE } from '@documenso/ui/primitives/document-flow/types';
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { PopoverHover } from '@documenso/ui/primitives/popover';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentMeta, Field } from '@prisma/client';
import { SigningStatus } from '@prisma/client';
import { Clock, EyeOffIcon } from 'lucide-react';
import { useState } from 'react';
import { getRecipientColorStyles } from '../../lib/recipient-colors';
import { FieldContent } from '../../primitives/document-flow/field-content';
@@ -67,10 +65,7 @@ export type DocumentReadOnlyFieldsProps = {
showRecipientColors?: boolean;
};
export const mapFieldsWithRecipients = (
fields: Field[],
recipients: TRecipientLite[],
): DocumentField[] => {
export const mapFieldsWithRecipients = (fields: Field[], recipients: TRecipientLite[]): DocumentField[] => {
return fields.map((field) => {
const recipient = recipients.find((recipient) => recipient.id === field.recipientId) || {
id: field.recipientId,
@@ -110,18 +105,16 @@ export const DocumentReadOnlyFields = ({
readonly={true}
color={
showRecipientColors
? getRecipientColorStyles(
recipientIds.findIndex((id) => id === field.recipientId),
)
? getRecipientColorStyles(recipientIds.findIndex((id) => id === field.recipientId))
: undefined
}
>
{showRecipientTooltip && (
<div className="absolute -right-3 -top-3">
<div className="absolute -top-3 -right-3">
<PopoverHover
trigger={
<Avatar className="h-6 w-6 border-2 border-solid border-gray-200/50 transition-colors hover:border-gray-200">
<AvatarFallback className="bg-neutral-50 text-xs text-gray-400">
<Avatar className="h-6 w-6 border-2 border-gray-200/50 border-solid transition-colors hover:border-gray-200">
<AvatarFallback className="bg-neutral-50 text-gray-400 text-xs">
{extractInitials(field.recipient.name || field.recipient.email)}
</AvatarFallback>
</Avatar>
@@ -133,11 +126,7 @@ export const DocumentReadOnlyFields = ({
{showFieldStatus && (
<Badge
className="mx-auto mb-1 py-0.5"
variant={
field.recipient.signingStatus === SigningStatus.SIGNED
? 'default'
: 'secondary'
}
variant={field.recipient.signingStatus === SigningStatus.SIGNED ? 'default' : 'secondary'}
>
{field.recipient.signingStatus === SigningStatus.SIGNED ? (
<>
@@ -154,17 +143,15 @@ export const DocumentReadOnlyFields = ({
)}
<p className="text-center font-semibold">
<span>
{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])} field
</span>
<span>{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])} field</span>
</p>
<p className="mt-1 text-center text-xs text-muted-foreground">
<p className="mt-1 text-center text-muted-foreground text-xs">
{getRecipientDisplayText(field.recipient)}
</p>
<button
className="absolute right-0 top-0 my-1 p-2 focus:outline-none focus-visible:ring-0"
className="absolute top-0 right-0 my-1 p-2 focus:outline-none focus-visible:ring-0"
onClick={() => handleHideField(field.secondaryId)}
title="Hide field"
>
@@ -1,5 +1,3 @@
import React from 'react';
import { Trans } from '@lingui/react/macro';
export const DocumentSendEmailMessageHelper = () => {
@@ -11,21 +9,15 @@ export const DocumentSendEmailMessageHelper = () => {
<ul className="mt-2 flex list-inside list-disc flex-col gap-y-2 text-sm">
<li className="text-muted-foreground">
<code className="text-muted-foreground bg-muted-foreground/20 rounded p-1 text-sm">
{'{signer.name}'}
</code>{' '}
- <Trans>The signer's name</Trans>
<code className="rounded bg-muted-foreground/20 p-1 text-muted-foreground text-sm">{'{signer.name}'}</code> -{' '}
<Trans>The signer's name</Trans>
</li>
<li className="text-muted-foreground">
<code className="text-muted-foreground bg-muted-foreground/20 rounded p-1 text-sm">
{'{signer.email}'}
</code>{' '}
- <Trans>The signer's email</Trans>
<code className="rounded bg-muted-foreground/20 p-1 text-muted-foreground text-sm">{'{signer.email}'}</code> -{' '}
<Trans>The signer's email</Trans>
</li>
<li className="text-muted-foreground">
<code className="text-muted-foreground bg-muted-foreground/20 rounded p-1 text-sm">
{'{document.name}'}
</code>{' '}
<code className="rounded bg-muted-foreground/20 p-1 text-muted-foreground text-sm">{'{document.name}'}</code>{' '}
- <Trans>The document's name</Trans>
</li>
</ul>
@@ -1,17 +1,16 @@
import type { HTMLAttributes } from 'react';
import React, { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Copy, Sparkles } from 'lucide-react';
import { FaXTwitter } from 'react-icons/fa6';
import { useCopyShareLink } from '@documenso/lib/client-only/hooks/use-copy-share-link';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { generateTwitterIntent } from '@documenso/lib/universal/generate-twitter-intent';
import { trpc } from '@documenso/trpc/react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Copy, Sparkles } from 'lucide-react';
import type React from 'react';
import type { HTMLAttributes } from 'react';
import { useState } from 'react';
import { FaXTwitter } from 'react-icons/fa6';
import { cn } from '../../lib/utils';
import { Button } from '../../primitives/button';
@@ -31,12 +30,7 @@ export type DocumentShareButtonProps = HTMLAttributes<HTMLButtonElement> & {
trigger?: (_props: { loading: boolean; disabled: boolean }) => React.ReactNode;
};
export const DocumentShareButton = ({
token,
documentId,
className,
trigger,
}: DocumentShareButtonProps) => {
export const DocumentShareButton = ({ token, documentId, className, trigger }: DocumentShareButtonProps) => {
const { _ } = useLingui();
const { toast } = useToast();
@@ -147,9 +141,8 @@ export const DocumentShareButton = ({
<DialogDescription className="mt-4">
<Trans>
Rest assured, your document is strictly confidential and will never be shared. Only
your signing experience will be highlighted. Share your personalized signing card to
showcase your signature!
Rest assured, your document is strictly confidential and will never be shared. Only your signing
experience will be highlighted. Share your personalized signing card to showcase your signature!
</Trans>
</DialogDescription>
</DialogHeader>
@@ -157,8 +150,7 @@ export const DocumentShareButton = ({
<div className="flex w-full flex-col">
<div className="rounded-md border p-4">
I just {token ? 'signed' : 'sent'} a document in style with{' '}
<span className="font-medium text-blue-400">@documenso</span>
. Check it out!
<span className="font-medium text-blue-400">@documenso</span>. Check it out!
<span className="mt-2 block" />
<span
className={cn('break-all font-medium text-blue-400', {
@@ -168,12 +160,9 @@ export const DocumentShareButton = ({
{NEXT_PUBLIC_WEBAPP_URL()}/share/{shareLink?.slug || '...'}
</span>
<div
className={cn(
'mt-4 aspect-[1200/630] overflow-hidden rounded-lg border bg-muted/40',
{
'animate-pulse': !shareLink?.slug,
},
)}
className={cn('mt-4 aspect-[1200/630] overflow-hidden rounded-lg border bg-muted/40', {
'animate-pulse': !shareLink?.slug,
})}
>
{shareLink?.slug && (
<img
@@ -1,8 +1,7 @@
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
export const DocumentSignatureSettingsTooltip = () => {
return (
<Tooltip>
@@ -10,7 +9,7 @@ export const DocumentSignatureSettingsTooltip = () => {
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<h2>
<strong>
<Trans>Signature types</Trans>
@@ -18,9 +17,7 @@ export const DocumentSignatureSettingsTooltip = () => {
</h2>
<p>
<Trans>
The types of signatures that recipients are allowed to use when signing the document.
</Trans>
<Trans>The types of signatures that recipients are allowed to use when signing the document.</Trans>
</p>
<ul className="ml-3.5 list-outside list-disc space-y-0.5 py-2">
@@ -1,22 +1,14 @@
import React, { forwardRef } from 'react';
import { DOCUMENT_VISIBILITY } from '@documenso/lib/constants/document-visibility';
import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { t } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { TeamMemberRole } from '@prisma/client';
import type { SelectProps } from '@radix-ui/react-select';
import { InfoIcon } from 'lucide-react';
import { DOCUMENT_VISIBILITY } from '@documenso/lib/constants/document-visibility';
import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { forwardRef } from 'react';
export type DocumentVisibilitySelectType = SelectProps & {
currentTeamMemberRole?: string;
@@ -26,10 +18,7 @@ export type DocumentVisibilitySelectType = SelectProps & {
};
export const DocumentVisibilitySelect = forwardRef<HTMLButtonElement, DocumentVisibilitySelectType>(
(
{ currentTeamMemberRole, isTeamSettings = false, disabled, canUpdateVisibility, ...props },
ref,
) => {
({ currentTeamMemberRole, isTeamSettings = false, disabled, canUpdateVisibility, ...props }, ref) => {
const { _ } = useLingui();
const isAdmin = currentTeamMemberRole === TeamMemberRole.ADMIN;
@@ -43,13 +32,8 @@ export const DocumentVisibilitySelect = forwardRef<HTMLButtonElement, DocumentVi
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value={DocumentVisibility.EVERYONE}>
{_(DOCUMENT_VISIBILITY.EVERYONE.value)}
</SelectItem>
<SelectItem
value={DocumentVisibility.MANAGER_AND_ABOVE}
disabled={!isAdmin && !isManager}
>
<SelectItem value={DocumentVisibility.EVERYONE}>{_(DOCUMENT_VISIBILITY.EVERYONE.value)}</SelectItem>
<SelectItem value={DocumentVisibility.MANAGER_AND_ABOVE} disabled={!isAdmin && !isManager}>
{_(DOCUMENT_VISIBILITY.MANAGER_AND_ABOVE.value)}
</SelectItem>
<SelectItem value={DocumentVisibility.ADMIN} disabled={!isAdmin}>
@@ -89,8 +73,7 @@ export const DocumentVisibilityTooltip = () => {
</li>
<li>
<Trans>
<strong>Managers and above</strong> - Only managers and above can access and view the
document
<strong>Managers and above</strong> - Only managers and above can access and view the document
</Trans>
</li>
<li>
@@ -1,12 +1,10 @@
import { useCallback, useEffect, useState } from 'react';
import { Trans, useLingui } from '@lingui/react/macro';
import { SigningStatus } from '@prisma/client';
import type { Field, Recipient } from '@prisma/client';
import { ClockIcon, EyeOffIcon, LockIcon } from 'lucide-react';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { Trans, useLingui } from '@lingui/react/macro';
import type { Field, Recipient } from '@prisma/client';
import { SigningStatus } from '@prisma/client';
import { ClockIcon, EyeOffIcon, LockIcon } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
import { isTemplateRecipientEmailPlaceholder } from '../../../lib/constants/template';
import { extractInitials } from '../../../lib/utils/recipient-formatter';
@@ -20,15 +18,7 @@ import { PopoverHover } from '../../primitives/popover';
interface EnvelopeRecipientFieldTooltipProps {
field: Pick<
Field,
| 'id'
| 'inserted'
| 'positionX'
| 'positionY'
| 'width'
| 'height'
| 'page'
| 'type'
| 'fieldMeta'
'id' | 'inserted' | 'positionX' | 'positionY' | 'width' | 'height' | 'page' | 'type' | 'fieldMeta'
> & {
recipient: Pick<Recipient, 'name' | 'email' | 'signingStatus'>;
};
@@ -70,9 +60,7 @@ export function EnvelopeRecipientFieldTooltip({
});
const calculateCoords = useCallback(() => {
const $page = document.querySelector<HTMLElement>(
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`,
);
const $page = document.querySelector<HTMLElement>(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`);
if (!$page) {
return;
@@ -111,9 +99,7 @@ export function EnvelopeRecipientFieldTooltip({
}, [calculateCoords]);
useEffect(() => {
const $page = document.querySelector<HTMLElement>(
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`,
);
const $page = document.querySelector<HTMLElement>(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`);
if (!$page) {
return;
@@ -145,8 +131,8 @@ export function EnvelopeRecipientFieldTooltip({
>
<PopoverHover
trigger={
<Avatar className="absolute -left-3 -top-3 z-50 h-6 w-6 border-2 border-solid border-gray-200/50 transition-colors hover:border-gray-200">
<AvatarFallback className="bg-neutral-50 text-xs text-gray-400">
<Avatar className="absolute -top-3 -left-3 z-50 h-6 w-6 border-2 border-gray-200/50 border-solid transition-colors hover:border-gray-200">
<AvatarFallback className="bg-neutral-50 text-gray-400 text-xs">
{extractInitials(field.recipient.name || field.recipient.email)}
</AvatarFallback>
</Avatar>
@@ -191,12 +177,10 @@ export function EnvelopeRecipientFieldTooltip({
</span>
</p>
<p className="mt-1 text-center text-xs text-muted-foreground">
{getRecipientDisplayText(field.recipient)}
</p>
<p className="mt-1 text-center text-muted-foreground text-xs">{getRecipientDisplayText(field.recipient)}</p>
<button
className="absolute right-0 top-0 my-1 p-2 focus:outline-none focus-visible:ring-0"
className="absolute top-0 right-0 my-1 p-2 focus:outline-none focus-visible:ring-0"
onClick={() => setHideField(true)}
title="Hide field"
>
@@ -1,17 +1,10 @@
import { Plural, Trans } from '@lingui/react/macro';
import type {
TEnvelopeExpirationDurationPeriod,
TEnvelopeExpirationPeriod,
} from '@documenso/lib/constants/envelope-expiration';
import { Input } from '@documenso/ui/primitives/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Plural, Trans } from '@lingui/react/macro';
type ExpirationMode = 'duration' | 'disabled' | 'inherit';
@@ -35,9 +28,7 @@ const getAmount = (value: TEnvelopeExpirationPeriod | null | undefined): number
return 1;
};
const getUnit = (
value: TEnvelopeExpirationPeriod | null | undefined,
): TEnvelopeExpirationDurationPeriod['unit'] => {
const getUnit = (value: TEnvelopeExpirationPeriod | null | undefined): TEnvelopeExpirationDurationPeriod['unit'] => {
if (value && 'unit' in value) {
return value.unit;
}
@@ -1,5 +1,3 @@
import { Plural, Trans } from '@lingui/react/macro';
import type {
TEnvelopeReminderDurationPeriod,
TEnvelopeReminderPeriod,
@@ -7,13 +5,8 @@ import type {
} from '@documenso/lib/constants/envelope-reminder';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Plural, Trans } from '@lingui/react/macro';
type ReminderMode = 'enabled' | 'disabled' | 'inherit';
@@ -37,9 +30,7 @@ const getPeriodAmount = (period: TEnvelopeReminderPeriod | undefined): number =>
return 1;
};
const getPeriodUnit = (
period: TEnvelopeReminderPeriod | undefined,
): TEnvelopeReminderDurationPeriod['unit'] => {
const getPeriodUnit = (period: TEnvelopeReminderPeriod | undefined): TEnvelopeReminderDurationPeriod['unit'] => {
if (period && 'unit' in period) {
return period.unit;
}
@@ -87,9 +78,7 @@ export const ReminderSettingsPicker = ({
});
};
const updateSendAfter = (
updates: Partial<{ amount: number; unit: TEnvelopeReminderDurationPeriod['unit'] }>,
) => {
const updateSendAfter = (updates: Partial<{ amount: number; unit: TEnvelopeReminderDurationPeriod['unit'] }>) => {
const newAmount = Math.max(1, Math.floor(updates.amount ?? sendAfterAmount));
const newUnit = updates.unit ?? sendAfterUnit;
@@ -99,9 +88,7 @@ export const ReminderSettingsPicker = ({
});
};
const updateRepeatEvery = (
updates: Partial<{ amount: number; unit: TEnvelopeReminderDurationPeriod['unit'] }>,
) => {
const updateRepeatEvery = (updates: Partial<{ amount: number; unit: TEnvelopeReminderDurationPeriod['unit'] }>) => {
const newAmount = Math.max(1, Math.floor(updates.amount ?? repeatEveryAmount));
const newUnit = updates.unit ?? repeatEveryUnit;
@@ -151,7 +138,7 @@ export const ReminderSettingsPicker = ({
{mode === 'enabled' && (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<Label className="text-sm text-muted-foreground">
<Label className="text-muted-foreground text-sm">
<Trans>Send first reminder after</Trans>
</Label>
@@ -181,7 +168,7 @@ export const ReminderSettingsPicker = ({
</div>
<div className="flex flex-col gap-2">
<Label className="text-sm text-muted-foreground">
<Label className="text-muted-foreground text-sm">
<Trans>Then repeat every</Trans>
</Label>
@@ -1,12 +1,10 @@
import { useCallback, useEffect, useState } from 'react';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import type { Field } from '@prisma/client';
import { TooltipArrow } from '@radix-ui/react-tooltip';
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { useCallback, useEffect, useState } from 'react';
import { cn } from '../../lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../primitives/tooltip';
@@ -26,21 +24,13 @@ const tooltipVariants = cva('font-semibold', {
interface EnvelopeFieldToolTipProps extends VariantProps<typeof tooltipVariants> {
children: React.ReactNode;
className?: string;
field: Pick<
Field,
'id' | 'inserted' | 'fieldMeta' | 'positionX' | 'positionY' | 'width' | 'height' | 'page'
>;
field: Pick<Field, 'id' | 'inserted' | 'fieldMeta' | 'positionX' | 'positionY' | 'width' | 'height' | 'page'>;
}
/**
* Renders a tooltip for a given field.
*/
export function EnvelopeFieldToolTip({
children,
color,
className = '',
field,
}: EnvelopeFieldToolTipProps) {
export function EnvelopeFieldToolTip({ children, color, className = '', field }: EnvelopeFieldToolTipProps) {
const [coords, setCoords] = useState({
x: 0,
y: 0,
@@ -49,9 +39,7 @@ export function EnvelopeFieldToolTip({
});
const calculateCoords = useCallback(() => {
const $page = document.querySelector<HTMLElement>(
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`,
);
const $page = document.querySelector<HTMLElement>(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`);
if (!$page) {
return;
@@ -90,9 +78,7 @@ export function EnvelopeFieldToolTip({
}, [calculateCoords]);
useEffect(() => {
const $page = document.querySelector<HTMLElement>(
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`,
);
const $page = document.querySelector<HTMLElement>(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`);
if (!$page) {
return;
@@ -124,10 +110,7 @@ export function EnvelopeFieldToolTip({
<Tooltip delayDuration={0} open={!field.inserted || !field.fieldMeta}>
<TooltipTrigger className="absolute inset-0 w-full"></TooltipTrigger>
<TooltipContent
className={tooltipVariants({ color, className: cn(className, 'z-40') })}
sideOffset={2}
>
<TooltipContent className={tooltipVariants({ color, className: cn(className, 'z-40') })} sideOffset={2}>
{children}
<TooltipArrow />
</TooltipContent>
+3 -12
View File
@@ -1,18 +1,12 @@
import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords';
import type { Field } from '@prisma/client';
import { TooltipArrow } from '@radix-ui/react-tooltip';
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
import { createPortal } from 'react-dom';
import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords';
import { cn } from '../..//lib/utils';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '../..//primitives/tooltip';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../..//primitives/tooltip';
const tooltipVariants = cva('font-semibold', {
variants: {
@@ -29,10 +23,7 @@ const tooltipVariants = cva('font-semibold', {
interface FieldToolTipProps extends VariantProps<typeof tooltipVariants> {
children: React.ReactNode;
className?: string;
field: Pick<
Field,
'id' | 'inserted' | 'fieldMeta' | 'positionX' | 'positionY' | 'width' | 'height' | 'page'
>;
field: Pick<Field, 'id' | 'inserted' | 'fieldMeta' | 'positionX' | 'positionY' | 'width' | 'height' | 'page'>;
}
/**
+8 -28
View File
@@ -1,16 +1,11 @@
import React, { useEffect, useMemo, useState } from 'react';
import { type Field, FieldType } from '@prisma/client';
import { createPortal } from 'react-dom';
import { useElementBounds } from '@documenso/lib/client-only/hooks/use-element-bounds';
import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords';
import { useIsPageInDom } from '@documenso/lib/client-only/hooks/use-is-page-in-dom';
import {
PDF_VIEWER_CONTENT_SELECTOR,
PDF_VIEWER_PAGE_SELECTOR,
} from '@documenso/lib/constants/pdf-viewer';
import { PDF_VIEWER_CONTENT_SELECTOR, PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
import { type Field, FieldType } from '@prisma/client';
import React, { useEffect, useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import type { RecipientColorStyles } from '../../lib/recipient-colors';
import { cn } from '../../lib/utils';
@@ -21,17 +16,11 @@ export type FieldContainerPortalProps = {
children: React.ReactNode;
};
export function FieldContainerPortal({
field,
children,
className = '',
}: FieldContainerPortalProps) {
export function FieldContainerPortal({ field, children, className = '' }: FieldContainerPortalProps) {
const alternativePortalRoot = document.getElementById('document-field-portal-root');
const coords = useFieldPageCoords(field);
const $pageBounds = useElementBounds(
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`,
);
const $pageBounds = useElementBounds(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`);
const maxWidth = $pageBounds?.width ? $pageBounds.width - coords.x : undefined;
@@ -77,13 +66,7 @@ export type FieldRootContainerProps = {
readonly?: boolean;
};
export function FieldRootContainer({
field,
children,
color,
className,
readonly,
}: FieldRootContainerProps) {
export function FieldRootContainer({ field, children, color, className, readonly }: FieldRootContainerProps) {
const [isValidating, setIsValidating] = useState(false);
const isPageInDom = useIsPageInDom(field.page);
@@ -101,10 +84,7 @@ export function FieldRootContainer({
// pick up the validation state immediately.
const pdfContent = document.querySelector(PDF_VIEWER_CONTENT_SELECTOR);
if (
pdfContent?.getAttribute('data-validate-fields') === 'true' &&
isFieldUnsignedAndRequired(field)
) {
if (pdfContent?.getAttribute('data-validate-fields') === 'true' && isFieldUnsignedAndRequired(field)) {
ref.current.setAttribute('data-validate', 'true');
setIsValidating(true);
}
@@ -1,14 +1,11 @@
import React from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
import { RecipientActionAuth } from '@documenso/lib/types/document-auth';
import { MultiSelect, type Option } from '@documenso/ui/primitives/multiselect';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { InfoIcon } from 'lucide-react';
import { cn } from '../../lib/utils';
@@ -45,15 +42,11 @@ export const RecipientActionAuthSelect = ({
// Convert string array to Option array for MultiSelect
const selectedOptions =
(value
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
(value?.map((val) => authOptions.find((option) => option.value === val)).filter(Boolean) as Option[]) || [];
// Convert default value to Option array
const defaultOptions =
(defaultValue
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
(defaultValue?.map((val) => authOptions.find((option) => option.value === val)).filter(Boolean) as Option[]) || [];
const handleChange = (options: Option[]) => {
const values = options.map((option) => option.value);
@@ -76,7 +69,7 @@ export const RecipientActionAuthSelect = ({
<Tooltip>
<TooltipTrigger
className={cn('absolute right-2 top-1/2 -translate-y-1/2', {
className={cn('absolute top-1/2 right-2 -translate-y-1/2', {
'right-8': selectedOptions.length > 0,
})}
>
@@ -95,28 +88,25 @@ export const RecipientActionAuthSelect = ({
</p>
<p className="mt-2">
<Trans>
These will override any global settings. Multiple methods can be selected.
</Trans>
<Trans>These will override any global settings. Multiple methods can be selected.</Trans>
</p>
<ul className="ml-3.5 list-outside list-disc space-y-0.5 py-2">
<li>
<Trans>
<strong>Inherit authentication method</strong> - Use the global action signing
authentication method configured in the "General Settings" step
<strong>Inherit authentication method</strong> - Use the global action signing authentication method
configured in the "General Settings" step
</Trans>
</li>
<li>
<Trans>
<strong>Require passkey</strong> - The recipient must have an account and passkey
configured via their settings
<strong>Require passkey</strong> - The recipient must have an account and passkey configured via their
settings
</Trans>
</li>
<li>
<Trans>
<strong>Require 2FA</strong> - The recipient must have an account and 2FA enabled
via their settings
<strong>Require 2FA</strong> - The recipient must have an account and 2FA enabled via their settings
</Trans>
</li>
<li>
@@ -1,9 +1,9 @@
import React, { useRef, useState } from 'react';
import { Popover, PopoverContent } from '@documenso/ui/primitives/popover';
import { Trans } from '@lingui/react/macro';
import { PopoverAnchor } from '@radix-ui/react-popover';
import { Popover, PopoverContent } from '@documenso/ui/primitives/popover';
import type React from 'react';
import { useRef, useState } from 'react';
import { Command, CommandGroup, CommandItem } from '../../primitives/command';
import { Input } from '../../primitives/input';
@@ -77,11 +77,7 @@ export const RecipientAutoCompleteInput = ({
{/* Not using <CommandEmpty /> here due to some weird behaviour */}
{options.length === 0 && (
<div className="px-2 py-1.5 text-sm">
{loading ? (
<Trans>Loading suggestions...</Trans>
) : (
<Trans>No suggestions found</Trans>
)}
{loading ? <Trans>Loading suggestions...</Trans> : <Trans>No suggestions found</Trans>}
</div>
)}
@@ -1,13 +1,11 @@
import React, { forwardRef } from 'react';
import { ROLE_ICONS } from '@documenso/ui/primitives/recipient-role-icons';
import { Select, SelectContent, SelectItem, SelectTrigger } from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { Trans } from '@lingui/react/macro';
import { RecipientRole } from '@prisma/client';
import type { SelectProps } from '@radix-ui/react-select';
import { InfoIcon } from 'lucide-react';
import { ROLE_ICONS } from '@documenso/ui/primitives/recipient-role-icons';
import { Select, SelectContent, SelectItem, SelectTrigger } from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { forwardRef } from 'react';
import { cn } from '../../lib/utils';
@@ -21,17 +19,7 @@ export type RecipientRoleSelectProps = SelectProps & {
};
export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSelectProps>(
(
{
hideAssistantRole,
hideCCerRole,
hideViewerRole,
hideApproverRole,
isAssistantEnabled = true,
...props
},
ref,
) => (
({ hideAssistantRole, hideCCerRole, hideViewerRole, hideApproverRole, isAssistantEnabled = true, ...props }, ref) => (
<Select {...props}>
<SelectTrigger ref={ref} className="w-[50px] bg-background p-2" title={props.value}>
{/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */}
@@ -51,9 +39,7 @@ export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSe
</TooltipTrigger>
<TooltipContent className="z-9999 max-w-md p-4 text-foreground">
<p>
<Trans>
The recipient is required to sign the document for it to be completed.
</Trans>
<Trans>The recipient is required to sign the document for it to be completed.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -73,9 +59,7 @@ export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSe
</TooltipTrigger>
<TooltipContent className="z-9999 max-w-md p-4 text-foreground">
<p>
<Trans>
The recipient is required to approve the document for it to be completed.
</Trans>
<Trans>The recipient is required to approve the document for it to be completed.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -96,9 +80,7 @@ export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSe
</TooltipTrigger>
<TooltipContent className="z-9999 max-w-md p-4 text-foreground">
<p>
<Trans>
The recipient is required to view the document for it to be completed.
</Trans>
<Trans>The recipient is required to view the document for it to be completed.</Trans>
</p>
</TooltipContent>
</Tooltip>
@@ -120,8 +102,8 @@ export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSe
<TooltipContent className="z-9999 max-w-md p-4 text-foreground">
<p>
<Trans>
The recipient is not required to take any action and receives a copy of the
document after it is completed.
The recipient is not required to take any action and receives a copy of the document after it is
completed.
</Trans>
</p>
</TooltipContent>
@@ -134,10 +116,7 @@ export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSe
<SelectItem
value={RecipientRole.ASSISTANT}
disabled={!isAssistantEnabled}
className={cn(
!isAssistantEnabled &&
'cursor-not-allowed opacity-50 data-[disabled]:pointer-events-auto',
)}
className={cn(!isAssistantEnabled && 'cursor-not-allowed opacity-50 data-[disabled]:pointer-events-auto')}
>
<div className="flex items-center">
<div className="flex w-[150px] items-center">
@@ -152,14 +131,10 @@ export const RecipientRoleSelect = forwardRef<HTMLButtonElement, RecipientRoleSe
<p>
{isAssistantEnabled ? (
<Trans>
The recipient can prepare the document for later signers by pre-filling
suggest values.
The recipient can prepare the document for later signers by pre-filling suggest values.
</Trans>
) : (
<Trans>
Assistant role is only available when the document is in sequential signing
mode.
</Trans>
<Trans>Assistant role is only available when the document is in sequential signing mode.</Trans>
)}
</p>
</TooltipContent>
+14 -41
View File
@@ -1,8 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import type { Signature } from '@prisma/client';
import { animate, motion, useMotionTemplate, useMotionValue, useTransform } from 'framer-motion';
import { P, match } from 'ts-pattern';
import { useCallback, useEffect, useRef, useState } from 'react';
import { match, P } from 'ts-pattern';
import { cn } from '../lib/utils';
import { Card, CardContent } from '../primitives/card';
@@ -17,19 +16,12 @@ export type SigningCardProps = {
/**
* 2D signing card.
*/
export const SigningCard = ({
className,
name,
signature,
signingCelebrationImage,
}: SigningCardProps) => {
export const SigningCard = ({ className, name, signature, signingCelebrationImage }: SigningCardProps) => {
return (
<div className={cn('relative w-full max-w-sm md:max-w-md', className)}>
<SigningCardContent name={name} signature={signature} />
{signingCelebrationImage && (
<SigningCardImage signingCelebrationImage={signingCelebrationImage} />
)}
{signingCelebrationImage && <SigningCardImage signingCelebrationImage={signingCelebrationImage} />}
</div>
);
};
@@ -37,12 +29,7 @@ export const SigningCard = ({
/**
* 3D signing card that follows the mouse movement within a certain range.
*/
export const SigningCard3D = ({
className,
name,
signature,
signingCelebrationImage,
}: SigningCardProps) => {
export const SigningCard3D = ({ className, name, signature, signingCelebrationImage }: SigningCardProps) => {
// Should use % based dimensions by calculating the window height/width.
const boundary = 400;
@@ -127,12 +114,9 @@ export const SigningCard3D = ({
}, [onMouseMove]);
return (
<div
className={cn('relative w-full max-w-sm md:max-w-md', className)}
style={{ perspective: 800 }}
>
<div className={cn('relative w-full max-w-sm md:max-w-md', className)} style={{ perspective: 800 }}>
<motion.div
className="bg-background w-full rounded-lg [--sheen-color:180_180_180] dark:[--sheen-color:200_200_200]"
className="w-full rounded-lg bg-background [--sheen-color:180_180_180] dark:[--sheen-color:200_200_200]"
ref={cardRef}
style={{
perspective: '800',
@@ -145,9 +129,7 @@ export const SigningCard3D = ({
<SigningCardContent className="bg-transparent" name={name} signature={signature} />
</motion.div>
{signingCelebrationImage && (
<SigningCardImage signingCelebrationImage={signingCelebrationImage} />
)}
{signingCelebrationImage && <SigningCardImage signingCelebrationImage={signingCelebrationImage} />}
</div>
);
};
@@ -161,34 +143,25 @@ type SigningCardContentProps = {
const SigningCardContent = ({ className, name, signature }: SigningCardContentProps) => {
return (
<Card
className={cn(
'group z-10 mx-auto flex aspect-[21/9] w-full items-center justify-center',
className,
)}
className={cn('group z-10 mx-auto flex aspect-[21/9] w-full items-center justify-center', className)}
degrees={-145}
gradient
>
<CardContent
className="font-signature p-6 text-center"
className="p-6 text-center font-signature"
style={{
container: 'main',
}}
>
{match(signature)
.with({ signatureImageAsBase64: P.string }, (signature) => (
<img
src={signature.signatureImageAsBase64}
alt="signature"
className="h-full max-w-[100%] dark:invert"
/>
<img src={signature.signatureImageAsBase64} alt="signature" className="h-full max-w-[100%] dark:invert" />
))
.with({ typedSignature: P.string }, (signature) => (
<span
className="text-muted-foreground/60 group-hover:text-primary/80 break-all font-semibold duration-300"
className="break-all font-semibold text-muted-foreground/60 duration-300 group-hover:text-primary/80"
style={{
fontSize: `max(min(4rem, ${(100 / signature.typedSignature.length / 2).toFixed(
4,
)}cqw), 1.875rem)`,
fontSize: `max(min(4rem, ${(100 / signature.typedSignature.length / 2).toFixed(4)}cqw), 1.875rem)`,
}}
>
{signature.typedSignature}
@@ -196,7 +169,7 @@ const SigningCardContent = ({ className, name, signature }: SigningCardContentPr
))
.otherwise(() => (
<span
className="text-muted-foreground/60 group-hover:text-primary/80 break-all font-semibold duration-300"
className="break-all font-semibold text-muted-foreground/60 duration-300 group-hover:text-primary/80"
style={{
fontSize: `max(min(4rem, ${(100 / name.length / 2).toFixed(4)}cqw), 1.875rem)`,
}}
@@ -1,50 +1,36 @@
import React, { forwardRef } from 'react';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { t } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { TemplateType } from '@prisma/client';
import type { SelectProps } from '@radix-ui/react-select';
import { InfoIcon } from 'lucide-react';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { forwardRef } from 'react';
export type TemplateTypeSelectProps = SelectProps;
export const TemplateTypeSelect = forwardRef<HTMLButtonElement, TemplateTypeSelectProps>(
({ ...props }, ref) => {
useLingui();
export const TemplateTypeSelect = forwardRef<HTMLButtonElement, TemplateTypeSelectProps>(({ ...props }, ref) => {
useLingui();
return (
<Select {...props}>
<SelectTrigger ref={ref} className="bg-background">
<SelectValue />
</SelectTrigger>
return (
<Select {...props}>
<SelectTrigger ref={ref} className="bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value={TemplateType.PRIVATE}>{t`Private`}</SelectItem>
<SelectItem value={TemplateType.PUBLIC}>{t`Public`}</SelectItem>
<SelectItem value={TemplateType.ORGANISATION}>{t`Organisation`}</SelectItem>
</SelectContent>
</Select>
);
},
);
<SelectContent>
<SelectItem value={TemplateType.PRIVATE}>{t`Private`}</SelectItem>
<SelectItem value={TemplateType.PUBLIC}>{t`Public`}</SelectItem>
<SelectItem value={TemplateType.ORGANISATION}>{t`Organisation`}</SelectItem>
</SelectContent>
</Select>
);
});
TemplateTypeSelect.displayName = 'TemplateTypeSelect';
export const TemplateTypeTooltip = ({
organisationTeamCount,
}: {
organisationTeamCount: number;
}) => {
export const TemplateTypeTooltip = ({ organisationTeamCount }: { organisationTeamCount: number }) => {
return (
<Tooltip>
<TooltipTrigger>
@@ -65,8 +51,8 @@ export const TemplateTypeTooltip = ({
{organisationTeamCount >= 2 && (
<p>
<Trans>
<strong>Organisation</strong> templates are shared across all teams in your
organisation but can only be edited by the owning team.
<strong>Organisation</strong> templates are shared across all teams in your organisation but can only be
edited by the owning team.
</Trans>
</p>
)}