-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: template #52
feat: template #52
Conversation
Signed-off-by: harkiratsm <[email protected]>
Issue - The `Ctrl+K` hotkey in our application is conflicting with the browser's default search hotkey, leading to unintended browser actions. https://github.com/documenso/documenso/assets/71957674/180b6028-58f7-4cf8-841c-1e13c9d4d355
Adds the basically ability to create and use templates for repetitive document types
Someone is attempting to deploy a commit to a Personal Account owned by @SergeWilfried on Vercel. @SergeWilfried first needs to authorize it. |
WalkthroughWalkthroughThe updates span across the application to integrate template functionality, impacting client-side components and server-side logic. Changes include new React components for template management, modifications to Prisma schema for database migrations, and the addition of template-related functions and schemas in server-side utilities. The focus is on creating, duplicating, editing, and deleting templates, and associated changes to documents and recipients. Changes
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on X ? TipsChat with CodeRabbit Bot (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 18
Configuration used: CodeRabbit UI
Files selected for processing (52)
- apps/marketing/src/app/(marketing)/singleplayer/client.tsx (2 hunks)
- apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx (1 hunks)
- apps/marketing/src/components/constants.ts (1 hunks)
- apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx (1 hunks)
- apps/web/src/app/(dashboard)/documents/duplicate-document-dialog.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/[id]/page.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/data-table-action-dropdown.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/data-table-templates.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/data-table-title.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/delete-template-dialog.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/duplicate-template-dialog.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/empty-state.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/new-template-dialog.tsx (1 hunks)
- apps/web/src/app/(dashboard)/templates/page.tsx (1 hunks)
- apps/web/src/components/(dashboard)/common/command-menu.tsx (1 hunks)
- apps/web/src/components/(dashboard)/layout/desktop-nav.tsx (3 hunks)
- apps/web/src/components/(dashboard)/layout/header.tsx (1 hunks)
- apps/web/src/components/(dashboard)/layout/profile-dropdown.tsx (2 hunks)
- apps/web/src/components/formatter/template-type.tsx (1 hunks)
- packages/lib/server-only/admin/get-recipients-stats.ts (1 hunks)
- packages/lib/server-only/document/find-documents.ts (2 hunks)
- packages/lib/server-only/field/get-fields-for-template.ts (1 hunks)
- packages/lib/server-only/field/remove-signed-field-with-token.ts (1 hunks)
- packages/lib/server-only/field/set-fields-for-document.ts (1 hunks)
- packages/lib/server-only/field/set-fields-for-template.ts (1 hunks)
- packages/lib/server-only/field/sign-field-with-token.ts (1 hunks)
- packages/lib/server-only/recipient/get-recipients-for-template.ts (1 hunks)
- packages/lib/server-only/recipient/set-recipients-for-template.ts (1 hunks)
- packages/lib/server-only/template/create-document-from-template.ts (1 hunks)
- packages/lib/server-only/template/create-template.ts (1 hunks)
- packages/lib/server-only/template/delete-template.ts (1 hunks)
- packages/lib/server-only/template/duplicate-template.ts (1 hunks)
- packages/lib/server-only/template/get-template-by-id.ts (1 hunks)
- packages/lib/server-only/template/get-templates.ts (1 hunks)
- packages/prisma/migrations/20231221101005_add_templates/migration.sql (1 hunks)
- packages/prisma/schema.prisma (6 hunks)
- packages/trpc/server/field-router/router.ts (2 hunks)
- packages/trpc/server/field-router/schema.ts (1 hunks)
- packages/trpc/server/recipient-router/router.ts (2 hunks)
- packages/trpc/server/recipient-router/schema.ts (1 hunks)
- packages/trpc/server/router.ts (2 hunks)
- packages/trpc/server/singleplayer-router/router.ts (1 hunks)
- packages/trpc/server/template-router/router.ts (1 hunks)
- packages/trpc/server/template-router/schema.ts (1 hunks)
- packages/ui/primitives/dialog.tsx (1 hunks)
- packages/ui/primitives/document-dropzone.tsx (2 hunks)
- packages/ui/primitives/document-flow/types.ts (1 hunks)
- packages/ui/primitives/template-flow/add-template-fields.tsx (1 hunks)
- packages/ui/primitives/template-flow/add-template-fields.types.ts (1 hunks)
- packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx (1 hunks)
- packages/ui/primitives/template-flow/add-template-placeholder-recipients.types.ts (1 hunks)
Files skipped from review due to trivial changes (4)
- apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx
- apps/marketing/src/components/constants.ts
- packages/lib/server-only/document/find-documents.ts
- packages/trpc/server/recipient-router/schema.ts
Additional comments: 61
apps/web/src/app/(dashboard)/templates/empty-state.tsx (1)
- 3-16: The component looks good and follows best practices for a presentational component. No action needed.
packages/ui/primitives/template-flow/add-template-fields.types.ts (1)
- 5-23: The schema looks comprehensive and includes validation for the fields. Ensure that all the fields are required for the operation and that the validation rules are in line with the application's requirements.
packages/lib/server-only/admin/get-recipients-stats.ts (1)
- 20-27: The accumulation logic for recipient statistics seems correct. However, ensure that the statistics are not exposing any sensitive information and are properly authorized.
packages/ui/primitives/template-flow/add-template-placeholder-recipients.types.ts (1)
- 3-26: The schema includes a refinement to ensure unique emails, which is a good practice. Confirm that the error message and path are correctly implemented and user-friendly.
packages/trpc/server/template-router/schema.ts (1)
- 3-26: The schemas appear to be well-defined with appropriate validation rules. Ensure that they match the expected input for the mutations they are used with.
packages/trpc/server/router.ts (1)
- 6-12: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [9-23]
The addition of the
templateRouter
to theappRouter
object is correct. Ensure that thetemplateRouter
is properly configured and that all routes are secured and validated.apps/web/src/components/formatter/template-type.tsx (1)
- 35-49: The component looks good and follows best practices for a presentational component. No action needed.
packages/ui/primitives/document-flow/types.ts (1)
- 24-30: The change to make the
signerEmail
field optional in theZDocumentFlowFormSchema
is correct. Ensure that this change is reflected in the UI and that the application logic accounts for the possibility of an absentsignerEmail
.packages/trpc/server/field-router/schema.ts (1)
- 24-41: The addition of the
ZAddTemplateFieldsMutationSchema
is correct. Ensure that the schema is used in the appropriate mutation and that the validation rules are consistent with the application's requirements.apps/web/src/components/(dashboard)/layout/header.tsx (1)
- 52-55: The change to add a margin-left style for medium-sized screens is correct. Ensure that this change is consistent with the design system and responsive layout of the application.
packages/trpc/server/recipient-router/router.ts (1)
- 40-63: The addition of the
addTemplateSigners
procedure is correct. Ensure that the procedure is properly secured and that the input is validated before processing.apps/web/src/components/(dashboard)/layout/desktop-nav.tsx (1)
- 41-69: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [29-87]
The component should ensure that the navigation links are accessible and that the active link is correctly highlighted based on the current route.
packages/lib/server-only/field/set-fields-for-document.ts (1)
- 5-6: The import statement for
FieldType
has been changed to a type import, which is correct ifFieldType
is only used for type annotations and not as a value.apps/web/src/app/(dashboard)/documents/duplicate-document-dialog.tsx (1)
- 41-43: The addition of
router.push
in theonSuccess
callback is correct. Ensure that the user is redirected to the correct route after the document is duplicated.packages/prisma/migrations/20231221101005_add_templates/migration.sql (16)
8-8: Creation of the
TemplateType
enum is correct and follows SQL standards.11-11: Dropping the foreign key constraint on the
Field
table is a significant change. Ensure that this is intentional and that the logic relying on this constraint is updated accordingly.14-15: Adding a
templateId
column and makingdocumentId
nullable in theField
table is a structural change that aligns with the introduction of templates. Ensure that the application logic is updated to handle these nullable fields.18-22: The CHECK constraint added to the
Field
table ensures that eithertemplateId
ordocumentId
is set, but not both. This is a good practice to maintain data integrity.25-26: Similar changes to the
Recipient
table as theField
table are noted here, with the addition oftemplateId
and makingdocumentId
nullable.29-33: The CHECK constraint for the
Recipient
table is consistent with theField
table and maintains data integrity.36-45: The
Template
table is created with appropriate fields and constraints. The use ofSERIAL
for theid
and setting defaults for timestamps are standard practices.49-49: Creating a unique index on
templateDocumentDataId
in theTemplate
table ensures that each template has a unique document data identifier.52-52: Creating an index on
templateId
in theField
table will improve query performance for operations involving templates.55-55: Similarly, creating an index on
templateId
in theRecipient
table will enhance performance for recipient-related queries.58-58: The unique index on
["templateId", "email"]
in theRecipient
table enforces the uniqueness of recipients per template, which is crucial for data integrity.61-61: Adding a foreign key constraint from
Recipient
toTemplate
with cascade on delete and update is a good practice to maintain referential integrity.64-64: Adding a foreign key constraint from
Field
toTemplate
with cascade on delete and update is consistent with the previous constraint and maintains referential integrity.67-67: Re-adding the foreign key constraint from
Field
toRecipient
with set null on delete is important for maintaining relationships between fields and recipients.70-70: Adding a foreign key constraint from
Template
toDocumentData
with cascade on delete and update ensures that templates are always associated with valid document data.73-73: Adding a foreign key constraint from
Template
toUser
with cascade on delete and update ensures that templates are always associated with a valid user.packages/trpc/server/template-router/router.ts (5)
1-1: Importing necessary modules and functions from external libraries and internal paths is correct.
16-36: The
createTemplate
procedure is correctly set up with authentication, input validation, and error handling. It uses a try-catch block to catch and handle errors gracefully.38-54: The
createDocumentFromTemplate
procedure follows the same pattern ascreateTemplate
and is correctly implemented with appropriate error handling.56-74: The
duplicateTemplate
procedure is implemented consistently with the other procedures and includes proper error handling.76-93: The
deleteTemplate
procedure is correctly implemented with authentication and error handling. It also uses a try-catch block to handle exceptions.packages/lib/server-only/field/set-fields-for-template.ts (4)
1-1: Importing the Prisma client and type definitions is correct.
4-14: Defining the
Field
type with optional and required properties is correct and follows TypeScript best practices.16-20: Defining the
SetFieldsForTemplateOptions
type with required properties is correct.22-117: The
setFieldsForTemplate
function is well-structured with clear error handling, transactional operations, and proper use of Prisma client methods. Ensure that the logic for handling existing fields and removed fields is thoroughly tested.packages/trpc/server/field-router/router.ts (2)
2-14: The imports are correct, and the addition of
setFieldsForTemplate
andZAddTemplateFieldsMutationSchema
is noted.47-66: The
addTemplateFields
procedure is correctly set up with authentication and input validation. It uses a try-catch block for error handling, which is good practice.packages/ui/primitives/dialog.tsx (1)
- 23-23: The change in the
className
property fromz-50
toz-[9999]
is a significant increase in the z-index value. Ensure that this change does not cause unintended stacking context issues with other components.apps/web/src/app/(dashboard)/templates/data-table-templates.tsx (1)
- 1-138: The
TemplatesDataTable
component is well-structured with state management, event handling, and conditional rendering. Ensure that the logic for handling loading states and routing is thoroughly tested.apps/web/src/components/(dashboard)/layout/profile-dropdown.tsx (2)
4-7: The addition of the
FileSpreadsheet
import is correct and follows TypeScript import syntax.110-116: The new
DropdownMenuItem
for "Templates" is correctly added with a link and an icon. Ensure that the link correctly routes to the templates page.packages/ui/primitives/document-dropzone.tsx (2)
78-85: The addition of the
DocumentDescription
object withdocument
andtemplate
properties is correct and follows TypeScript best practices for defining object structures.171-171: The dynamic selection of the headline based on the
type
property is a good use of conditional rendering.packages/trpc/server/singleplayer-router/router.ts (1)
- 66-66: The addition of the
templateId
property with a value ofnull
to the object within thesignPdf
function is a structural change. Ensure that the application logic correctly handles this new nullable property.apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx (1)
- 101-101: The addition of the line that filters non-signed recipients from a row is correct. Ensure that this logic is consistent with the application's requirements for displaying actions based on recipient status.
apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx (1)
- 1-156: The
EditTemplateForm
component is well-structured with state management, form handling, and conditional rendering. Ensure that the logic for handling form submissions and routing is thoroughly tested.apps/web/src/components/(dashboard)/common/command-menu.tsx (1)
- 88-91: The modification to the
toggleOpen
function to accept aKeyboardEvent
parameter and calle.preventDefault()
is correct. This change prevents the default behavior of the event, which is a common practice in event handling.packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx (1)
- 1-193: The
AddTemplatePlaceholderRecipientsFormPartial
component is correctly implemented with form handling, dynamic form fields, and animations. Ensure that the form validation and submission logic is thoroughly tested.packages/prisma/schema.prisma (5)
44-44: The addition of the
Template
relation to theUser
model is correct and follows Prisma schema conventions.157-157: The addition of the
Template
relation to theDocumentData
model is correct.184-203: The changes to the
Recipient
model, including the newtemplateId
field andTemplate
relation, are correct. Ensure that the unique and index constraints are appropriate for the application's requirements.226-238: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [218-235]
The changes to the
Field
model, including the newtemplateId
field andTemplate
relation, are correct. The index ontemplateId
will improve query performance.
- 272-286: The new
Template
model is correctly defined with fields, relations, and a unique constraint ontemplateDocumentDataId
.apps/marketing/src/app/(marketing)/singleplayer/client.tsx (2)
86-92: Adding
templateId: null
to the mapped fields ensures that the new template functionality is considered when fields are created. This change seems consistent with the PR's objective to integrate template management.149-155: The addition of
templateId: null
to theplaceholderRecipient
object aligns with the new template features. This ensures that the recipient structure is consistent with the expected data model changes.apps/web/src/app/(dashboard)/templates/new-template-dialog.tsx (1)
- 46-115: The
NewTemplateDialog
component provides a form for creating new templates, including file upload and automatic form value setting based on the uploaded file's name. Ensure that the file upload and form submission processes are thoroughly tested, especially error handling during the file upload and template creation steps.Verification successful
The search results indicate that
createTemplate
andputFile
are used in multiple places within the codebase. The snippets fromtemplate-router/router.ts
andcreate-template.ts
suggest that there is a try-catch block in the router that likely handles errors when callingcreateTemplate
. Similarly, theputFile
function input-file.ts
is used in various contexts, and the presence of try-catch blocks in the calling code (e.g.,new-template-dialog.tsx
) suggests that errors during file upload are also handled.Given the evidence of error handling in the provided contexts, it seems that the file upload and template creation processes are designed to handle errors gracefully.
Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that the file upload and template creation processes handle errors gracefully. # Expecting no unhandled exceptions or user-facing technical error messages. rg --type typescript --type javascript -C 3 "createTemplate" rg --type typescript --type javascript -C 3 "putFile"Length of output: 184
Script:
#!/bin/bash # Search for occurrences of "createTemplate" and "putFile" to verify error handling. rg -C 3 "createTemplate" rg -C 3 "putFile"Length of output: 15035
packages/ui/primitives/template-flow/add-template-fields.tsx (1)
- 62-538: The
AddTemplateFieldsFormPartial
component handles the addition, movement, and resizing of fields within a template. It's important to ensure that the field manipulation logic works correctly within the bounds of the document and that the state updates are handled efficiently without unnecessary re-renders.Verification successful
The verification process has confirmed the use of
useDocumentElement
anduseFieldArray
hooks within theadd-template-fields.tsx
file, which are integral to the field manipulation logic. There is no direct evidence from the search results to suggest issues with field placement outside the document bounds or incorrect state updates.Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that the field manipulation logic correctly restricts actions within the document bounds. # Expecting no issues with field placement outside the document or incorrect state updates. rg --type typescript --type javascript -C 3 "useDocumentElement" rg --type typescript --type javascript -C 3 "useFieldArray"Length of output: 194
Script:
#!/bin/bash # Verify that the field manipulation logic correctly restricts actions within the document bounds. # Expecting no issues with field placement outside the document or incorrect state updates. rg -C 3 "useDocumentElement" rg -C 3 "useFieldArray"Length of output: 8852
/* | ||
Warnings: | ||
|
||
- A unique constraint covering the columns `[templateId,email]` on the table `Recipient` will be added. If there are existing duplicate values, this will fail. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning about the unique constraint on [templateId,email]
is important. Ensure that any existing duplicates are resolved before applying this migration to avoid failure.
export const deleteTemplate = async ({ id, userId }: DeleteTemplateOptions) => { | ||
return await prisma.template.delete({ where: { id, userId } }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure that the deletion of a template is properly authorized and that only the owner or an authorized user can delete the template.
export const getTemplateById = async ({ id, userId }: GetTemplateByIdOptions) => { | ||
return await prisma.template.findFirstOrThrow({ | ||
where: { | ||
id, | ||
userId, | ||
}, | ||
include: { | ||
templateDocumentData: true, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding error handling for the case where the template is not found or the user is not authorized to access it.
export const getFieldsForTemplate = async ({ templateId, userId }: GetFieldsForTemplateOptions) => { | ||
const fields = await prisma.field.findMany({ | ||
where: { | ||
templateId, | ||
Template: { | ||
userId, | ||
}, | ||
}, | ||
orderBy: { | ||
id: 'asc', | ||
}, | ||
}); | ||
|
||
return fields; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check if there is a need to handle scenarios where no fields are found for the given template ID, or if the user is not authorized to view them.
export const createTemplate = async ({ | ||
title, | ||
userId, | ||
templateDocumentDataId, | ||
}: CreateTemplateOptions) => { | ||
return await prisma.template.create({ | ||
data: { | ||
title, | ||
userId, | ||
templateDocumentDataId, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure that the creation of a template includes all necessary validation and error handling, especially considering the direct interaction with the database.
export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) => { | ||
const { data: session } = useSession(); | ||
|
||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false); | ||
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false); | ||
|
||
if (!session) { | ||
return null; | ||
} | ||
|
||
const isOwner = row.userId === session.user.id; | ||
|
||
return ( | ||
<DropdownMenu> | ||
<DropdownMenuTrigger> | ||
<MoreHorizontal className="text-muted-foreground h-5 w-5" /> | ||
</DropdownMenuTrigger> | ||
|
||
<DropdownMenuContent className="w-52" align="start" forceMount> | ||
<DropdownMenuLabel>Action</DropdownMenuLabel> | ||
|
||
<DropdownMenuItem disabled={!isOwner} asChild> | ||
<Link href={`/templates/${row.id}`}> | ||
<Edit className="mr-2 h-4 w-4" /> | ||
Edit | ||
</Link> | ||
</DropdownMenuItem> | ||
|
||
{/* <DropdownMenuItem disabled={!isOwner} onClick={async () => onDuplicateButtonClick(row.id)}> */} | ||
<DropdownMenuItem disabled={!isOwner} onClick={() => setDuplicateDialogOpen(true)}> | ||
<Copy className="mr-2 h-4 w-4" /> | ||
Duplicate | ||
</DropdownMenuItem> | ||
|
||
<DropdownMenuItem disabled={!isOwner} onClick={() => setDeleteDialogOpen(true)}> | ||
<Trash2 className="mr-2 h-4 w-4" /> | ||
Delete | ||
</DropdownMenuItem> | ||
</DropdownMenuContent> | ||
|
||
<DuplicateTemplateDialog | ||
id={row.id} | ||
open={isDuplicateDialogOpen} | ||
onOpenChange={setDuplicateDialogOpen} | ||
/> | ||
|
||
<DeleteTemplateDialog | ||
id={row.id} | ||
open={isDeleteDialogOpen} | ||
onOpenChange={setDeleteDialogOpen} | ||
/> | ||
</DropdownMenu> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The component should handle the disabled state more gracefully, possibly by providing tooltips or other indicators as to why an action is disabled.
export default async function TemplatePage({ params }: TemplatePageProps) { | ||
const { id } = params; | ||
|
||
const templateId = Number(id); | ||
|
||
if (!templateId || Number.isNaN(templateId)) { | ||
redirect('/documents'); | ||
} | ||
|
||
const { user } = await getRequiredServerComponentSession(); | ||
|
||
const template = await getTemplateById({ | ||
id: templateId, | ||
userId: user.id, | ||
}).catch(() => null); | ||
|
||
if (!template || !template.templateDocumentData) { | ||
redirect('/documents'); | ||
} | ||
|
||
const { templateDocumentData } = template; | ||
|
||
const [templateRecipients, templateFields] = await Promise.all([ | ||
getRecipientsForTemplate({ | ||
templateId, | ||
userId: user.id, | ||
}), | ||
getFieldsForTemplate({ | ||
templateId, | ||
userId: user.id, | ||
}), | ||
]); | ||
|
||
return ( | ||
<div className="mx-auto max-w-screen-xl px-4 md:px-8"> | ||
<Link href="/templates" className="flex items-center text-[#7AC455] hover:opacity-80"> | ||
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> | ||
Templates | ||
</Link> | ||
|
||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}> | ||
{template.title} | ||
</h1> | ||
|
||
<div className="mt-2.5 flex items-center gap-x-6"> | ||
<TemplateType inheritColor type={template.type} className="text-muted-foreground" /> | ||
</div> | ||
|
||
<EditTemplateForm | ||
className="mt-8" | ||
template={template} | ||
user={user} | ||
recipients={templateRecipients} | ||
fields={templateFields} | ||
documentData={templateDocumentData} | ||
/> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The server-side logic should include error handling for scenarios where the template is not found, the user session is not found, or the user is not authorized to view the template.
export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateDialogProps) => { | ||
const router = useRouter(); | ||
|
||
const { toast } = useToast(); | ||
|
||
const { mutateAsync: deleteTemplate, isLoading } = trpcReact.template.deleteTemplate.useMutation({ | ||
onSuccess: () => { | ||
router.refresh(); | ||
|
||
toast({ | ||
title: 'Template deleted', | ||
description: 'Your template has been successfully deleted.', | ||
duration: 5000, | ||
}); | ||
|
||
onOpenChange(false); | ||
}, | ||
}); | ||
|
||
const onDeleteTemplate = async () => { | ||
try { | ||
await deleteTemplate({ id }); | ||
} catch { | ||
toast({ | ||
title: 'Something went wrong', | ||
description: 'This template could not be deleted at this time. Please try again.', | ||
variant: 'destructive', | ||
duration: 7500, | ||
}); | ||
} | ||
}; | ||
|
||
return ( | ||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}> | ||
<DialogContent> | ||
<DialogHeader> | ||
<DialogTitle>Do you want to delete this template?</DialogTitle> | ||
|
||
<DialogDescription> | ||
Please note that this action is irreversible. Once confirmed, your template will be | ||
permanently deleted. | ||
</DialogDescription> | ||
</DialogHeader> | ||
|
||
<DialogFooter> | ||
<div className="flex w-full flex-1 flex-nowrap gap-4"> | ||
<Button | ||
type="button" | ||
variant="secondary" | ||
onClick={() => onOpenChange(false)} | ||
className="flex-1" | ||
> | ||
Cancel | ||
</Button> | ||
|
||
<Button type="button" loading={isLoading} onClick={onDeleteTemplate} className="flex-1"> | ||
Delete | ||
</Button> | ||
</div> | ||
</DialogFooter> | ||
</DialogContent> | ||
</Dialog> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The component should handle errors more gracefully, possibly by providing more detailed error messages or guidance on what the user should do next.
export const setRecipientsForTemplate = async ({ | ||
userId, | ||
templateId, | ||
recipients, | ||
}: SetRecipientsForTemplateOptions) => { | ||
const template = await prisma.template.findFirst({ | ||
where: { | ||
id: templateId, | ||
userId, | ||
}, | ||
}); | ||
|
||
if (!template) { | ||
throw new Error('Template not found'); | ||
} | ||
|
||
const normalizedRecipients = recipients.map((recipient) => ({ | ||
...recipient, | ||
email: recipient.email.toLowerCase(), | ||
})); | ||
|
||
const existingRecipients = await prisma.recipient.findMany({ | ||
where: { | ||
templateId, | ||
}, | ||
}); | ||
|
||
const removedRecipients = existingRecipients.filter( | ||
(existingRecipient) => | ||
!normalizedRecipients.find( | ||
(recipient) => | ||
recipient.id === existingRecipient.id || recipient.email === existingRecipient.email, | ||
), | ||
); | ||
|
||
const linkedRecipients = normalizedRecipients.map((recipient) => { | ||
const existing = existingRecipients.find( | ||
(existingRecipient) => | ||
existingRecipient.id === recipient.id || existingRecipient.email === recipient.email, | ||
); | ||
|
||
return { | ||
...recipient, | ||
_persisted: existing, | ||
}; | ||
}); | ||
|
||
const persistedRecipients = await prisma.$transaction( | ||
// Disabling as wrapping promises here causes type issues | ||
// eslint-disable-next-line @typescript-eslint/promise-function-async | ||
linkedRecipients.map((recipient) => | ||
prisma.recipient.upsert({ | ||
where: { | ||
id: recipient._persisted?.id ?? -1, | ||
templateId, | ||
}, | ||
update: { | ||
name: recipient.name, | ||
email: recipient.email, | ||
templateId, | ||
}, | ||
create: { | ||
name: recipient.name, | ||
email: recipient.email, | ||
token: nanoid(), | ||
templateId, | ||
}, | ||
}), | ||
), | ||
); | ||
|
||
if (removedRecipients.length > 0) { | ||
await prisma.recipient.deleteMany({ | ||
where: { | ||
id: { | ||
in: removedRecipients.map((recipient) => recipient.id), | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
return persistedRecipients; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function should include comprehensive error handling for scenarios such as template not found, unauthorized access, and database operation failures.
const { Document: document, Recipient: recipient } = field; | ||
|
||
if (!document) { | ||
throw new Error(`Document not found for field ${field.id}`); | ||
} | ||
|
||
if (document.status === DocumentStatus.COMPLETED) { | ||
throw new Error(`Document ${document.id} has already been completed`); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function should include comprehensive error handling for scenarios such as document not found, document completed, document deleted, recipient already signed, and field already inserted.
Summary by CodeRabbit
New Features
Enhancements
templateId
property for better integration with new features.Bug Fixes
Documentation
SIGN
property.Refactor
FindDocumentsOptions
from an interface to a type for better type handling.Style
Tests
Chores
Revert