This change actually makes the authoring flow work for the most part by tying in emailing and more. We have also done a number of quality of life updates to simplify the codebase overall making it easier to continue work on the refresh.
247 lines
7.2 KiB
TypeScript
247 lines
7.2 KiB
TypeScript
'use client';
|
|
|
|
import React, { useId } from 'react';
|
|
|
|
import { useRouter } from 'next/navigation';
|
|
|
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
import { Plus, Trash } from 'lucide-react';
|
|
import { nanoid } from 'nanoid';
|
|
import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
|
|
|
import { Document, Field, Recipient, SendStatus } from '@documenso/prisma/client';
|
|
import { Button } from '@documenso/ui/primitives/button';
|
|
import { Input } from '@documenso/ui/primitives/input';
|
|
import { Label } from '@documenso/ui/primitives/label';
|
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
|
|
|
import { FormErrorMessage } from '~/components/form/form-error-message';
|
|
|
|
import { addSigners } from './add-signers.action';
|
|
import { TAddSignersFormSchema } from './add-signers.types';
|
|
import {
|
|
EditDocumentFormContainer,
|
|
EditDocumentFormContainerActions,
|
|
EditDocumentFormContainerContent,
|
|
EditDocumentFormContainerFooter,
|
|
EditDocumentFormContainerStep,
|
|
} from './container';
|
|
|
|
export type AddSignersFormProps = {
|
|
recipients: Recipient[];
|
|
fields: Field[];
|
|
document: Document;
|
|
onContinue?: () => void;
|
|
onGoBack?: () => void;
|
|
};
|
|
|
|
export const AddSignersFormPartial = ({
|
|
recipients,
|
|
fields: _fields,
|
|
document: document,
|
|
onContinue,
|
|
onGoBack,
|
|
}: AddSignersFormProps) => {
|
|
const { toast } = useToast();
|
|
const router = useRouter();
|
|
|
|
const initialId = useId();
|
|
|
|
const {
|
|
control,
|
|
handleSubmit,
|
|
formState: { errors, isSubmitting },
|
|
} = useForm<TAddSignersFormSchema>({
|
|
defaultValues: {
|
|
signers:
|
|
recipients.length > 0
|
|
? recipients.map((recipient) => ({
|
|
nativeId: recipient.id,
|
|
formId: String(recipient.id),
|
|
name: recipient.name,
|
|
email: recipient.email,
|
|
}))
|
|
: [
|
|
{
|
|
formId: initialId,
|
|
name: '',
|
|
email: '',
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
const {
|
|
append: appendSigner,
|
|
fields: signers,
|
|
remove: removeSigner,
|
|
} = useFieldArray({
|
|
control,
|
|
name: 'signers',
|
|
});
|
|
|
|
const hasBeenSentToRecipientId = (id?: number) => {
|
|
if (!id) {
|
|
return false;
|
|
}
|
|
|
|
return recipients.some(
|
|
(recipient) => recipient.id === id && recipient.sendStatus === SendStatus.SENT,
|
|
);
|
|
};
|
|
|
|
const onAddSigner = () => {
|
|
appendSigner({
|
|
formId: nanoid(12),
|
|
name: '',
|
|
email: '',
|
|
});
|
|
};
|
|
|
|
const onRemoveSigner = (index: number) => {
|
|
const signer = signers[index];
|
|
|
|
if (hasBeenSentToRecipientId(signer.nativeId)) {
|
|
toast({
|
|
title: 'Cannot remove signer',
|
|
description: 'This signer has already received the document.',
|
|
variant: 'destructive',
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
removeSigner(index);
|
|
};
|
|
|
|
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (event.key === 'Enter' && event.target instanceof HTMLInputElement) {
|
|
onAddSigner();
|
|
}
|
|
};
|
|
|
|
const onFormSubmit = handleSubmit(async (data: TAddSignersFormSchema) => {
|
|
try {
|
|
// Custom invocation server action
|
|
await addSigners({
|
|
documentId: document.id,
|
|
signers: data.signers,
|
|
});
|
|
|
|
router.refresh();
|
|
|
|
onContinue?.();
|
|
} catch (err) {
|
|
console.error(err);
|
|
|
|
toast({
|
|
title: 'Error',
|
|
description: 'An error occurred while adding signers.',
|
|
variant: 'destructive',
|
|
});
|
|
}
|
|
});
|
|
|
|
return (
|
|
<EditDocumentFormContainer onSubmit={onFormSubmit}>
|
|
<EditDocumentFormContainerContent
|
|
title="Add Signers"
|
|
description="Add the people who will sign the document."
|
|
>
|
|
<div className="flex-col flex w-full gap-y-4">
|
|
<AnimatePresence>
|
|
{signers.map((signer, index) => (
|
|
<motion.div
|
|
key={signer.formId}
|
|
data-native-id={signer.nativeId}
|
|
className="flex flex-wrap items-end gap-x-4"
|
|
>
|
|
<div className="flex-1">
|
|
<Label htmlFor={`signer-${signer.formId}-email`}>
|
|
Email
|
|
<span className="text-destructive ml-1 inline-block font-medium">*</span>
|
|
</Label>
|
|
|
|
<Controller
|
|
control={control}
|
|
name={`signers.${index}.email`}
|
|
render={({ field }) => (
|
|
<Input
|
|
id={`signer-${signer.formId}-email`}
|
|
type="email"
|
|
className="bg-background mt-2"
|
|
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
|
|
onKeyDown={onKeyDown}
|
|
{...field}
|
|
/>
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex-1">
|
|
<Label htmlFor={`signer-${signer.formId}-name`}>Name</Label>
|
|
|
|
<Controller
|
|
control={control}
|
|
name={`signers.${index}.name`}
|
|
render={({ field }) => (
|
|
<Input
|
|
id={`signer-${signer.formId}-name`}
|
|
type="text"
|
|
className="bg-background mt-2"
|
|
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
|
|
onKeyDown={onKeyDown}
|
|
{...field}
|
|
/>
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<button
|
|
type="button"
|
|
className="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 ||
|
|
hasBeenSentToRecipientId(signer.nativeId) ||
|
|
signers.length === 1
|
|
}
|
|
onClick={() => onRemoveSigner(index)}
|
|
>
|
|
<Trash className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="w-full">
|
|
<FormErrorMessage className="mt-2" error={errors.signers?.[index]?.email} />
|
|
<FormErrorMessage className="mt-2" error={errors.signers?.[index]?.name} />
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</AnimatePresence>
|
|
</div>
|
|
|
|
<FormErrorMessage className="mt-2" error={errors.signers} />
|
|
|
|
<div className="mt-4">
|
|
<Button type="button" disabled={isSubmitting} onClick={() => onAddSigner()}>
|
|
<Plus className="-ml-1 mr-2 h-5 w-5" />
|
|
Add Signer
|
|
</Button>
|
|
</div>
|
|
</EditDocumentFormContainerContent>
|
|
|
|
<EditDocumentFormContainerFooter>
|
|
<EditDocumentFormContainerStep title="Add Signers" step={1} maxStep={3} />
|
|
|
|
<EditDocumentFormContainerActions
|
|
loading={isSubmitting}
|
|
disabled={isSubmitting}
|
|
onGoNextClick={() => onFormSubmit()}
|
|
onGoBackClick={onGoBack}
|
|
/>
|
|
</EditDocumentFormContainerFooter>
|
|
</EditDocumentFormContainer>
|
|
);
|
|
};
|