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
@@ -1,13 +1,3 @@
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { flushSync } from 'react-dom';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { Button } from '@documenso/ui/primitives/button';
@@ -20,17 +10,18 @@ import {
DialogTitle,
DialogTrigger,
} from '@documenso/ui/primitives/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { useState } from 'react';
import { flushSync } from 'react-dom';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
export const ZDisable2FAForm = z.object({
totpCode: z.string().trim().optional(),
@@ -120,8 +111,8 @@ export const DisableAuthenticatorAppDialog = () => {
<DialogDescription>
<Trans>
Please provide a token from the authenticator, or a backup code. If you do not have a
backup code available, please contact support.
Please provide a token from the authenticator, or a backup code. If you do not have a backup code
available, please contact support.
</Trans>
</DialogDescription>
</DialogHeader>
@@ -171,11 +162,7 @@ export const DisableAuthenticatorAppDialog = () => {
)}
<DialogFooter>
<Button
type="button"
variant="secondary"
onClick={onToggleTwoFactorDisableMethodClick}
>
<Button type="button" variant="secondary" onClick={onToggleTwoFactorDisableMethodClick}>
{twoFactorDisableMethod === 'totp' ? (
<Trans>Use Backup Code</Trans>
) : (
@@ -1,13 +1,3 @@
import { useEffect, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { renderSVG } from 'uqr';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { downloadFile } from '@documenso/lib/client-only/download-file';
import { useSession } from '@documenso/lib/client-only/providers/session';
@@ -22,16 +12,17 @@ import {
DialogTitle,
DialogTrigger,
} from '@documenso/ui/primitives/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { renderSVG } from 'uqr';
import { z } from 'zod';
import { RecoveryCodeList } from './recovery-code-list';
@@ -100,9 +91,7 @@ export const EnableAuthenticatorAppDialog = ({ onSuccess }: EnableAuthenticatorA
toast({
title: _(msg`Two-factor authentication enabled`),
description: _(
msg`You will now be required to enter a code from your authenticator app when signing in.`,
),
description: _(msg`You will now be required to enter a code from your authenticator app when signing in.`),
});
} catch (_err) {
toast({
@@ -162,119 +151,112 @@ export const EnableAuthenticatorAppDialog = ({ onSuccess }: EnableAuthenticatorA
</DialogTrigger>
<DialogContent position="center">
{setup2FAData && (
<>
{recoveryCodes ? (
<div>
{setup2FAData &&
(recoveryCodes ? (
<div>
<DialogHeader>
<DialogTitle>
<Trans>Backup codes</Trans>
</DialogTitle>
<DialogDescription>
<Trans>Your recovery codes are listed below. Please store them in a safe place.</Trans>
</DialogDescription>
</DialogHeader>
<div className="mt-4">
<RecoveryCodeList recoveryCodes={recoveryCodes} />
</div>
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button variant="secondary">
<Trans>Close</Trans>
</Button>
</DialogClose>
<Button onClick={downloadRecoveryCodes}>
<Trans>Download</Trans>
</Button>
</DialogFooter>
</div>
) : (
<Form {...enable2FAForm}>
<form onSubmit={enable2FAForm.handleSubmit(onEnable2FAFormSubmit)}>
<DialogHeader>
<DialogTitle>
<Trans>Backup codes</Trans>
<Trans>Enable Authenticator App</Trans>
</DialogTitle>
<DialogDescription>
<Trans>
Your recovery codes are listed below. Please store them in a safe place.
To enable two-factor authentication, scan the following QR code using your authenticator app.
</Trans>
</DialogDescription>
</DialogHeader>
<div className="mt-4">
<RecoveryCodeList recoveryCodes={recoveryCodes} />
</div>
<fieldset disabled={isEnabling2FA} className="mt-4 flex flex-col gap-y-4">
<div
className="flex h-36 justify-center"
dangerouslySetInnerHTML={{
__html: renderSVG(setup2FAData?.uri ?? ''),
}}
/>
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button variant="secondary">
<Trans>Close</Trans>
</Button>
</DialogClose>
<p className="text-muted-foreground text-sm">
<Trans>
If your authenticator app does not support QR codes, you can use the following code instead:
</Trans>
</p>
<Button onClick={downloadRecoveryCodes}>
<Trans>Download</Trans>
</Button>
</DialogFooter>
</div>
) : (
<Form {...enable2FAForm}>
<form onSubmit={enable2FAForm.handleSubmit(onEnable2FAFormSubmit)}>
<DialogHeader>
<DialogTitle>
<Trans>Enable Authenticator App</Trans>
</DialogTitle>
<DialogDescription>
<Trans>
To enable two-factor authentication, scan the following QR code using your
authenticator app.
</Trans>
</DialogDescription>
</DialogHeader>
<p className="rounded-lg bg-muted/60 p-2 text-center font-mono text-muted-foreground tracking-widest">
{setup2FAData?.secret}
</p>
<fieldset disabled={isEnabling2FA} className="mt-4 flex flex-col gap-y-4">
<div
className="flex h-36 justify-center"
dangerouslySetInnerHTML={{
__html: renderSVG(setup2FAData?.uri ?? ''),
}}
/>
<p className="text-muted-foreground text-sm">
<Trans>
Once you have scanned the QR code or entered the code manually, enter the code provided by your
authenticator app below.
</Trans>
</p>
<p className="text-muted-foreground text-sm">
<Trans>
If your authenticator app does not support QR codes, you can use the
following code instead:
</Trans>
</p>
<FormField
name="token"
control={enable2FAForm.control}
render={({ field }) => (
<FormItem>
<FormLabel className="text-muted-foreground">
<Trans>Token</Trans>
</FormLabel>
<FormControl>
<PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<p className="bg-muted/60 text-muted-foreground rounded-lg p-2 text-center font-mono tracking-widest">
{setup2FAData?.secret}
</p>
<p className="text-muted-foreground text-sm">
<Trans>
Once you have scanned the QR code or entered the code manually, enter the
code provided by your authenticator app below.
</Trans>
</p>
<FormField
name="token"
control={enable2FAForm.control}
render={({ field }) => (
<FormItem>
<FormLabel className="text-muted-foreground">
<Trans>Token</Trans>
</FormLabel>
<FormControl>
<PinInput {...field} value={field.value ?? ''} maxLength={6}>
{Array(6)
.fill(null)
.map((_, i) => (
<PinInputGroup key={i}>
<PinInputSlot index={i} />
</PinInputGroup>
))}
</PinInput>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">
<Trans>Cancel</Trans>
</Button>
</DialogClose>
<Button type="submit" loading={isEnabling2FA}>
<Trans>Enable 2FA</Trans>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">
<Trans>Cancel</Trans>
</Button>
</DialogFooter>
</fieldset>
</form>
</Form>
)}
</>
)}
</DialogClose>
<Button type="submit" loading={isEnabling2FA}>
<Trans>Enable 2FA</Trans>
</Button>
</DialogFooter>
</fieldset>
</form>
</Form>
))}
</DialogContent>
</Dialog>
);
@@ -1,10 +1,9 @@
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Copy } from 'lucide-react';
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { useToast } from '@documenso/ui/primitives/use-toast';
export type RecoveryCodeListProps = {
recoveryCodes: string[];
};
@@ -29,9 +28,7 @@ export const RecoveryCodeList = ({ recoveryCodes }: RecoveryCodeListProps) => {
} catch (_err) {
toast({
title: _(msg`Unable to copy recovery code`),
description: _(
msg`We were unable to copy your recovery code to your clipboard. Please try again.`,
),
description: _(msg`We were unable to copy your recovery code to your clipboard. Please try again.`),
variant: 'destructive',
});
}
@@ -40,17 +37,11 @@ export const RecoveryCodeList = ({ recoveryCodes }: RecoveryCodeListProps) => {
return (
<div className="grid grid-cols-2 gap-4">
{recoveryCodes.map((code) => (
<div
key={code}
className="bg-muted text-muted-foreground relative rounded-lg p-4 font-mono md:text-center"
>
<div key={code} className="relative rounded-lg bg-muted p-4 font-mono text-muted-foreground md:text-center">
<span>{code}</span>
<div className="absolute inset-y-0 right-4 flex items-center justify-center">
<button
className="opacity-60 hover:opacity-80"
onClick={() => void onCopyRecoveryCodeClick(code)}
>
<button className="opacity-60 hover:opacity-80" onClick={() => void onCopyRecoveryCodeClick(code)}>
<Copy className="h-5 w-5" />
</button>
</div>
@@ -1,11 +1,3 @@
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { downloadFile } from '@documenso/lib/client-only/download-file';
import { AppError } from '@documenso/lib/errors/app-error';
@@ -21,14 +13,14 @@ import {
DialogTitle,
DialogTrigger,
} from '@documenso/ui/primitives/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormMessage } from '@documenso/ui/primitives/form/form';
import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { RecoveryCodeList } from './recovery-code-list';
@@ -97,9 +89,7 @@ export const ViewRecoveryCodesDialog = () => {
</DialogTitle>
<DialogDescription>
<Trans>
Your recovery codes are listed below. Please store them in a safe place.
</Trans>
<Trans>Your recovery codes are listed below. Please store them in a safe place.</Trans>
</DialogDescription>
</DialogHeader>
@@ -156,9 +146,7 @@ export const ViewRecoveryCodesDialog = () => {
<Alert variant="destructive">
<AlertDescription>
{match(AppError.parseError(error).message)
.with('INCORRECT_TWO_FACTOR_CODE', () => (
<Trans>Invalid code. Please try again.</Trans>
))
.with('INCORRECT_TWO_FACTOR_CODE', () => <Trans>Invalid code. Please try again.</Trans>)
.otherwise(() => (
<Trans>Something went wrong. Please try again or contact support.</Trans>
))}
@@ -1,14 +1,3 @@
import { useMemo } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ErrorCode, useDropzone } from 'react-dropzone';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { AppError } from '@documenso/lib/errors/app-error';
import { base64 } from '@documenso/lib/universal/base64';
@@ -18,15 +7,17 @@ import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { useMemo } from 'react';
import { ErrorCode, useDropzone } from 'react-dropzone';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { z } from 'zod';
export const ZAvatarImageFormSchema = z.object({
bytes: z.string().nullish(),
@@ -69,11 +60,7 @@ export const AvatarImageForm = ({ className, team, organisation }: AvatarImageFo
return user.avatarImageId !== null;
}, [team, organisation, user.avatarImageId]);
const avatarImageId = team
? team.avatarImageId
: organisation
? organisation.avatarImageId
: user.avatarImageId;
const avatarImageId = team ? team.avatarImageId : organisation ? organisation.avatarImageId : user.avatarImageId;
const form = useForm<TAvatarImageFormSchema>({
values: {
@@ -127,8 +114,7 @@ export const AvatarImageForm = ({ className, team, organisation }: AvatarImageFo
const error = AppError.parseError(err);
const errorMessage = match(error.code).otherwise(
() =>
msg`We encountered an unknown error while attempting to update your password. Please try again later.`,
() => msg`We encountered an unknown error while attempting to update your password. Please try again later.`,
);
toast({
@@ -160,15 +146,13 @@ export const AvatarImageForm = ({ className, team, organisation }: AvatarImageFo
<div className="relative">
<Avatar className="h-16 w-16 border-2 border-solid">
{avatarImageId && <AvatarImage src={formatAvatarUrl(avatarImageId)} />}
<AvatarFallback className="text-sm text-gray-400">
{initials}
</AvatarFallback>
<AvatarFallback className="text-gray-400 text-sm">{initials}</AvatarFallback>
</Avatar>
{hasAvatarImage && (
<button
type="button"
className="bg-background/70 text-destructive absolute inset-0 flex cursor-pointer items-center justify-center text-xs opacity-0 transition-opacity hover:opacity-100"
className="absolute inset-0 flex cursor-pointer items-center justify-center bg-background/70 text-destructive text-xs opacity-0 transition-opacity hover:opacity-100"
disabled={form.formState.isSubmitting}
onClick={() => void onFormSubmit({ bytes: null })}
>
@@ -1,33 +1,18 @@
import { useEffect, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { Loader } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel } from '@documenso/ui/primitives/form/form';
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 { Textarea } from '@documenso/ui/primitives/textarea';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { Loader } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useOptionalCurrentTeam } from '~/providers/team';
@@ -39,10 +24,7 @@ const ZBrandingPreferencesFormSchema = z.object({
brandingLogo: z
.instanceof(File)
.refine((file) => file.size <= MAX_FILE_SIZE, 'File size must be less than 5MB')
.refine(
(file) => ACCEPTED_FILE_TYPES.includes(file.type),
'Only .jpg, .png, and .webp files are accepted',
)
.refine((file) => ACCEPTED_FILE_TYPES.includes(file.type), 'Only .jpg, .png, and .webp files are accepted')
.nullish(),
brandingUrl: z.string().url().optional().or(z.literal('')),
brandingCompanyDetails: z.string().max(500).optional(),
@@ -136,10 +118,7 @@ export function BrandingPreferencesForm({
field.onChange(value === 'true' ? true : value === 'false' ? false : null)
}
>
<SelectTrigger
className="bg-background text-muted-foreground"
data-testid="enable-branding"
>
<SelectTrigger className="bg-background text-muted-foreground" data-testid="enable-branding">
<SelectValue />
</SelectTrigger>
@@ -187,13 +166,9 @@ export function BrandingPreferencesForm({
<div className="flex flex-col gap-4">
<div className="relative h-48 w-full overflow-hidden rounded-lg border border-border bg-background">
{previewUrl ? (
<img
src={previewUrl}
alt="Logo preview"
className="h-full w-full object-contain p-4"
/>
<img src={previewUrl} alt="Logo preview" className="h-full w-full object-contain p-4" />
) : (
<div className="relative flex h-full w-full items-center justify-center bg-muted/20 text-sm text-muted-foreground dark:bg-muted">
<div className="relative flex h-full w-full items-center justify-center bg-muted/20 text-muted-foreground text-sm dark:bg-muted">
<Trans>Please upload a logo</Trans>
{!hasLoadedPreview && (
@@ -238,12 +213,12 @@ export function BrandingPreferencesForm({
/>
</FormControl>
<div className="absolute right-2 top-0 inline-flex h-full items-center justify-center">
<div className="absolute top-0 right-2 inline-flex h-full items-center justify-center">
<Button
type="button"
variant="link"
size="sm"
className="text-xs text-destructive"
className="text-destructive text-xs"
onClick={() => {
setPreviewUrl('');
onChange(null);
@@ -279,12 +254,7 @@ export function BrandingPreferencesForm({
</FormLabel>
<FormControl>
<Input
type="url"
placeholder="https://example.com"
disabled={!isBrandingEnabled}
{...field}
/>
<Input type="url" placeholder="https://example.com" disabled={!isBrandingEnabled} {...field} />
</FormControl>
<FormDescription>
@@ -1,12 +1,3 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { msg, t } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { DocumentVisibility, OrganisationType, type RecipientRole } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { DATE_FORMATS } from '@documenso/lib/constants/date-formats';
@@ -15,22 +6,12 @@ import {
type TEnvelopeExpirationPeriod,
ZEnvelopeExpirationPeriod,
} from '@documenso/lib/constants/envelope-expiration';
import {
type TEnvelopeReminderSettings,
ZEnvelopeReminderSettings,
} from '@documenso/lib/constants/envelope-reminder';
import {
SUPPORTED_LANGUAGES,
SUPPORTED_LANGUAGE_CODES,
isValidLanguageCode,
} from '@documenso/lib/constants/i18n';
import { type TEnvelopeReminderSettings, ZEnvelopeReminderSettings } from '@documenso/lib/constants/envelope-reminder';
import { isValidLanguageCode, SUPPORTED_LANGUAGE_CODES, SUPPORTED_LANGUAGES } from '@documenso/lib/constants/i18n';
import { TIME_ZONES } from '@documenso/lib/constants/time-zones';
import type { TDefaultRecipients } from '@documenso/lib/types/default-recipients';
import { ZDefaultRecipientsSchema } from '@documenso/lib/types/default-recipients';
import {
type TDocumentMetaDateFormat,
ZDocumentMetaTimezoneSchema,
} from '@documenso/lib/types/document-meta';
import { type TDocumentMetaDateFormat, ZDocumentMetaTimezoneSchema } from '@documenso/lib/types/document-meta';
import { isPersonalLayout } from '@documenso/lib/utils/organisations';
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
import { extractTeamSignatureSettings } from '@documenso/lib/utils/teams';
@@ -52,13 +33,15 @@ import {
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { MultiSelectCombobox } from '@documenso/ui/primitives/multi-select-combobox';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg, t } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { DocumentVisibility, OrganisationType, type RecipientRole } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useOptionalCurrentTeam } from '~/providers/team';
@@ -147,9 +130,7 @@ export const DocumentPreferencesForm = ({
const form = useForm<TDocumentPreferencesFormSchema>({
defaultValues: {
documentVisibility: settings.documentVisibility,
documentLanguage: isValidLanguageCode(settings.documentLanguage)
? settings.documentLanguage
: null,
documentLanguage: isValidLanguageCode(settings.documentLanguage) ? settings.documentLanguage : null,
documentTimezone: settings.documentTimezone,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
documentDateFormat: settings.documentDateFormat as TDocumentMetaDateFormat | null,
@@ -157,9 +138,7 @@ export const DocumentPreferencesForm = ({
includeSigningCertificate: settings.includeSigningCertificate,
includeAuditLog: settings.includeAuditLog,
signatureTypes: extractTeamSignatureSettings({ ...settings }),
defaultRecipients: settings.defaultRecipients
? ZDefaultRecipientsSchema.parse(settings.defaultRecipients)
: null,
defaultRecipients: settings.defaultRecipients ? ZDefaultRecipientsSchema.parse(settings.defaultRecipients) : null,
delegateDocumentOwnership: settings.delegateDocumentOwnership,
aiFeaturesEnabled: settings.aiFeaturesEnabled,
envelopeExpirationPeriod: settings.envelopeExpirationPeriod ?? null,
@@ -171,10 +150,7 @@ export const DocumentPreferencesForm = ({
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset
className="flex h-full max-w-2xl flex-col gap-y-6"
disabled={form.formState.isSubmitting}
>
<fieldset className="flex h-full max-w-2xl flex-col gap-y-6" disabled={form.formState.isSubmitting}>
{!isPersonalLayoutMode && (
<FormField
control={form.control}
@@ -264,8 +240,8 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Controls the default language of an uploaded document. This will be used as the
language in email communications with the recipients.
Controls the default language of an uploaded document. This will be used as the language in email
communications with the recipients.
</Trans>
</FormDescription>
</FormItem>
@@ -322,9 +298,7 @@ export const DocumentPreferencesForm = ({
<FormControl>
<Combobox
triggerPlaceholder={
canInherit ? t`Inherit from organisation` : t`Local timezone`
}
triggerPlaceholder={canInherit ? t`Inherit from organisation` : t`Local timezone`}
placeholder={t`Select a time zone`}
options={TIME_ZONES}
value={field.value}
@@ -358,9 +332,7 @@ export const DocumentPreferencesForm = ({
onChange={field.onChange}
className="w-full bg-background"
enableSearch={false}
emptySelectionPlaceholder={
canInherit ? t`Inherit from organisation` : t`Select signature types`
}
emptySelectionPlaceholder={canInherit ? t`Inherit from organisation` : t`Select signature types`}
testId="signature-types-trigger"
/>
</FormControl>
@@ -369,9 +341,7 @@ export const DocumentPreferencesForm = ({
<FormMessage />
) : (
<FormDescription>
<Trans>
Controls which signatures are allowed to be used when signing a document.
</Trans>
<Trans>Controls which signatures are allowed to be used when signing a document.</Trans>
</FormDescription>
)}
</FormItem>
@@ -422,15 +392,14 @@ export const DocumentPreferencesForm = ({
</FormControl>
<div className="pt-2">
<div className="text-xs font-medium text-muted-foreground">
<div className="font-medium text-muted-foreground text-xs">
<Trans>Preview</Trans>
</div>
<Alert variant="neutral" className="mt-1 px-2.5 py-1.5 text-sm">
{field.value ? (
<Trans>
"{placeholderEmail}" on behalf of "Team Name" has invited you to sign
"example document".
"{placeholderEmail}" on behalf of "Team Name" has invited you to sign "example document".
</Trans>
) : (
<Trans>"Team Name" has invited you to sign "example document".</Trans>
@@ -440,9 +409,9 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Controls the formatting of the message that will be sent when inviting a
recipient to sign a document. If a custom message has been provided while
configuring the document, it will be used instead.
Controls the formatting of the message that will be sent when inviting a recipient to sign a
document. If a custom message has been provided while configuring the document, it will be used
instead.
</Trans>
</FormDescription>
</FormItem>
@@ -494,9 +463,8 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Controls whether the signing certificate will be included in the document when
it is downloaded. The signing certificate can still be downloaded from the logs
page separately.
Controls whether the signing certificate will be included in the document when it is downloaded. The
signing certificate can still be downloaded from the logs page separately.
</Trans>
</FormDescription>
</FormItem>
@@ -544,9 +512,8 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Controls whether the audit logs will be included in the document when it is
downloaded. The audit logs can still be downloaded from the logs page
separately.
Controls whether the audit logs will be included in the document when it is downloaded. The audit
logs can still be downloaded from the logs page separately.
</Trans>
</FormDescription>
</FormItem>
@@ -602,15 +569,11 @@ export const DocumentPreferencesForm = ({
<AvatarWithText
avatarFallback={recipientAbbreviation(recipient)}
primaryText={
<span className="text-sm font-medium">
{recipient.name || recipient.email}
</span>
<span className="font-medium text-sm">{recipient.name || recipient.email}</span>
}
secondaryText={
recipient.name ? (
<span className="text-xs text-muted-foreground">
{recipient.email}
</span>
<span className="text-muted-foreground text-xs">{recipient.email}</span>
) : undefined
}
className="flex-1"
@@ -653,9 +616,7 @@ export const DocumentPreferencesForm = ({
<Select
{...field}
value={field.value === null ? '-1' : field.value.toString()}
onValueChange={(value) =>
field.onChange(value === 'true' ? true : value === 'false' ? false : null)
}
onValueChange={(value) => field.onChange(value === 'true' ? true : value === 'false' ? false : null)}
>
<SelectTrigger className="bg-background text-muted-foreground">
<SelectValue />
@@ -679,9 +640,7 @@ export const DocumentPreferencesForm = ({
</Select>
<FormDescription>
<Trans>
Enable team API tokens to delegate document ownership to another team member.
</Trans>
<Trans>Enable team API tokens to delegate document ownership to another team member.</Trans>
</FormDescription>
</FormItem>
)}
@@ -706,8 +665,8 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Controls how long recipients have to complete signing before the document
expires. After expiration, recipients can no longer sign the document.
Controls how long recipients have to complete signing before the document expires. After expiration,
recipients can no longer sign the document.
</Trans>
</FormDescription>
@@ -735,8 +694,8 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Controls when and how often reminder emails are sent to recipients who have not
yet completed signing.
Controls when and how often reminder emails are sent to recipients who have not yet completed
signing.
</Trans>
</FormDescription>
@@ -787,10 +746,9 @@ export const DocumentPreferencesForm = ({
<FormDescription>
<Trans>
Enable AI-powered features such as automatic recipient detection. When
enabled, document content will be sent to AI providers. We only use providers
that do not retain data for training and prefer European regions where
available.
Enable AI-powered features such as automatic recipient detection. When enabled, document content
will be sent to AI providers. We only use providers that do not retain data for training and
prefer European regions where available.
</Trans>
</FormDescription>
</FormItem>
@@ -1,12 +1,3 @@
import { useEffect, useMemo } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { PlusIcon, Trash } from 'lucide-react';
import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod';
import { validateCheckboxLength } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
import {
type TCheckboxFieldMeta as CheckboxFieldMeta,
@@ -20,23 +11,17 @@ import {
checkboxValidationRules,
checkboxValidationSigns,
} from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
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 { Separator } from '@documenso/ui/primitives/separator';
import { zodResolver } from '@hookform/resolvers/zod';
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { PlusIcon, Trash } from 'lucide-react';
import { useEffect, useMemo } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod';
import {
EditorGenericFontSizeField,
@@ -150,19 +135,13 @@ export const EditorFieldCheckboxForm = ({
const preselectedValues = (formValues.values || [])?.filter((value) => value.checked);
if (formValues.validationLength && formValues.validationRule && preselectedValues.length > 0) {
const validationRule = checkboxValidationSigns.find(
(sign) => sign.label === formValues.validationRule,
);
const validationRule = checkboxValidationSigns.find((sign) => sign.label === formValues.validationRule);
if (!validationRule) {
return false;
}
return validateCheckboxLength(
preselectedValues.length,
validationRule.value,
formValues.validationLength,
);
return validateCheckboxLength(preselectedValues.length, validationRule.value, formValues.validationLength);
}
return true;
@@ -251,8 +230,7 @@ export const EditorFieldCheckboxForm = ({
const currentValues = formValues.values || [];
const minimumNumberOfValuesRequired =
validationNumber - currentValues.length;
const minimumNumberOfValuesRequired = validationNumber - currentValues.length;
if (!formValues.validationRule) {
form.setValue('validationRule', checkboxValidationRules[0]);
@@ -295,12 +273,12 @@ export const EditorFieldCheckboxForm = ({
<EditorGenericReadOnlyField formControl={form.control} />
<section className="space-y-2">
<div className="-mx-4 mb-4 mt-2">
<div className="-mx-4 mt-2 mb-4">
<Separator />
</div>
<div className="flex flex-row items-center justify-between gap-2">
<p className="text-sm font-medium">
<p className="font-medium text-sm">
<Trans>Checkbox values</Trans>
</p>
@@ -335,11 +313,7 @@ export const EditorFieldCheckboxForm = ({
render={({ field }) => (
<FormItem>
<FormControl>
<Input
data-testid={`field-form-values-${index}-value`}
className="w-full"
{...field}
/>
<Input data-testid={`field-form-values-${index}-value`} className="w-full" {...field} />
</FormControl>
</FormItem>
)}
@@ -360,9 +334,7 @@ export const EditorFieldCheckboxForm = ({
{!isValidationRuleMetForPreselectedValues && (
<Alert variant="warning">
<AlertDescription>
<Trans>
The preselected values will be ignored unless they meet the validation criteria.
</Trans>
<Trans>The preselected values will be ignored unless they meet the validation criteria.</Trans>
</AlertDescription>
</Alert>
)}
@@ -1,22 +1,17 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TDateFieldMeta as DateFieldMeta,
DEFAULT_FIELD_FONT_SIZE,
FIELD_DATE_META_DEFAULT_VALUES,
FIELD_DEFAULT_GENERIC_ALIGN,
ZDateFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
EditorGenericTextAlignField,
} from './editor-field-generic-field-forms';
import { EditorGenericFontSizeField, EditorGenericTextAlignField } from './editor-field-generic-field-forms';
const ZDateFieldFormSchema = ZDateFieldMeta.pick({
fontSize: true,
@@ -1,35 +1,16 @@
import { useEffect } from 'react';
import { DEFAULT_FIELD_FONT_SIZE, type TDropdownFieldMeta as DropdownFieldMeta } from '@documenso/lib/types/field-meta';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Separator } from '@documenso/ui/primitives/separator';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { PlusIcon, Trash } from 'lucide-react';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TDropdownFieldMeta as DropdownFieldMeta,
} from '@documenso/lib/types/field-meta';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Separator } from '@documenso/ui/primitives/separator';
import {
EditorGenericFontSizeField,
EditorGenericReadOnlyField,
@@ -209,12 +190,12 @@ export const EditorFieldDropdownForm = ({
<EditorGenericReadOnlyField formControl={form.control} />
<section className="space-y-2">
<div className="-mx-4 mb-4 mt-2">
<div className="-mx-4 mt-2 mb-4">
<Separator />
</div>
<div className="flex flex-row items-center justify-between gap-2">
<p className="text-sm font-medium">
<p className="font-medium text-sm">
<Trans>Dropdown values</Trans>
</p>
@@ -1,9 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TEmailFieldMeta as EmailFieldMeta,
@@ -12,11 +6,12 @@ import {
ZEmailFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
EditorGenericTextAlignField,
} from './editor-field-generic-field-forms';
import { EditorGenericFontSizeField, EditorGenericTextAlignField } from './editor-field-generic-field-forms';
const ZEmailFieldFormSchema = ZEmailFieldMeta.pick({
fontSize: true,
@@ -1,29 +1,17 @@
import { useEffect } from 'react';
import { Trans, useLingui } from '@lingui/react/macro';
import { type Control, useFormContext } from 'react-hook-form';
import { FIELD_MIN_LINE_HEIGHT } from '@documenso/lib/types/field-meta';
import { FIELD_MAX_LINE_HEIGHT } from '@documenso/lib/types/field-meta';
import { FIELD_MIN_LETTER_SPACING } from '@documenso/lib/types/field-meta';
import { FIELD_MAX_LETTER_SPACING } from '@documenso/lib/types/field-meta';
import {
FIELD_MAX_LETTER_SPACING,
FIELD_MAX_LINE_HEIGHT,
FIELD_MIN_LETTER_SPACING,
FIELD_MIN_LINE_HEIGHT,
} from '@documenso/lib/types/field-meta';
import { cn } from '@documenso/ui/lib/utils';
import { Checkbox } from '@documenso/ui/primitives/checkbox';
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
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 { Trans, useLingui } from '@lingui/react/macro';
import { useEffect } from 'react';
import { type Control, useFormContext } from 'react-hook-form';
// Can't seem to get the non-any type to work with correct types.
// Eg Control<{ fontSize?: number } doesn't seem to work when there are required items.
@@ -259,7 +247,7 @@ export const EditorGenericRequiredField = ({
onCheckedChange={field.onChange}
/>
<label className="ml-2 text-sm text-muted-foreground" htmlFor="field-required">
<label className="ml-2 text-muted-foreground text-sm" htmlFor="field-required">
<Trans>Required Field</Trans>
</label>
</div>
@@ -303,7 +291,7 @@ export const EditorGenericReadOnlyField = ({
onCheckedChange={field.onChange}
/>
<label className="ml-2 text-sm text-muted-foreground" htmlFor="field-read-only">
<label className="ml-2 text-muted-foreground text-sm" htmlFor="field-read-only">
<Trans>Read Only</Trans>
</label>
</div>
@@ -1,9 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
@@ -11,11 +5,12 @@ import {
ZInitialsFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
EditorGenericTextAlignField,
} from './editor-field-generic-field-forms';
import { EditorGenericFontSizeField, EditorGenericTextAlignField } from './editor-field-generic-field-forms';
const ZInitialsFieldFormSchema = ZInitialsFieldMeta.pick({
fontSize: true,
@@ -1,9 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
@@ -11,11 +5,12 @@ import {
ZNameFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
EditorGenericTextAlignField,
} from './editor-field-generic-field-forms';
import { EditorGenericFontSizeField, EditorGenericTextAlignField } from './editor-field-generic-field-forms';
const ZNameFieldFormSchema = ZNameFieldMeta.pick({
fontSize: true,
@@ -1,10 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
@@ -15,23 +8,15 @@ import {
ZNumberFieldMeta,
} from '@documenso/lib/types/field-meta';
import { numberFormatValues } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
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 { Separator } from '@documenso/ui/primitives/separator';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
@@ -188,12 +173,7 @@ export const EditorFieldNumberForm = ({
<Trans>Value</Trans>
</FormLabel>
<FormControl>
<Input
data-testid="field-form-value"
className="bg-background"
placeholder={t`Value`}
{...field}
/>
<Input data-testid="field-form-value" className="bg-background" placeholder={t`Value`} {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -251,11 +231,11 @@ export const EditorFieldNumberForm = ({
{/* Validation section */}
<section className="space-y-2">
<div className="-mx-4 mb-4 mt-2">
<div className="-mx-4 mt-2 mb-4">
<Separator />
</div>
<p className="text-sm font-medium">
<p className="font-medium text-sm">
<Trans>Validation</Trans>
</p>
@@ -275,9 +255,7 @@ export const EditorFieldNumberForm = ({
placeholder={t`E.g. 0`}
{...field}
value={field.value ?? ''}
onChange={(e) =>
field.onChange(e.target.value === '' ? null : e.target.value)
}
onChange={(e) => field.onChange(e.target.value === '' ? null : e.target.value)}
/>
</FormControl>
<FormMessage />
@@ -300,9 +278,7 @@ export const EditorFieldNumberForm = ({
placeholder={t`E.g. 100`}
{...field}
value={field.value ?? ''}
onChange={(e) =>
field.onChange(e.target.value === '' ? null : e.target.value)
}
onChange={(e) => field.onChange(e.target.value === '' ? null : e.target.value)}
/>
</FormControl>
<FormMessage />
@@ -1,34 +1,19 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { PlusIcon, Trash } from 'lucide-react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TRadioFieldMeta as RadioFieldMeta,
ZRadioFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Checkbox } from '@documenso/ui/primitives/checkbox';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
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 { Separator } from '@documenso/ui/primitives/separator';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { PlusIcon, Trash } from 'lucide-react';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
@@ -93,8 +78,7 @@ export const EditorFieldRadioForm = ({
const addValue = () => {
const currentValues = form.getValues('values') || [];
const newId =
currentValues.length > 0 ? Math.max(...currentValues.map((val) => val.id)) + 1 : 1;
const newId = currentValues.length > 0 ? Math.max(...currentValues.map((val) => val.id)) + 1 : 1;
const newValues = [...currentValues, { id: newId, checked: false, value: '' }];
form.setValue('values', newValues);
@@ -166,12 +150,12 @@ export const EditorFieldRadioForm = ({
<EditorGenericReadOnlyField formControl={form.control} />
<section className="space-y-2">
<div className="-mx-4 mb-4 mt-2">
<div className="-mx-4 mt-2 mb-4">
<Separator />
</div>
<div className="flex flex-row items-center justify-between gap-2">
<p className="text-sm font-medium">
<p className="font-medium text-sm">
<Trans>Radio values</Trans>
</p>
@@ -220,11 +204,7 @@ export const EditorFieldRadioForm = ({
render={({ field }) => (
<FormItem>
<FormControl>
<Input
data-testid={`field-form-values-${index}-value`}
className="w-full"
{...field}
/>
<Input data-testid={`field-form-values-${index}-value`} className="w-full" {...field} />
</FormControl>
</FormItem>
)}
@@ -1,10 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import { DEFAULT_SIGNATURE_TEXT_FONT_SIZE } from '@documenso/lib/constants/pdf';
import {
FIELD_SIGNATURE_META_DEFAULT_VALUES,
@@ -12,6 +5,11 @@ import {
ZSignatureFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import { EditorGenericFontSizeField } from './editor-field-generic-field-forms';
@@ -66,7 +64,7 @@ export const EditorFieldSignatureForm = ({
<fieldset className="flex flex-col gap-2">
<div>
<EditorGenericFontSizeField formControl={form.control} />
<p className="mt-0.5 text-xs text-muted-foreground">
<p className="mt-0.5 text-muted-foreground text-xs">
<Trans>The typed signature font size</Trans>
</p>
</div>
@@ -1,10 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
@@ -14,16 +7,14 @@ import {
type TTextFieldMeta as TextFieldMeta,
ZTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Textarea } from '@documenso/ui/primitives/textarea';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
EditorGenericFontSizeField,
@@ -150,11 +141,7 @@ export const EditorFieldTextForm = ({
<Trans>Placeholder</Trans>
</FormLabel>
<FormControl>
<Input
data-testid="field-form-placeholder"
placeholder={t`Field placeholder`}
{...field}
/>
<Input data-testid="field-form-placeholder" placeholder={t`Field placeholder`} {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -1,15 +1,6 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { FROM_ADDRESS } from '@documenso/lib/constants/email';
import {
DEFAULT_DOCUMENT_EMAIL_SETTINGS,
ZDocumentEmailSettingsSchema,
} from '@documenso/lib/types/document-email';
import { DEFAULT_DOCUMENT_EMAIL_SETTINGS, ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import { zEmail } from '@documenso/lib/utils/zod';
import { trpc } from '@documenso/trpc/react';
import { DocumentEmailCheckboxes } from '@documenso/ui/components/document/document-email-checkboxes';
@@ -24,13 +15,12 @@ import {
FormMessage,
} from '@documenso/ui/primitives/form/form';
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 { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const ZEmailPreferencesFormSchema = z.object({
emailId: z.string().nullable(),
@@ -41,10 +31,7 @@ const ZEmailPreferencesFormSchema = z.object({
export type TEmailPreferencesFormSchema = z.infer<typeof ZEmailPreferencesFormSchema>;
type SettingsSubset = Pick<
TeamGlobalSettings,
'emailId' | 'emailReplyTo' | 'emailDocumentSettings'
>;
type SettingsSubset = Pick<TeamGlobalSettings, 'emailId' | 'emailReplyTo' | 'emailDocumentSettings'>;
export type EmailPreferencesFormProps = {
settings: SettingsSubset;
@@ -52,11 +39,7 @@ export type EmailPreferencesFormProps = {
onFormSubmit: (data: TEmailPreferencesFormSchema) => Promise<void>;
};
export const EmailPreferencesForm = ({
settings,
onFormSubmit,
canInherit,
}: EmailPreferencesFormProps) => {
export const EmailPreferencesForm = ({ settings, onFormSubmit, canInherit }: EmailPreferencesFormProps) => {
const organisation = useCurrentOrganisation();
const form = useForm<TEmailPreferencesFormSchema>({
@@ -69,21 +52,17 @@ export const EmailPreferencesForm = ({
resolver: zodResolver(ZEmailPreferencesFormSchema),
});
const { data: emailData, isLoading: isLoadingEmails } =
trpc.enterprise.organisation.email.find.useQuery({
organisationId: organisation.id,
perPage: 100,
});
const { data: emailData, isLoading: isLoadingEmails } = trpc.enterprise.organisation.email.find.useQuery({
organisationId: organisation.id,
perPage: 100,
});
const emails = emailData?.data || [];
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset
className="flex h-full max-w-2xl flex-col gap-y-6"
disabled={form.formState.isSubmitting}
>
<fieldset className="flex h-full max-w-2xl flex-col gap-y-6" disabled={form.formState.isSubmitting}>
{organisation.organisationClaim.flags.emailDomains && (
<FormField
control={form.control}
@@ -145,9 +124,7 @@ export const EmailPreferencesForm = ({
</FormControl>
<FormMessage />
<FormDescription>
<Trans>
The email address which will show up in the "Reply To" field in emails
</Trans>
<Trans>The email address which will show up in the "Reply To" field in emails</Trans>
{canInherit && (
<span>
@@ -188,9 +165,7 @@ export const EmailPreferencesForm = ({
<Select
value={field.value === null ? 'INHERIT' : 'CONTROLLED'}
onValueChange={(value) =>
field.onChange(
value === 'CONTROLLED' ? DEFAULT_DOCUMENT_EMAIL_SETTINGS : null,
)
field.onChange(value === 'CONTROLLED' ? DEFAULT_DOCUMENT_EMAIL_SETTINGS : null)
}
>
<SelectTrigger>
@@ -220,8 +195,8 @@ export const EmailPreferencesForm = ({
<FormDescription>
<Trans>
Controls the default email settings when new documents or templates are created.
Updating these settings will not affect existing documents or templates.
Controls the default email settings when new documents or templates are created. Updating these
settings will not affect existing documents or templates.
</Trans>
</FormDescription>
</FormItem>
@@ -1,3 +1,10 @@
import { authClient } from '@documenso/auth/client';
import { zEmail } from '@documenso/lib/utils/zod';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -6,21 +13,6 @@ import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { zEmail } from '@documenso/lib/utils/zod';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZForgotPasswordFormSchema = z.object({
email: zEmail().min(1),
});
@@ -64,10 +56,7 @@ export const ForgotPasswordForm = ({ className }: ForgotPasswordFormProps) => {
return (
<Form {...form}>
<form
className={cn('flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className={cn('flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
@@ -1,3 +1,13 @@
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import { ZUpdateOrganisationRequestSchema } from '@documenso/trpc/server/organisation-router/update-organisation.types';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -7,24 +17,6 @@ import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';
import type { z } from 'zod';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import { ZUpdateOrganisationRequestSchema } from '@documenso/trpc/server/organisation-router/update-organisation.types';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
const ZOrganisationUpdateFormSchema = ZUpdateOrganisationRequestSchema.shape.data.pick({
name: true,
url: true,
@@ -131,7 +123,7 @@ export const OrganisationUpdateForm = () => {
<Input className="bg-background" {...field} />
</FormControl>
{!form.formState.errors.url && (
<span className="text-foreground/50 text-xs font-normal">
<span className="font-normal text-foreground/50 text-xs">
{field.value ? (
`${NEXT_PUBLIC_WEBAPP_URL()}/o/${field.value}`
) : (
+13 -28
View File
@@ -1,3 +1,12 @@
import { authClient } from '@documenso/auth/client';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { AppError } from '@documenso/lib/errors/app-error';
import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -6,23 +15,6 @@ import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { AppError } from '@documenso/lib/errors/app-error';
import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZPasswordFormSchema = z
.object({
currentPassword: ZCurrentPasswordSchema,
@@ -76,13 +68,9 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
const errorMessage = match(error.code)
.with('NO_PASSWORD', () => msg`User has no password.`)
.with('INCORRECT_PASSWORD', () => msg`Current password is incorrect.`)
.with(
'SAME_PASSWORD',
() => msg`Your new password cannot be the same as your old password.`,
)
.with('SAME_PASSWORD', () => msg`Your new password cannot be the same as your old password.`)
.otherwise(
() =>
msg`We encountered an unknown error while attempting to update your password. Please try again later.`,
() => msg`We encountered an unknown error while attempting to update your password. Please try again later.`,
);
toast({
@@ -95,10 +83,7 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
return (
<Form {...form}>
<form
className={cn('flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className={cn('flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
@@ -149,7 +134,7 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
/>
</fieldset>
<div className="ml-auto mt-4">
<div className="mt-4 ml-auto">
<Button type="submit" loading={isSubmitting}>
{isSubmitting ? <Trans>Updating password...</Trans> : <Trans>Update password</Trans>}
</Button>
+11 -22
View File
@@ -1,3 +1,13 @@
import { useSession } from '@documenso/lib/client-only/providers/session';
import { ZNameSchema } from '@documenso/lib/constants/auth';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -5,24 +15,6 @@ import { Trans } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { ZNameSchema } from '@documenso/lib/constants/auth';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZProfileFormSchema = z.object({
name: ZNameSchema,
signature: z.string().min(1, { message: msg`Signature Pad cannot be empty.`.id }),
@@ -83,10 +75,7 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
return (
<Form {...form}>
<form
className={cn('flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className={cn('flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
@@ -1,38 +1,25 @@
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Plural, Trans } from '@lingui/react/macro';
import type { TeamProfile } from '@prisma/client';
import { motion } from 'framer-motion';
import { AnimatePresence } from 'framer-motion';
import { CheckSquareIcon, CopyIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
import type { z } from 'zod';
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { isPersonalLayout } from '@documenso/lib/utils/organisations';
import { formatUserProfilePath } from '@documenso/lib/utils/public-profiles';
import {
MAX_PROFILE_BIO_LENGTH,
ZUpdateTeamRequestSchema,
} from '@documenso/trpc/server/team-router/update-team.types';
import { MAX_PROFILE_BIO_LENGTH, ZUpdateTeamRequestSchema } from '@documenso/trpc/server/team-router/update-team.types';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Textarea } from '@documenso/ui/primitives/textarea';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Plural, Trans } from '@lingui/react/macro';
import type { TeamProfile } from '@prisma/client';
import { AnimatePresence, motion } from 'framer-motion';
import { CheckSquareIcon, CopyIcon } from 'lucide-react';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import type { z } from 'zod';
import { useCurrentTeam } from '~/providers/team';
@@ -48,11 +35,7 @@ export type PublicProfileFormProps = {
onProfileUpdate: (data: TPublicProfileFormSchema) => Promise<unknown>;
profile: TeamProfile;
};
export const PublicProfileForm = ({
className,
profile,
onProfileUpdate,
}: PublicProfileFormProps) => {
export const PublicProfileForm = ({ className, profile, onProfileUpdate }: PublicProfileFormProps) => {
const { _ } = useLingui();
const { toast } = useToast();
@@ -137,10 +120,7 @@ export const PublicProfileForm = ({
return (
<Form {...form}>
<form
className={cn('flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className={cn('flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
@@ -156,22 +136,19 @@ export const PublicProfileForm = ({
{!isPersonalLayoutMode && (
<p className="text-muted-foreground text-xs">
<Trans>
You can update the profile URL by updating the team URL in the general
settings page.
</Trans>
<Trans>You can update the profile URL by updating the team URL in the general settings page.</Trans>
</p>
)}
<div className="h-8">
{!form.formState.errors.url && (
<div className="text-muted-foreground h-8 text-sm">
<div className="h-8 text-muted-foreground text-sm">
{field.value ? (
<div>
<Button
type="button"
variant="none"
className="h-7 rounded bg-neutral-50 pl-2 pr-0.5 font-normal dark:border dark:border-neutral-500 dark:bg-neutral-600"
className="h-7 rounded bg-neutral-50 pr-0.5 pl-2 font-normal dark:border dark:border-neutral-500 dark:bg-neutral-600"
onClick={async () => onCopy()}
>
<p>
@@ -222,10 +199,7 @@ export const PublicProfileForm = ({
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
{...field}
placeholder={_(msg`Write a description to display on your public profile`)}
/>
<Textarea {...field} placeholder={_(msg`Write a description to display on your public profile`)} />
</FormControl>
{!form.formState.errors.profileBio && (
@@ -1,3 +1,11 @@
import { authClient } from '@documenso/auth/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -7,22 +15,6 @@ import { useNavigate } from 'react-router';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZResetPasswordFormSchema = z
.object({
password: ZPasswordSchema,
@@ -78,13 +70,9 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps)
const errorMessage = match(error.code)
.with(AppErrorCode.EXPIRED_CODE, () => msg`Token has expired. Please try again.`)
.with('INVALID_TOKEN', () => msg`Invalid token provided. Please try again.`)
.with(
'SAME_PASSWORD',
() => msg`Your new password cannot be the same as your old password.`,
)
.with('SAME_PASSWORD', () => msg`Your new password cannot be the same as your old password.`)
.otherwise(
() =>
msg`We encountered an unknown error while attempting to reset your password. Please try again later.`,
() => msg`We encountered an unknown error while attempting to reset your password. Please try again later.`,
);
toast({
@@ -97,10 +85,7 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps)
return (
<Form {...form}>
<form
className={cn('flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className={cn('flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
@@ -1,9 +1,7 @@
import React, { useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router';
import { useSearchParams } from 'react-router';
import { Select, SelectContent, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import type React from 'react';
import { useMemo } from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router';
export type SearchParamSelector = {
paramKey: string;
@@ -41,7 +39,7 @@ export const SearchParamSelector = ({ children, paramKey, isValueValid }: Search
return (
<Select defaultValue={value} onValueChange={onValueChange}>
<SelectTrigger className="text-muted-foreground max-w-[200px]">
<SelectTrigger className="max-w-[200px] text-muted-foreground">
<SelectValue />
</SelectTrigger>
@@ -1,3 +1,10 @@
import { authClient } from '@documenso/auth/client';
import { zEmail } from '@documenso/lib/utils/zod';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -5,21 +12,6 @@ import { Trans } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { zEmail } from '@documenso/lib/utils/zod';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZSendConfirmationEmailFormSchema = z.object({
email: zEmail().min(1),
});
@@ -49,9 +41,7 @@ export const SendConfirmationEmailForm = ({ className }: SendConfirmationEmailFo
toast({
title: _(msg`Confirmation email sent`),
description: _(
msg`A confirmation email has been sent, and it should arrive in your inbox shortly.`,
),
description: _(msg`A confirmation email has been sent, and it should arrive in your inbox shortly.`),
duration: 5000,
});
@@ -67,10 +57,7 @@ export const SendConfirmationEmailForm = ({ className }: SendConfirmationEmailFo
return (
<Form {...form}>
<form
className={cn('mt-6 flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className={cn('mt-6 flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
+37 -102
View File
@@ -1,21 +1,3 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { TurnstileInstance } from '@marsidev/react-turnstile';
import { Turnstile } from '@marsidev/react-turnstile';
import { browserSupportsWebAuthn, startAuthentication } from '@simplewebauthn/browser';
import { KeyRoundIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { FaIdCardClip } from 'react-icons/fa6';
import { FcGoogle } from 'react-icons/fc';
import { Link, useNavigate } from 'react-router';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
import { AuthenticationErrorCode } from '@documenso/auth/server/lib/errors/error-codes';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
@@ -25,25 +7,28 @@ import { trpc } from '@documenso/trpc/react';
import { ZCurrentPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@documenso/ui/primitives/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@documenso/ui/primitives/dialog';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { PinInput, PinInputGroup, PinInputSlot } from '@documenso/ui/primitives/pin-input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { TurnstileInstance } from '@marsidev/react-turnstile';
import { Turnstile } from '@marsidev/react-turnstile';
import { browserSupportsWebAuthn, startAuthentication } from '@simplewebauthn/browser';
import { KeyRoundIcon } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { FaIdCardClip } from 'react-icons/fa6';
import { FcGoogle } from 'react-icons/fc';
import { Link, useNavigate } from 'react-router';
import { match } from 'ts-pattern';
import { z } from 'zod';
const CommonErrorMessages: Record<string, MessageDescriptor> = {
[AuthenticationErrorCode.AccountDisabled]: msg`This account has been disabled. Please contact support.`,
@@ -94,13 +79,10 @@ export const SignInForm = ({
const navigate = useNavigate();
const [isTwoFactorAuthenticationDialogOpen, setIsTwoFactorAuthenticationDialogOpen] =
useState(false);
const [isTwoFactorAuthenticationDialogOpen, setIsTwoFactorAuthenticationDialogOpen] = useState(false);
const [isEmbeddedRedirect, setIsEmbeddedRedirect] = useState(false);
const [twoFactorAuthenticationMethod, setTwoFactorAuthenticationMethod] = useState<
'totp' | 'backup'
>('totp');
const [twoFactorAuthenticationMethod, setTwoFactorAuthenticationMethod] = useState<'totp' | 'backup'>('totp');
const hasSocialAuthEnabled = isGoogleSSOEnabled || isMicrosoftSSOEnabled || isOIDCSSOEnabled;
@@ -127,8 +109,7 @@ export const SignInForm = ({
return url.toString();
}, [returnTo]);
const { mutateAsync: createPasskeySigninOptions } =
trpc.auth.passkey.createSigninOptions.useMutation();
const { mutateAsync: createPasskeySigninOptions } = trpc.auth.passkey.createSigninOptions.useMutation();
const form = useForm<TSignInFormSchema>({
values: {
@@ -203,10 +184,7 @@ export const SignInForm = ({
() =>
msg`This passkey is not configured for this application. Please login and add one in the user settings.`,
)
.with(
AuthenticationErrorCode.SessionExpired,
() => msg`This session has expired. Please try again.`,
)
.with(AuthenticationErrorCode.SessionExpired, () => msg`This session has expired. Please try again.`)
.otherwise(() => handleFallbackErrorMessages(error.code));
toast({
@@ -248,27 +226,19 @@ export const SignInForm = ({
toast({
title: _(msg`Unable to sign in`),
description: _(
msg`This account has not been verified. Please verify your account before signing in.`,
),
description: _(msg`This account has not been verified. Please verify your account before signing in.`),
});
return;
}
const errorMessage = match(error.code)
.with(
AuthenticationErrorCode.InvalidCredentials,
() => msg`The email or password provided is incorrect.`,
)
.with(AuthenticationErrorCode.InvalidCredentials, () => msg`The email or password provided is incorrect.`)
.with(
AuthenticationErrorCode.InvalidTwoFactorCode,
() => msg`The two-factor authentication code provided is incorrect.`,
)
.with(
AppErrorCode.INVALID_CAPTCHA,
() => msg`We were unable to verify that you're human. Please try again.`,
)
.with(AppErrorCode.INVALID_CAPTCHA, () => msg`We were unable to verify that you're human. Please try again.`)
.otherwise(() => handleFallbackErrorMessages(error.code));
toast({
@@ -290,9 +260,7 @@ export const SignInForm = ({
} catch (err) {
toast({
title: _(msg`An unknown error occurred`),
description: _(
msg`We encountered an unknown error while attempting to sign you In. Please try again later.`,
),
description: _(msg`We encountered an unknown error while attempting to sign you In. Please try again later.`),
variant: 'destructive',
});
}
@@ -306,9 +274,7 @@ export const SignInForm = ({
} catch (err) {
toast({
title: _(msg`An unknown error occurred`),
description: _(
msg`We encountered an unknown error while attempting to sign you In. Please try again later.`,
),
description: _(msg`We encountered an unknown error while attempting to sign you In. Please try again later.`),
variant: 'destructive',
});
}
@@ -322,9 +288,7 @@ export const SignInForm = ({
} catch (err) {
toast({
title: _(msg`An unknown error occurred`),
description: _(
msg`We encountered an unknown error while attempting to sign you In. Please try again later.`,
),
description: _(msg`We encountered an unknown error while attempting to sign you In. Please try again later.`),
variant: 'destructive',
});
}
@@ -346,14 +310,8 @@ export const SignInForm = ({
return (
<Form {...form}>
<form
className={cn('flex w-full flex-col gap-y-4', className)}
onSubmit={form.handleSubmit(onFormSubmit)}
>
<fieldset
className="flex w-full flex-col gap-y-4"
disabled={isSubmitting || isPasskeyLoading}
>
<form className={cn('flex w-full flex-col gap-y-4', className)} onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting || isPasskeyLoading}>
<FormField
control={form.control}
name="email"
@@ -388,10 +346,7 @@ export const SignInForm = ({
<FormMessage />
<p className="mt-2 text-right">
<Link
to="/forgot-password"
className="text-sm text-muted-foreground duration-200 hover:opacity-70"
>
<Link to="/forgot-password" className="text-muted-foreground text-sm duration-200 hover:opacity-70">
<Trans>Forgot your password?</Trans>
</Link>
</p>
@@ -412,12 +367,7 @@ export const SignInForm = ({
/>
)}
<Button
type="submit"
size="lg"
loading={isSubmitting}
className="dark:bg-documenso dark:hover:opacity-90"
>
<Button type="submit" size="lg" loading={isSubmitting} className="dark:bg-documenso dark:hover:opacity-90">
{isSubmitting ? <Trans>Signing in...</Trans> : <Trans>Sign In</Trans>}
</Button>
@@ -456,11 +406,7 @@ export const SignInForm = ({
disabled={isSubmitting}
onClick={onSignInWithMicrosoftClick}
>
<img
className="mr-2 h-4 w-4"
alt="Microsoft Logo"
src={'/static/microsoft.svg'}
/>
<img className="mr-2 h-4 w-4" alt="Microsoft Logo" src={'/static/microsoft.svg'} />
Microsoft
</Button>
)}
@@ -490,16 +436,13 @@ export const SignInForm = ({
className="border bg-background text-muted-foreground"
onClick={onSignInWithPasskey}
>
{!isPasskeyLoading && <KeyRoundIcon className="-ml-1 mr-1 h-5 w-5" />}
{!isPasskeyLoading && <KeyRoundIcon className="mr-1 -ml-1 h-5 w-5" />}
<Trans>Passkey</Trans>
</Button>
</fieldset>
</form>
<Dialog
open={isTwoFactorAuthenticationDialogOpen}
onOpenChange={onCloseTwoFactorAuthenticationDialog}
>
<Dialog open={isTwoFactorAuthenticationDialogOpen} onOpenChange={onCloseTwoFactorAuthenticationDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle>
@@ -567,11 +510,7 @@ export const SignInForm = ({
)}
<DialogFooter className="mt-4">
<Button
type="button"
variant="secondary"
onClick={onToggleTwoFactorAuthenticationMethodClick}
>
<Button type="button" variant="secondary" onClick={onToggleTwoFactorAuthenticationMethodClick}>
{twoFactorAuthenticationMethod === 'totp' ? (
<Trans>Use Backup Code</Trans>
) : (
@@ -579,11 +518,7 @@ export const SignInForm = ({
)}
</Button>
<Button
type="submit"
loading={isSubmitting}
disabled={Boolean(turnstileSiteKey) && !captchaToken}
>
<Button type="submit" loading={isSubmitting} disabled={Boolean(turnstileSiteKey) && !captchaToken}>
{isSubmitting ? <Trans>Signing in...</Trans> : <Trans>Sign In</Trans>}
</Button>
</DialogFooter>
+30 -65
View File
@@ -1,18 +1,3 @@
import { useEffect, useRef, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { TurnstileInstance } from '@marsidev/react-turnstile';
import { Turnstile } from '@marsidev/react-turnstile';
import { useForm } from 'react-hook-form';
import { FaIdCardClip } from 'react-icons/fa6';
import { FcGoogle } from 'react-icons/fc';
import { Link, useNavigate, useSearchParams } from 'react-router';
import { z } from 'zod';
import communityCardsImage from '@documenso/assets/images/community-cards.png';
import { authClient } from '@documenso/auth/client';
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
@@ -23,18 +8,24 @@ import { zEmail } from '@documenso/lib/utils/zod';
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { TurnstileInstance } from '@marsidev/react-turnstile';
import { Turnstile } from '@marsidev/react-turnstile';
import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { FaIdCardClip } from 'react-icons/fa6';
import { FcGoogle } from 'react-icons/fc';
import { Link, useNavigate, useSearchParams } from 'react-router';
import { z } from 'zod';
import { UserProfileTimur } from '~/components/general/user-profile-timur';
@@ -138,8 +129,7 @@ export const SignUpForm = ({
} catch (err) {
const error = AppError.parseError(err);
const errorMessage =
SIGNUP_ERROR_MESSAGES[error.code] ?? SIGNUP_ERROR_MESSAGES.INVALID_REQUEST;
const errorMessage = SIGNUP_ERROR_MESSAGES[error.code] ?? SIGNUP_ERROR_MESSAGES.INVALID_REQUEST;
toast({
title: _(msg`An error occurred`),
@@ -158,9 +148,7 @@ export const SignUpForm = ({
} catch (err) {
toast({
title: _(msg`An unknown error occurred`),
description: _(
msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`,
),
description: _(msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`),
variant: 'destructive',
});
}
@@ -172,9 +160,7 @@ export const SignUpForm = ({
} catch (err) {
toast({
title: _(msg`An unknown error occurred`),
description: _(
msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`,
),
description: _(msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`),
variant: 'destructive',
});
}
@@ -186,9 +172,7 @@ export const SignUpForm = ({
} catch (err) {
toast({
title: _(msg`An unknown error occurred`),
description: _(
msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`,
),
description: _(msg`We encountered an unknown error while attempting to sign you Up. Please try again later.`),
variant: 'destructive',
});
}
@@ -220,15 +204,12 @@ export const SignUpForm = ({
<div className="absolute -inset-8 -z-[1] bg-background/50 backdrop-blur-[2px]" />
<div className="relative flex h-full w-full flex-col items-center justify-evenly">
<div className="rounded-2xl border bg-background px-4 py-1 text-sm font-medium">
<div className="rounded-2xl border bg-background px-4 py-1 font-medium text-sm">
<Trans>User profiles are here!</Trans>
</div>
<div className="w-full max-w-md">
<UserProfileTimur
rows={2}
className="rounded-2xl border border-border bg-background shadow-md"
/>
<UserProfileTimur rows={2} className="rounded-2xl border border-border bg-background shadow-md" />
</div>
<div />
@@ -237,14 +218,14 @@ export const SignUpForm = ({
<div className="relative z-10 flex min-h-[min(850px,80vh)] w-full max-w-lg flex-col rounded-xl border border-border bg-neutral-100 p-6 dark:bg-background">
<div className="h-20">
<h1 className="text-xl font-semibold md:text-2xl">
<h1 className="font-semibold text-xl md:text-2xl">
<Trans>Create a new account</Trans>
</h1>
<p className="mt-2 text-xs text-muted-foreground md:text-sm">
<p className="mt-2 text-muted-foreground text-xs md:text-sm">
<Trans>
Create your account and start using state-of-the-art document signing. Open and
beautiful signing is within your grasp.
Create your account and start using state-of-the-art document signing. Open and beautiful signing is
within your grasp.
</Trans>
</p>
</div>
@@ -252,10 +233,7 @@ export const SignUpForm = ({
<hr className="-mx-6 my-4" />
<Form {...form}>
<form
className="flex w-full flex-1 flex-col gap-y-4"
onSubmit={form.handleSubmit(onFormSubmit)}
>
<form className="flex w-full flex-1 flex-col gap-y-4" onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
<FormField
control={form.control}
@@ -316,11 +294,7 @@ export const SignUpForm = ({
<Trans>Sign Here</Trans>
</FormLabel>
<FormControl>
<SignaturePadDialog
disabled={isSubmitting}
value={value}
onChange={(v) => onChange(v ?? '')}
/>
<SignaturePadDialog disabled={isSubmitting} value={value} onChange={(v) => onChange(v ?? '')} />
</FormControl>
<FormMessage />
@@ -374,11 +348,7 @@ export const SignUpForm = ({
disabled={isSubmitting}
onClick={onSignUpWithMicrosoftClick}
>
<img
className="mr-2 h-4 w-4"
alt="Microsoft Logo"
src={'/static/microsoft.svg'}
/>
<img className="mr-2 h-4 w-4" alt="Microsoft Logo" src={'/static/microsoft.svg'} />
<Trans>Sign Up with Microsoft</Trans>
</Button>
)}
@@ -397,7 +367,7 @@ export const SignUpForm = ({
</Button>
)}
<p className="mt-4 text-sm text-muted-foreground">
<p className="mt-4 text-muted-foreground text-sm">
<Trans>
Already have an account?{' '}
<Link to="/signin" className="text-documenso-700 duration-200 hover:opacity-70">
@@ -407,17 +377,12 @@ export const SignUpForm = ({
</p>
</fieldset>
<Button
loading={form.formState.isSubmitting}
type="submit"
size="lg"
className="mt-6 w-full"
>
<Button loading={form.formState.isSubmitting} type="submit" size="lg" className="mt-6 w-full">
<Trans>Create account</Trans>
</Button>
</form>
</Form>
<p className="mt-6 text-xs text-muted-foreground">
<p className="mt-6 text-muted-foreground text-xs">
<Trans>
By proceeding, you agree to our{' '}
<Link
@@ -1,10 +1,3 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import type { SubscriptionClaim } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { Link } from 'react-router';
import type { z } from 'zod';
import type { TLicenseClaim } from '@documenso/lib/types/license';
import { SUBSCRIPTION_CLAIM_FEATURE_FLAGS } from '@documenso/lib/types/subscription';
import { ZCreateSubscriptionClaimRequestSchema } from '@documenso/trpc/server/admin-router/create-subscription-claim.types';
@@ -20,6 +13,12 @@ import {
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import type { SubscriptionClaim } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { Link } from 'react-router';
import type { z } from 'zod';
export type SubscriptionClaimFormValues = z.infer<typeof ZCreateSubscriptionClaimRequestSchema>;
@@ -152,42 +151,39 @@ export const SubscriptionClaimForm = ({
</FormLabel>
<div className="mt-2 space-y-2 rounded-md border p-4">
{Object.values(SUBSCRIPTION_CLAIM_FEATURE_FLAGS).map(
({ key, label, isEnterprise }) => {
const isRestrictedFeature =
isEnterprise && !licenseFlags?.[key as keyof TLicenseClaim]; // eslint-disable-line @typescript-eslint/consistent-type-assertions
{Object.values(SUBSCRIPTION_CLAIM_FEATURE_FLAGS).map(({ key, label, isEnterprise }) => {
const isRestrictedFeature = isEnterprise && !licenseFlags?.[key as keyof TLicenseClaim]; // eslint-disable-line @typescript-eslint/consistent-type-assertions
return (
<FormField
key={key}
control={form.control}
name={`flags.${key}`}
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<div className="flex items-center">
<Checkbox
id={`flag-${key}`}
checked={field.value}
onCheckedChange={field.onChange}
disabled={isRestrictedFeature && !field.value} // Allow disabling of restricted features.
/>
return (
<FormField
key={key}
control={form.control}
name={`flags.${key}`}
render={({ field }) => (
<FormItem className="flex items-center space-x-2">
<FormControl>
<div className="flex items-center">
<Checkbox
id={`flag-${key}`}
checked={field.value}
onCheckedChange={field.onChange}
disabled={isRestrictedFeature && !field.value} // Allow disabling of restricted features.
/>
<label
className="ml-2 flex flex-row items-center text-sm text-muted-foreground"
htmlFor={`flag-${key}`}
>
{label}
{isRestrictedFeature && ' ¹'}
</label>
</div>
</FormControl>
</FormItem>
)}
/>
);
},
)}
<label
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
htmlFor={`flag-${key}`}
>
{label}
{isRestrictedFeature && ' ¹'}
</label>
</div>
</FormControl>
</FormItem>
)}
/>
);
})}
</div>
{hasRestrictedEnterpriseFeatures && (
@@ -1,22 +1,14 @@
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Textarea } from '@documenso/ui/primitives/textarea';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { Textarea } from '@documenso/ui/primitives/textarea';
import { useToast } from '@documenso/ui/primitives/use-toast';
const ZSupportTicketSchema = z.object({
subject: z.string().min(3, 'Subject is required'),
message: z.string().min(10, 'Message must be at least 10 characters'),
@@ -31,17 +23,11 @@ export type SupportTicketFormProps = {
onClose?: () => void;
};
export const SupportTicketForm = ({
organisationId,
teamId,
onSuccess,
onClose,
}: SupportTicketFormProps) => {
export const SupportTicketForm = ({ organisationId, teamId, onSuccess, onClose }: SupportTicketFormProps) => {
const { t } = useLingui();
const { toast } = useToast();
const { mutateAsync: submitSupportTicket, isPending } =
trpc.profile.submitSupportTicket.useMutation();
const { mutateAsync: submitSupportTicket, isPending } = trpc.profile.submitSupportTicket.useMutation();
const form = useForm<TSupportTicket>({
resolver: zodResolver(ZSupportTicketSchema),
@@ -84,55 +70,53 @@ export const SupportTicketForm = ({
};
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<fieldset disabled={isLoading} className="flex flex-col gap-4">
<FormField
control={form.control}
name="subject"
render={({ field }) => (
<FormItem>
<FormLabel required>
<Trans>Subject</Trans>
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<fieldset disabled={isLoading} className="flex flex-col gap-4">
<FormField
control={form.control}
name="subject"
render={({ field }) => (
<FormItem>
<FormLabel required>
<Trans>Subject</Trans>
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="message"
render={({ field }) => (
<FormItem>
<FormLabel required>
<Trans>Message</Trans>
</FormLabel>
<FormControl>
<Textarea rows={5} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="message"
render={({ field }) => (
<FormItem>
<FormLabel required>
<Trans>Message</Trans>
</FormLabel>
<FormControl>
<Textarea rows={5} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="mt-2 flex flex-row gap-2">
<Button type="submit" size="sm" loading={isLoading}>
<Trans>Submit</Trans>
<div className="mt-2 flex flex-row gap-2">
<Button type="submit" size="sm" loading={isLoading}>
<Trans>Submit</Trans>
</Button>
{onClose && (
<Button variant="outline" size="sm" type="button" onClick={onClose}>
<Trans>Close</Trans>
</Button>
{onClose && (
<Button variant="outline" size="sm" type="button" onClick={onClose}>
<Trans>Close</Trans>
</Button>
)}
</div>
</fieldset>
</form>
</Form>
</>
)}
</div>
</fieldset>
</form>
</Form>
);
};
@@ -1,3 +1,11 @@
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import { ZUpdateTeamRequestSchema } from '@documenso/trpc/server/team-router/update-team.types';
import { Button } from '@documenso/ui/primitives/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -7,22 +15,6 @@ import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';
import type { z } from 'zod';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import { ZUpdateTeamRequestSchema } from '@documenso/trpc/server/team-router/update-team.types';
import { Button } from '@documenso/ui/primitives/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
export type UpdateTeamDialogProps = {
teamId: number;
teamName: string;
@@ -129,7 +121,7 @@ export const TeamUpdateForm = ({ teamId, teamName, teamUrl }: UpdateTeamDialogPr
<Input className="bg-background" {...field} />
</FormControl>
{!form.formState.errors.url && (
<span className="text-foreground/50 text-xs font-normal">
<span className="font-normal text-foreground/50 text-xs">
{field.value ? (
`${NEXT_PUBLIC_WEBAPP_URL()}/t/${field.value}`
) : (
+39 -63
View File
@@ -1,15 +1,3 @@
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { ApiToken } from '@prisma/client';
import { AnimatePresence, motion } from 'framer-motion';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import type { z } from 'zod';
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
@@ -27,15 +15,19 @@ import {
FormMessage,
} from '@documenso/ui/primitives/form/form';
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 { Switch } from '@documenso/ui/primitives/switch';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { ApiToken } from '@prisma/client';
import { AnimatePresence, motion } from 'framer-motion';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import type { z } from 'zod';
import { useCurrentTeam } from '~/providers/team';
@@ -129,10 +121,7 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
const error = AppError.parseError(err);
const errorMessage = match(error.code)
.with(
AppErrorCode.UNAUTHORIZED,
() => msg`You do not have permission to create a token for this team.`,
)
.with(AppErrorCode.UNAUTHORIZED, () => msg`You do not have permission to create a token for this team.`)
.otherwise(() => msg`Something went wrong. Please try again later.`);
toast({
@@ -148,10 +137,7 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
<div className={cn(className)}>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<fieldset
className="mt-6 flex w-full flex-col gap-4"
disabled={form.formState.isSubmitting}
>
<fieldset className="mt-6 flex w-full flex-col gap-4" disabled={form.formState.isSubmitting}>
<FormField
control={form.control}
name="tokenName"
@@ -168,10 +154,7 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
</div>
<FormDescription className="text-xs italic">
<Trans>
Please enter a meaningful name for your token. This will help you identify it
later.
</Trans>
<Trans>Please enter a meaningful name for your token. This will help you identify it later.</Trans>
</FormDescription>
<FormMessage />
@@ -225,11 +208,7 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
</div>
</div>
<Button
type="submit"
className="hidden md:inline-flex"
loading={form.formState.isSubmitting}
>
<Button type="submit" className="hidden md:inline-flex" loading={form.formState.isSubmitting}>
<Trans>Create token</Trans>
</Button>
@@ -243,35 +222,32 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
</Form>
<AnimatePresence>
{newlyCreatedToken &&
tokens &&
tokens.find((token) => token.id === newlyCreatedToken.id) && (
<motion.div
className="mt-8"
initial={{ opacity: 0, y: -40 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 40 }}
>
<Card gradient>
<CardContent className="p-4">
<p className="mt-2 text-sm text-muted-foreground">
<Trans>
Your token was created successfully! Make sure to copy it because you won't be
able to see it again!
</Trans>
</p>
{newlyCreatedToken && tokens && tokens.find((token) => token.id === newlyCreatedToken.id) && (
<motion.div
className="mt-8"
initial={{ opacity: 0, y: -40 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 40 }}
>
<Card gradient>
<CardContent className="p-4">
<p className="mt-2 text-muted-foreground text-sm">
<Trans>
Your token was created successfully! Make sure to copy it because you won't be able to see it again!
</Trans>
</p>
<p className="my-4 rounded-md bg-muted-foreground/10 px-2.5 py-1 font-mono text-sm">
{newlyCreatedToken.token}
</p>
<p className="my-4 rounded-md bg-muted-foreground/10 px-2.5 py-1 font-mono text-sm">
{newlyCreatedToken.token}
</p>
<Button variant="outline" onClick={() => void copyToken(newlyCreatedToken.token)}>
<Trans>Copy token</Trans>
</Button>
</CardContent>
</Card>
</motion.div>
)}
<Button variant="outline" onClick={() => void copyToken(newlyCreatedToken.token)}>
<Trans>Copy token</Trans>
</Button>
</CardContent>
</Card>
</motion.div>
)}
</AnimatePresence>
</div>
);