mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 17:21:41 +10:00
feat: template recipients on blur
This commit is contained in:
@ -261,6 +261,7 @@ export const EditTemplateForm = ({
|
|||||||
onSubmit={onAddTemplatePlaceholderFormSubmit}
|
onSubmit={onAddTemplatePlaceholderFormSubmit}
|
||||||
isEnterprise={isEnterprise}
|
isEnterprise={isEnterprise}
|
||||||
isDocumentPdfLoaded={isDocumentPdfLoaded}
|
isDocumentPdfLoaded={isDocumentPdfLoaded}
|
||||||
|
template={template}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AddTemplateFieldsFormPartial
|
<AddTemplateFieldsFormPartial
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
ZAddTemplateSignersMutationSchema,
|
ZAddTemplateSignersMutationSchema,
|
||||||
ZCompleteDocumentWithTokenMutationSchema,
|
ZCompleteDocumentWithTokenMutationSchema,
|
||||||
ZRemoveSignerMutationSchema,
|
ZRemoveSignerMutationSchema,
|
||||||
|
ZRemoveTemplateSignerMutationSchema,
|
||||||
} from './schema';
|
} from './schema';
|
||||||
|
|
||||||
export const recipientRouter = router({
|
export const recipientRouter = router({
|
||||||
@ -72,6 +73,12 @@ export const recipientRouter = router({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
removeTemplateSigner: authenticatedProcedure
|
||||||
|
.input(ZRemoveTemplateSignerMutationSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement
|
||||||
|
}),
|
||||||
|
|
||||||
removeSigner: authenticatedProcedure
|
removeSigner: authenticatedProcedure
|
||||||
.input(ZRemoveSignerMutationSchema)
|
.input(ZRemoveSignerMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@ -75,3 +75,13 @@ export const ZCompleteDocumentWithTokenMutationSchema = z.object({
|
|||||||
export type TCompleteDocumentWithTokenMutationSchema = z.infer<
|
export type TCompleteDocumentWithTokenMutationSchema = z.infer<
|
||||||
typeof ZCompleteDocumentWithTokenMutationSchema
|
typeof ZCompleteDocumentWithTokenMutationSchema
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export const ZRemoveTemplateSignerMutationSchema = z.object({
|
||||||
|
templateId: z.number(),
|
||||||
|
teamId: z.number().optional(),
|
||||||
|
recipientId: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TRemoveTemplateSignerMutationSchema = z.infer<
|
||||||
|
typeof ZRemoveTemplateSignerMutationSchema
|
||||||
|
>;
|
||||||
|
|||||||
@ -250,7 +250,7 @@ export const AddSignersFormPartial = ({
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
toast({
|
toast({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
description: 'An error occurred while updating the document settings.',
|
description: 'An error occurred while updating the document recipient.',
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,22 @@
|
|||||||
|
|
||||||
import React, { useEffect, useId, useMemo, useState } from 'react';
|
import React, { useEffect, useId, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Link2Icon, Plus, Trash } from 'lucide-react';
|
import { Link2Icon, Plus, Trash } from 'lucide-react';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { useFieldArray, useForm } from 'react-hook-form';
|
import { useFieldArray, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
|
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
|
||||||
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
|
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
|
||||||
import { nanoid } from '@documenso/lib/universal/id';
|
import { nanoid } from '@documenso/lib/universal/id';
|
||||||
import { generateRecipientPlaceholder } from '@documenso/lib/utils/templates';
|
import { generateRecipientPlaceholder } from '@documenso/lib/utils/templates';
|
||||||
import type { TemplateDirectLink } from '@documenso/prisma/client';
|
import type { TemplateDirectLink } from '@documenso/prisma/client';
|
||||||
import { type Field, type Recipient, RecipientRole } from '@documenso/prisma/client';
|
import { type Field, type Recipient, RecipientRole } from '@documenso/prisma/client';
|
||||||
|
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
|
||||||
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
|
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
|
||||||
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
|
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
|
||||||
import { RecipientRoleSelect } from '@documenso/ui/components/recipient/recipient-role-select';
|
import { RecipientRoleSelect } from '@documenso/ui/components/recipient/recipient-role-select';
|
||||||
@ -20,6 +25,7 @@ import { cn } from '@documenso/ui/lib/utils';
|
|||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
|
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
|
||||||
import { Input } from '@documenso/ui/primitives/input';
|
import { Input } from '@documenso/ui/primitives/input';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
import { Checkbox } from '../checkbox';
|
import { Checkbox } from '../checkbox';
|
||||||
import {
|
import {
|
||||||
@ -44,6 +50,7 @@ export type AddTemplatePlaceholderRecipientsFormProps = {
|
|||||||
templateDirectLink: TemplateDirectLink | null;
|
templateDirectLink: TemplateDirectLink | null;
|
||||||
isEnterprise: boolean;
|
isEnterprise: boolean;
|
||||||
isDocumentPdfLoaded: boolean;
|
isDocumentPdfLoaded: boolean;
|
||||||
|
template: TemplateWithDetails;
|
||||||
onSubmit: (_data: TAddTemplatePlacholderRecipientsFormSchema) => void;
|
onSubmit: (_data: TAddTemplatePlacholderRecipientsFormSchema) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,7 +62,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
fields,
|
fields,
|
||||||
isDocumentPdfLoaded,
|
isDocumentPdfLoaded,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
template,
|
||||||
}: AddTemplatePlaceholderRecipientsFormProps) => {
|
}: AddTemplatePlaceholderRecipientsFormProps) => {
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
const initialId = useId();
|
const initialId = useId();
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
|
||||||
@ -67,6 +77,39 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
|
|
||||||
const { currentStep, totalSteps, previousStep } = useStep();
|
const { currentStep, totalSteps, previousStep } = useStep();
|
||||||
|
|
||||||
|
const utils = trpc.useUtils();
|
||||||
|
|
||||||
|
const { mutateAsync: addTemplateSigners } = trpc.recipient.addTemplateSigners.useMutation({
|
||||||
|
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
|
onSuccess: (newData) => {
|
||||||
|
utils.template.getTemplateWithDetailsById.setData(
|
||||||
|
{
|
||||||
|
id: template.id,
|
||||||
|
},
|
||||||
|
(oldData) => ({ ...(oldData || template), ...newData }),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutateAsync: removeTemplateSigner } = trpc.recipient.removeTemplateSigner.useMutation({
|
||||||
|
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
|
onSuccess: (deletedRecipient) => {
|
||||||
|
console.log('deletedRecipient', deletedRecipient);
|
||||||
|
utils.template.getTemplateWithDetailsById.setData(
|
||||||
|
{
|
||||||
|
id: template.id,
|
||||||
|
},
|
||||||
|
(oldData) => {
|
||||||
|
if (!oldData) return template;
|
||||||
|
return {
|
||||||
|
...oldData,
|
||||||
|
recipients: oldData.recipients.filter((r) => r.id !== deletedRecipient.id),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const generateDefaultFormSigners = () => {
|
const generateDefaultFormSigners = () => {
|
||||||
if (recipients.length === 0) {
|
if (recipients.length === 0) {
|
||||||
return [
|
return [
|
||||||
@ -154,10 +197,6 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
setPlaceholderRecipientCount((count) => count + 1);
|
setPlaceholderRecipientCount((count) => count + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRemoveSigner = (index: number) => {
|
|
||||||
removeSigner(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isSignerDirectRecipient = (
|
const isSignerDirectRecipient = (
|
||||||
signer: TAddTemplatePlacholderRecipientsFormSchema['signers'][number],
|
signer: TAddTemplatePlacholderRecipientsFormSchema['signers'][number],
|
||||||
): boolean => {
|
): boolean => {
|
||||||
@ -167,6 +206,58 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnBlur = async (index: number) => {
|
||||||
|
try {
|
||||||
|
const currentSigner = form.getValues(`signers.${index}`);
|
||||||
|
|
||||||
|
if (!currentSigner.email) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Updating signer', currentSigner);
|
||||||
|
|
||||||
|
await addTemplateSigners({
|
||||||
|
templateId: template.id,
|
||||||
|
teamId: template.teamId ?? undefined,
|
||||||
|
signers: form.getValues('signers'),
|
||||||
|
});
|
||||||
|
|
||||||
|
router.refresh();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'An error occurred while updating the template recipient.',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveSigner = async (index: number) => {
|
||||||
|
const signer = signers[index];
|
||||||
|
|
||||||
|
console.log('signer', signer);
|
||||||
|
|
||||||
|
if (!signer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSigner(index);
|
||||||
|
|
||||||
|
if (signer.nativeId) {
|
||||||
|
await removeTemplateSigner({
|
||||||
|
templateId: template.id,
|
||||||
|
teamId: template.teamId ?? undefined,
|
||||||
|
recipientId: signer.nativeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Signer removed',
|
||||||
|
description: 'The signer has been removed from the document.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DocumentFlowFormContainerHeader
|
<DocumentFlowFormContainerHeader
|
||||||
@ -216,6 +307,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
signers[index].email === user?.email ||
|
signers[index].email === user?.email ||
|
||||||
isSignerDirectRecipient(signer)
|
isSignerDirectRecipient(signer)
|
||||||
}
|
}
|
||||||
|
onBlur={() => void handleOnBlur(index)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
@ -246,6 +338,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
signers[index].email === user?.email ||
|
signers[index].email === user?.email ||
|
||||||
isSignerDirectRecipient(signer)
|
isSignerDirectRecipient(signer)
|
||||||
}
|
}
|
||||||
|
onBlur={() => void handleOnBlur(index)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
@ -281,7 +374,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<RecipientRoleSelect
|
<RecipientRoleSelect
|
||||||
{...field}
|
{...field}
|
||||||
onValueChange={field.onChange}
|
onValueChange={(value) => {
|
||||||
|
field.onChange(value);
|
||||||
|
void handleOnBlur(index);
|
||||||
|
}}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
hideCCRecipients={isSignerDirectRecipient(signer)}
|
hideCCRecipients={isSignerDirectRecipient(signer)}
|
||||||
/>
|
/>
|
||||||
@ -313,7 +409,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
|||||||
type="button"
|
type="button"
|
||||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
disabled={isSubmitting || signers.length === 1}
|
disabled={isSubmitting || signers.length === 1}
|
||||||
onClick={() => onRemoveSigner(index)}
|
onClick={() => void handleRemoveSigner(index)}
|
||||||
>
|
>
|
||||||
<Trash className="h-5 w-5" />
|
<Trash className="h-5 w-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user