From daf01ac77b2d2c3c45ae498cb22a4bbae96aad91 Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Mon, 15 Jun 2026 15:59:52 +0300 Subject: [PATCH] refactor: replace string validations with ZNameSchema across multiple components for consistency --- .../app/components/dialogs/passkey-create-dialog.tsx | 4 ++-- .../components/dialogs/team-email-update-dialog.tsx | 10 ++++++---- .../app/components/forms/email-transport-form.tsx | 5 +++-- apps/remix/app/components/general/claim-account.tsx | 4 ++-- .../tables/settings-security-passkey-table-actions.tsx | 3 ++- .../_authenticated+/o.$orgUrl.settings.groups.$id.tsx | 5 ++--- .../admin-router/create-subscription-claim.types.ts | 3 ++- .../email-transport/create-email-transport.types.ts | 5 +++-- .../email-transport/update-email-transport.types.ts | 5 +++-- packages/trpc/server/admin-router/update-user.types.ts | 3 ++- .../trpc/server/auth-router/create-passkey.types.ts | 3 ++- .../trpc/server/auth-router/update-passkey.types.ts | 3 ++- .../create-organisation-email.types.ts | 3 ++- .../update-organisation-group.types.ts | 3 ++- 14 files changed, 35 insertions(+), 24 deletions(-) diff --git a/apps/remix/app/components/dialogs/passkey-create-dialog.tsx b/apps/remix/app/components/dialogs/passkey-create-dialog.tsx index 5a51cd8d4..3fe62a653 100644 --- a/apps/remix/app/components/dialogs/passkey-create-dialog.tsx +++ b/apps/remix/app/components/dialogs/passkey-create-dialog.tsx @@ -1,5 +1,6 @@ import { MAXIMUM_PASSKEYS } from '@documenso/lib/constants/auth'; import { AppError } from '@documenso/lib/errors/app-error'; +import { ZNameSchema } from '@documenso/lib/types/name'; import { trpc } from '@documenso/trpc/react'; import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert'; import { Button } from '@documenso/ui/primitives/button'; @@ -25,14 +26,13 @@ import { useForm } from 'react-hook-form'; import { match } from 'ts-pattern'; import { UAParser } from 'ua-parser-js'; import { z } from 'zod'; - export type PasskeyCreateDialogProps = { trigger?: React.ReactNode; onSuccess?: () => void; } & Omit; const ZCreatePasskeyFormSchema = z.object({ - passkeyName: z.string().min(3), + passkeyName: ZNameSchema, }); type TCreatePasskeyFormSchema = z.infer; diff --git a/apps/remix/app/components/dialogs/team-email-update-dialog.tsx b/apps/remix/app/components/dialogs/team-email-update-dialog.tsx index 6eb5b0e88..3fbddc3c2 100644 --- a/apps/remix/app/components/dialogs/team-email-update-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-email-update-dialog.tsx @@ -1,4 +1,5 @@ import { trpc } from '@documenso/trpc/react'; +import { ZUpdateTeamEmailMutationSchema } from '@documenso/trpc/server/team-router/schema'; import { Button } from '@documenso/ui/primitives/button'; import { Dialog, @@ -19,16 +20,16 @@ import type * as DialogPrimitive from '@radix-ui/react-dialog'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useRevalidator } from 'react-router'; -import { z } from 'zod'; +import type { z } from 'zod'; export type TeamEmailUpdateDialogProps = { teamEmail: TeamEmail; trigger?: React.ReactNode; } & Omit; -const ZUpdateTeamEmailFormSchema = z.object({ - name: z.string().trim().min(1, { message: 'Please enter a valid name.' }), -}); +const ZUpdateTeamEmailFormSchema = ZUpdateTeamEmailMutationSchema.pick({ + data: true, +}).shape.data; type TUpdateTeamEmailFormSchema = z.infer; @@ -44,6 +45,7 @@ export const TeamEmailUpdateDialog = ({ teamEmail, trigger, ...props }: TeamEmai defaultValues: { name: teamEmail.name, }, + mode: 'onSubmit', }); const { mutateAsync: updateTeamEmail } = trpc.team.email.update.useMutation(); diff --git a/apps/remix/app/components/forms/email-transport-form.tsx b/apps/remix/app/components/forms/email-transport-form.tsx index 2e20fdb59..c8af2f478 100644 --- a/apps/remix/app/components/forms/email-transport-form.tsx +++ b/apps/remix/app/components/forms/email-transport-form.tsx @@ -1,3 +1,4 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { Form, FormControl, @@ -15,8 +16,8 @@ import { useForm } from 'react-hook-form'; import { z } from 'zod'; const ZEmailTransportFormSchema = z.object({ - name: z.string().min(1), - fromName: z.string().min(1), + name: ZNameSchema, + fromName: ZNameSchema, fromAddress: z.string().email(), type: z.enum(['SMTP_AUTH', 'SMTP_API', 'RESEND', 'MAILCHANNELS']), host: z.string().optional(), diff --git a/apps/remix/app/components/general/claim-account.tsx b/apps/remix/app/components/general/claim-account.tsx index 110549bc7..4301391e6 100644 --- a/apps/remix/app/components/general/claim-account.tsx +++ b/apps/remix/app/components/general/claim-account.tsx @@ -1,6 +1,7 @@ import { authClient } from '@documenso/auth/client'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { AppError } from '@documenso/lib/errors/app-error'; +import { ZNameSchema } from '@documenso/lib/types/name'; import { env } from '@documenso/lib/utils/env'; import { zEmail } from '@documenso/lib/utils/zod'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; @@ -19,7 +20,6 @@ import { useRef } from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router'; import { z } from 'zod'; - import { SIGNUP_ERROR_MESSAGES } from '~/components/forms/signup'; export type ClaimAccountProps = { @@ -30,7 +30,7 @@ export type ClaimAccountProps = { export const ZClaimAccountFormSchema = z .object({ - name: z.string().trim().min(1, { message: msg`Please enter a valid name.`.id }), + name: ZNameSchema, email: zEmail().min(1), password: ZPasswordSchema, }) diff --git a/apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx b/apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx index 7feb0e191..180dc3e44 100644 --- a/apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx +++ b/apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx @@ -1,3 +1,4 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { trpc } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -29,7 +30,7 @@ export type SettingsSecurityPasskeyTableActionsProps = { }; const ZUpdatePasskeySchema = z.object({ - name: z.string(), + name: ZNameSchema, }); type TUpdatePasskeySchema = z.infer; diff --git a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx index 307cfe479..b3abcb81a 100644 --- a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx +++ b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx @@ -3,6 +3,7 @@ import { ORGANISATION_MEMBER_ROLE_HIERARCHY } from '@documenso/lib/constants/org import { EXTENDED_ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations'; import { TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams-translations'; import { AppError } from '@documenso/lib/errors/app-error'; +import { ZNameSchema } from '@documenso/lib/types/name'; import { trpc } from '@documenso/trpc/react'; import type { TFindOrganisationGroupsResponse } from '@documenso/trpc/server/organisation-router/find-organisation-groups.types'; import { Button } from '@documenso/ui/primitives/button'; @@ -28,7 +29,6 @@ import { useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { Link } from 'react-router'; import { z } from 'zod'; - import { OrganisationGroupDeleteDialog } from '~/components/dialogs/organisation-group-delete-dialog'; import { GenericErrorLayout } from '~/components/general/generic-error-layout'; import { @@ -36,7 +36,6 @@ import { OrganisationMembersMultiSelectCombobox, } from '~/components/general/organisation-members-multiselect-combobox'; import { SettingsHeader } from '~/components/general/settings-header'; - import type { Route } from './+types/o.$orgUrl.settings.groups.$id'; export default function OrganisationGroupSettingsPage({ params }: Route.ComponentProps) { @@ -113,7 +112,7 @@ export default function OrganisationGroupSettingsPage({ params }: Route.Componen } const ZUpdateOrganisationGroupFormSchema = z.object({ - name: z.string().min(1, msg`Name is required`.id), + name: ZNameSchema, organisationRole: z.nativeEnum(OrganisationMemberRole), memberIds: z.array(z.string()), }); diff --git a/packages/trpc/server/admin-router/create-subscription-claim.types.ts b/packages/trpc/server/admin-router/create-subscription-claim.types.ts index f1cfbad60..9cb1da986 100644 --- a/packages/trpc/server/admin-router/create-subscription-claim.types.ts +++ b/packages/trpc/server/admin-router/create-subscription-claim.types.ts @@ -1,8 +1,9 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { ZClaimFlagsSchema, ZRateLimitArraySchema } from '@documenso/lib/types/subscription'; import { z } from 'zod'; export const ZCreateSubscriptionClaimRequestSchema = z.object({ - name: z.string().min(1), + name: ZNameSchema, teamCount: z.number().int().min(0), memberCount: z.number().int().min(0), envelopeItemCount: z.number().int().min(1), diff --git a/packages/trpc/server/admin-router/email-transport/create-email-transport.types.ts b/packages/trpc/server/admin-router/email-transport/create-email-transport.types.ts index 26a47c12c..ecfa77bd4 100644 --- a/packages/trpc/server/admin-router/email-transport/create-email-transport.types.ts +++ b/packages/trpc/server/admin-router/email-transport/create-email-transport.types.ts @@ -1,9 +1,10 @@ import { ZEmailTransportConfigSchema } from '@documenso/lib/server-only/email/email-transport-config'; +import { ZNameSchema } from '@documenso/lib/types/name'; import { z } from 'zod'; export const ZCreateEmailTransportRequestSchema = z.object({ - name: z.string().min(1), - fromName: z.string().min(1), + name: ZNameSchema, + fromName: ZNameSchema, fromAddress: z.string().email(), config: ZEmailTransportConfigSchema, }); diff --git a/packages/trpc/server/admin-router/email-transport/update-email-transport.types.ts b/packages/trpc/server/admin-router/email-transport/update-email-transport.types.ts index d007c7e19..c8924c175 100644 --- a/packages/trpc/server/admin-router/email-transport/update-email-transport.types.ts +++ b/packages/trpc/server/admin-router/email-transport/update-email-transport.types.ts @@ -4,6 +4,7 @@ import { ZSmtpApiConfigSchema, ZSmtpAuthConfigSchema, } from '@documenso/lib/server-only/email/email-transport-config'; +import { ZNameSchema } from '@documenso/lib/types/name'; import { z } from 'zod'; // Reuses the canonical transport config schemas, but relaxes the secret field so @@ -21,8 +22,8 @@ const ZUpdateConfigSchema = z.discriminatedUnion('type', [ export const ZUpdateEmailTransportRequestSchema = z.object({ id: z.string(), data: z.object({ - name: z.string().min(1), - fromName: z.string().min(1), + name: ZNameSchema, + fromName: ZNameSchema, fromAddress: z.string().email(), config: ZUpdateConfigSchema, }), diff --git a/packages/trpc/server/admin-router/update-user.types.ts b/packages/trpc/server/admin-router/update-user.types.ts index 153b5a785..300db6fa7 100644 --- a/packages/trpc/server/admin-router/update-user.types.ts +++ b/packages/trpc/server/admin-router/update-user.types.ts @@ -1,10 +1,11 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { zEmail } from '@documenso/lib/utils/zod'; import { Role } from '@prisma/client'; import { z } from 'zod'; export const ZUpdateUserRequestSchema = z.object({ id: z.number().min(1), - name: z.string().nullish(), + name: ZNameSchema.nullish(), email: zEmail().optional(), roles: z.array(z.nativeEnum(Role)).optional(), }); diff --git a/packages/trpc/server/auth-router/create-passkey.types.ts b/packages/trpc/server/auth-router/create-passkey.types.ts index d5d2483a4..b6ff4a163 100644 --- a/packages/trpc/server/auth-router/create-passkey.types.ts +++ b/packages/trpc/server/auth-router/create-passkey.types.ts @@ -1,8 +1,9 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { ZRegistrationResponseJSONSchema } from '@documenso/lib/types/webauthn'; import { z } from 'zod'; export const ZCreatePasskeyRequestSchema = z.object({ - passkeyName: z.string().trim().min(1), + passkeyName: ZNameSchema, verificationResponse: ZRegistrationResponseJSONSchema, }); diff --git a/packages/trpc/server/auth-router/update-passkey.types.ts b/packages/trpc/server/auth-router/update-passkey.types.ts index e898234da..310389396 100644 --- a/packages/trpc/server/auth-router/update-passkey.types.ts +++ b/packages/trpc/server/auth-router/update-passkey.types.ts @@ -1,8 +1,9 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { z } from 'zod'; export const ZUpdatePasskeyRequestSchema = z.object({ passkeyId: z.string().trim().min(1), - name: z.string().trim().min(1), + name: ZNameSchema, }); export const ZUpdatePasskeyResponseSchema = z.void(); diff --git a/packages/trpc/server/enterprise-router/create-organisation-email.types.ts b/packages/trpc/server/enterprise-router/create-organisation-email.types.ts index 22ff2779c..0a3830c53 100644 --- a/packages/trpc/server/enterprise-router/create-organisation-email.types.ts +++ b/packages/trpc/server/enterprise-router/create-organisation-email.types.ts @@ -1,9 +1,10 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { zEmail } from '@documenso/lib/utils/zod'; import { z } from 'zod'; export const ZCreateOrganisationEmailRequestSchema = z.object({ emailDomainId: z.string(), - emailName: z.string().min(1).max(100), + emailName: ZNameSchema, email: zEmail().toLowerCase(), // This does not need to be validated to be part of the domain. diff --git a/packages/trpc/server/organisation-router/update-organisation-group.types.ts b/packages/trpc/server/organisation-router/update-organisation-group.types.ts index a32187edc..e6f4eb03f 100644 --- a/packages/trpc/server/organisation-router/update-organisation-group.types.ts +++ b/packages/trpc/server/organisation-router/update-organisation-group.types.ts @@ -1,3 +1,4 @@ +import { ZNameSchema } from '@documenso/lib/types/name'; import { OrganisationMemberRole } from '@prisma/client'; import { z } from 'zod'; @@ -14,7 +15,7 @@ import { z } from 'zod'; export const ZUpdateOrganisationGroupRequestSchema = z.object({ id: z.string(), - name: z.string().nullable().optional(), + name: ZNameSchema.nullable().optional(), organisationRole: z.nativeEnum(OrganisationMemberRole).optional(), memberIds: z.array(z.string()).optional(), });