From 59a514c23810106ab49c8e6353295781a0bc6b52 Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Thu, 29 Jan 2026 04:32:18 +0200 Subject: [PATCH] feat: allow non-team members as default recipients (#2404) --- ...efault-recipients-multiselect-combobox.tsx | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/apps/remix/app/components/general/default-recipients-multiselect-combobox.tsx b/apps/remix/app/components/general/default-recipients-multiselect-combobox.tsx index 3fbb9d424..b8fba1f7a 100644 --- a/apps/remix/app/components/general/default-recipients-multiselect-combobox.tsx +++ b/apps/remix/app/components/general/default-recipients-multiselect-combobox.tsx @@ -1,10 +1,13 @@ import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; +import { Trans, useLingui as useLinguiMacro } from '@lingui/react/macro'; import { RecipientRole } from '@prisma/client'; import type { TDefaultRecipient } from '@documenso/lib/types/default-recipients'; +import { isRecipientEmailValidForSending } from '@documenso/lib/utils/recipients'; import { trpc } from '@documenso/trpc/react'; import { MultiSelect, type Option } from '@documenso/ui/primitives/multiselect'; +import { useToast } from '@documenso/ui/primitives/use-toast'; type DefaultRecipientsMultiSelectComboboxProps = { listValues: TDefaultRecipient[]; @@ -20,6 +23,8 @@ export const DefaultRecipientsMultiSelectCombobox = ({ organisationId, }: DefaultRecipientsMultiSelectComboboxProps) => { const { _ } = useLingui(); + const { t } = useLinguiMacro(); + const { toast } = useToast(); const { data: organisationData, isLoading: isLoadingOrganisation } = trpc.organisation.member.find.useQuery( @@ -60,31 +65,56 @@ export const DefaultRecipientsMultiSelectCombobox = ({ })); const onSelectionChange = (selected: Option[]) => { - const updatedRecipients = selected.map((option) => { - const existingRecipient = listValues.find((r) => r.email === option.value); - const member = members?.find((m) => m.email === option.value); + const invalidEmails = selected.filter( + (option) => !isRecipientEmailValidForSending({ email: option.value }), + ); - return { - email: option.value, - name: member?.name || option.value, - role: existingRecipient?.role ?? RecipientRole.CC, - }; - }); + if (invalidEmails.length > 0) { + toast({ + title: t`Invalid email`, + description: t`"${invalidEmails[0].value}" is not a valid email address.`, + variant: 'destructive', + }); + } + + const updatedRecipients = selected + .filter((option) => !invalidEmails.includes(option)) + .map((option) => { + const existingRecipient = listValues.find((r) => r.email === option.value); + const member = members?.find((m) => m.email === option.value); + + return { + email: option.value, + name: member?.name || option.value, + role: existingRecipient?.role ?? RecipientRole.CC, + }; + }); onChange(updatedRecipients); }; return ( Loading...

: undefined} - emptyIndicator={

No members found

} + creatable + loadingIndicator={ + isLoading ? ( +

+ Loading... +

+ ) : undefined + } + emptyIndicator={ +

+ Type an email address to add a recipient +

+ } /> ); };