diff --git a/packages/lib/server-only/recipient/set-recipients-for-template.ts b/packages/lib/server-only/recipient/set-recipients-for-template.ts index 7c96bcf44..5315711a5 100644 --- a/packages/lib/server-only/recipient/set-recipients-for-template.ts +++ b/packages/lib/server-only/recipient/set-recipients-for-template.ts @@ -1,4 +1,5 @@ import { prisma } from '@documenso/prisma'; +import type { RecipientRole } from '@documenso/prisma/client'; import { nanoid } from '../../universal/id'; @@ -9,6 +10,7 @@ export type SetRecipientsForTemplateOptions = { id?: number; email: string; name: string; + role: RecipientRole; }[]; }; @@ -84,11 +86,13 @@ export const setRecipientsForTemplate = async ({ update: { name: recipient.name, email: recipient.email, + role: recipient.role, templateId, }, create: { name: recipient.name, email: recipient.email, + role: recipient.role, token: nanoid(), templateId, }, diff --git a/packages/lib/server-only/template/create-document-from-template.ts b/packages/lib/server-only/template/create-document-from-template.ts index c520d4ce1..fc4e161e4 100644 --- a/packages/lib/server-only/template/create-document-from-template.ts +++ b/packages/lib/server-only/template/create-document-from-template.ts @@ -57,6 +57,7 @@ export const createDocumentFromTemplate = async ({ create: template.Recipient.map((recipient) => ({ email: recipient.email, name: recipient.name, + role: recipient.role, token: nanoid(), })), }, diff --git a/packages/trpc/server/recipient-router/router.ts b/packages/trpc/server/recipient-router/router.ts index c36b09ec9..d28edb6c3 100644 --- a/packages/trpc/server/recipient-router/router.ts +++ b/packages/trpc/server/recipient-router/router.ts @@ -53,6 +53,7 @@ export const recipientRouter = router({ id: signer.nativeId, email: signer.email, name: signer.name, + role: signer.role, })), }); } catch (err) { diff --git a/packages/trpc/server/recipient-router/schema.ts b/packages/trpc/server/recipient-router/schema.ts index a6b4e0d11..edcd34ed6 100644 --- a/packages/trpc/server/recipient-router/schema.ts +++ b/packages/trpc/server/recipient-router/schema.ts @@ -34,6 +34,7 @@ export const ZAddTemplateSignersMutationSchema = z nativeId: z.number().optional(), email: z.string().email().min(1), name: z.string(), + role: z.nativeEnum(RecipientRole), }), ), }) diff --git a/packages/ui/primitives/document-flow/add-signers.tsx b/packages/ui/primitives/document-flow/add-signers.tsx index b1341c6ca..b13e220f3 100644 --- a/packages/ui/primitives/document-flow/add-signers.tsx +++ b/packages/ui/primitives/document-flow/add-signers.tsx @@ -4,7 +4,7 @@ import React, { useId } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { AnimatePresence, motion } from 'framer-motion'; -import { BadgeCheck, Copy, Eye, PencilLine, Plus, Trash } from 'lucide-react'; +import { Plus, Trash } from 'lucide-react'; import { Controller, useFieldArray, useForm } from 'react-hook-form'; import { useLimits } from '@documenso/ee/server-only/limits/provider/client'; @@ -17,6 +17,7 @@ import { Button } from '../button'; import { FormErrorMessage } from '../form/form-error-message'; import { Input } from '../input'; import { Label } from '../label'; +import { ROLE_ICONS } from '../recipient-role-icons'; import { Select, SelectContent, SelectItem, SelectTrigger } from '../select'; import { useStep } from '../stepper'; import { useToast } from '../use-toast'; @@ -32,13 +33,6 @@ import { import { ShowFieldItem } from './show-field-item'; import type { DocumentFlowStep } from './types'; -const ROLE_ICONS: Record = { - SIGNER: , - APPROVER: , - CC: , - VIEWER: , -}; - export type AddSignersFormProps = { documentFlow: DocumentFlowStep; recipients: Recipient[]; diff --git a/packages/ui/primitives/recipient-role-icons.tsx b/packages/ui/primitives/recipient-role-icons.tsx new file mode 100644 index 000000000..5bc4f34b9 --- /dev/null +++ b/packages/ui/primitives/recipient-role-icons.tsx @@ -0,0 +1,10 @@ +import { BadgeCheck, Copy, Eye, PencilLine } from 'lucide-react'; + +import type { RecipientRole } from '.prisma/client'; + +export const ROLE_ICONS: Record = { + SIGNER: , + APPROVER: , + CC: , + VIEWER: , +}; diff --git a/packages/ui/primitives/template-flow/add-template-fields.tsx b/packages/ui/primitives/template-flow/add-template-fields.tsx index bb9c304d9..602bd749b 100644 --- a/packages/ui/primitives/template-flow/add-template-fields.tsx +++ b/packages/ui/primitives/template-flow/add-template-fields.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Caveat } from 'next/font/google'; @@ -10,9 +10,10 @@ import { useFieldArray, useForm } from 'react-hook-form'; import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect'; import { useDocumentElement } from '@documenso/lib/client-only/hooks/use-document-element'; import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; +import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { nanoid } from '@documenso/lib/universal/id'; import type { Field, Recipient } from '@documenso/prisma/client'; -import { FieldType } from '@documenso/prisma/client'; +import { FieldType, RecipientRole } from '@documenso/prisma/client'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Card, CardContent } from '@documenso/ui/primitives/card'; @@ -291,6 +292,28 @@ export const AddTemplateFieldsFormPartial = ({ setSelectedSigner(recipients[0]); }, [recipients]); + const recipientsByRole = useMemo(() => { + const recipientsByRole: Record = { + CC: [], + VIEWER: [], + SIGNER: [], + APPROVER: [], + }; + + recipients.forEach((recipient) => { + recipientsByRole[recipient.role].push(recipient); + }); + + return recipientsByRole; + }, [recipients]); + + const recipientsByRoleToDisplay = useMemo(() => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return (Object.entries(recipientsByRole) as [RecipientRole, Recipient[]][]).filter( + ([role]) => role !== RecipientRole.CC && role !== RecipientRole.VIEWER, + ); + }, [recipientsByRole]); + return ( <> @@ -363,55 +386,49 @@ export const AddTemplateFieldsFormPartial = ({ - - {recipients.map((recipient, index) => ( - { - setSelectedSigner(recipient); - setShowRecipientsSelector(false); - }} - > - {/* {recipient.sendStatus !== SendStatus.SENT ? ( - - ) : ( - - - - - - This document has already been sent to this recipient. You can no - longer edit this recipient. - - - )} */} + {recipientsByRoleToDisplay.map(([role, recipients], roleIndex) => ( + +
+ {`${RECIPIENT_ROLES_DESCRIPTION[role].roleName}s`} +
- {recipient.name && ( + {recipients.length === 0 && ( +
+ No recipients with this role +
+ )} + + {recipients.map((recipient) => ( + { + setSelectedSigner(recipient); + setShowRecipientsSelector(false); + }} + > - {recipient.name} ({recipient.email}) - - )} + {recipient.name && ( + + {recipient.name} ({recipient.email}) + + )} - {!recipient.name && ( - - {recipient.email} + {!recipient.name && ( + {recipient.email} + )} - )} - - ))} -
+
+ ))} +
+ ))} diff --git a/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx b/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx index ebe48b562..87ec48ad1 100644 --- a/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx +++ b/packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx @@ -5,10 +5,10 @@ import React, { useId, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { AnimatePresence, motion } from 'framer-motion'; import { Plus, Trash } from 'lucide-react'; -import { useFieldArray, useForm } from 'react-hook-form'; +import { Controller, useFieldArray, useForm } from 'react-hook-form'; import { nanoid } from '@documenso/lib/universal/id'; -import type { Field, Recipient } from '@documenso/prisma/client'; +import { type Field, type Recipient, RecipientRole } from '@documenso/prisma/client'; import { Button } from '@documenso/ui/primitives/button'; import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message'; import { Input } from '@documenso/ui/primitives/input'; @@ -21,6 +21,8 @@ import { DocumentFlowFormContainerStep, } from '../document-flow/document-flow-root'; import type { DocumentFlowStep } from '../document-flow/types'; +import { ROLE_ICONS } from '../recipient-role-icons'; +import { Select, SelectContent, SelectItem, SelectTrigger } from '../select'; import { useStep } from '../stepper'; import type { TAddTemplatePlacholderRecipientsFormSchema } from './add-template-placeholder-recipients.types'; import { ZAddTemplatePlacholderRecipientsFormSchema } from './add-template-placeholder-recipients.types'; @@ -59,12 +61,14 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({ formId: String(recipient.id), name: recipient.name, email: recipient.email, + role: recipient.role, })) : [ { formId: initialId, name: `Recipient 1`, email: `recipient.1@documenso.com`, + role: RecipientRole.SIGNER, }, ], }, @@ -86,6 +90,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({ formId: nanoid(12), name: `Recipient ${placeholderRecipientCount}`, email: `recipient.${placeholderRecipientCount}@documenso.com`, + role: RecipientRole.SIGNER, }); setPlaceholderRecipientCount((count) => count + 1); @@ -95,12 +100,6 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({ removeSigner(index); }; - const onKeyDown = (event: React.KeyboardEvent) => { - if (event.key === 'Enter' && event.target instanceof HTMLInputElement) { - onAddPlaceholderRecipient(); - } - }; - return ( <> @@ -113,10 +112,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({ className="flex flex-wrap items-end gap-x-4" >
- +
+
+ ( + + )} + /> +
+