mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
feat: add/update title of the document (#663)
This commit is contained in:
@ -4,7 +4,7 @@ import { useState } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { DocumentData, Field, Recipient, User } from '@documenso/prisma/client';
|
||||
import { DocumentData, DocumentStatus, Field, Recipient, User } from '@documenso/prisma/client';
|
||||
import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
@ -14,6 +14,8 @@ import { AddSignersFormPartial } from '@documenso/ui/primitives/document-flow/ad
|
||||
import { TAddSignersFormSchema } from '@documenso/ui/primitives/document-flow/add-signers.types';
|
||||
import { AddSubjectFormPartial } from '@documenso/ui/primitives/document-flow/add-subject';
|
||||
import { TAddSubjectFormSchema } from '@documenso/ui/primitives/document-flow/add-subject.types';
|
||||
import { AddTitleFormPartial } from '@documenso/ui/primitives/document-flow/add-title';
|
||||
import { TAddTitleFormSchema } from '@documenso/ui/primitives/document-flow/add-title.types';
|
||||
import {
|
||||
DocumentFlowFormContainer,
|
||||
DocumentFlowFormContainerHeader,
|
||||
@ -25,6 +27,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
import { addFields } from '~/components/forms/edit-document/add-fields.action';
|
||||
import { addSigners } from '~/components/forms/edit-document/add-signers.action';
|
||||
import { completeDocument } from '~/components/forms/edit-document/add-subject.action';
|
||||
import { addTitle } from '~/components/forms/edit-document/add-title.action';
|
||||
|
||||
export type EditDocumentFormProps = {
|
||||
className?: string;
|
||||
@ -35,7 +38,7 @@ export type EditDocumentFormProps = {
|
||||
documentData: DocumentData;
|
||||
};
|
||||
|
||||
type EditDocumentStep = 'signers' | 'fields' | 'subject';
|
||||
type EditDocumentStep = 'title' | 'signers' | 'fields' | 'subject';
|
||||
|
||||
export const EditDocumentForm = ({
|
||||
className,
|
||||
@ -48,30 +51,60 @@ export const EditDocumentForm = ({
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
|
||||
const [step, setStep] = useState<EditDocumentStep>('signers');
|
||||
const [step, setStep] = useState<EditDocumentStep>(
|
||||
document.status === DocumentStatus.DRAFT ? 'title' : 'signers',
|
||||
);
|
||||
|
||||
const documentFlow: Record<EditDocumentStep, DocumentFlowStep> = {
|
||||
title: {
|
||||
title: 'Add Title',
|
||||
description: 'Add the title to the document.',
|
||||
stepIndex: 1,
|
||||
},
|
||||
signers: {
|
||||
title: 'Add Signers',
|
||||
description: 'Add the people who will sign the document.',
|
||||
stepIndex: 1,
|
||||
stepIndex: 2,
|
||||
onBackStep: () => document.status === DocumentStatus.DRAFT && setStep('title'),
|
||||
},
|
||||
fields: {
|
||||
title: 'Add Fields',
|
||||
description: 'Add all relevant fields for each recipient.',
|
||||
stepIndex: 2,
|
||||
stepIndex: 3,
|
||||
onBackStep: () => setStep('signers'),
|
||||
},
|
||||
subject: {
|
||||
title: 'Add Subject',
|
||||
description: 'Add the subject and message you wish to send to signers.',
|
||||
stepIndex: 3,
|
||||
stepIndex: 4,
|
||||
onBackStep: () => setStep('fields'),
|
||||
},
|
||||
};
|
||||
|
||||
const currentDocumentFlow = documentFlow[step];
|
||||
|
||||
const onAddTitleFormSubmit = async (data: TAddTitleFormSchema) => {
|
||||
try {
|
||||
// Custom invocation server action
|
||||
await addTitle({
|
||||
documentId: document.id,
|
||||
title: data.title,
|
||||
});
|
||||
|
||||
router.refresh();
|
||||
|
||||
setStep('signers');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while updating title.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onAddSignersFormSubmit = async (data: TAddSignersFormSchema) => {
|
||||
try {
|
||||
// Custom invocation server action
|
||||
@ -164,10 +197,23 @@ export const EditDocumentForm = ({
|
||||
description={currentDocumentFlow.description}
|
||||
/>
|
||||
|
||||
{step === 'title' && (
|
||||
<AddTitleFormPartial
|
||||
key={recipients.length}
|
||||
documentFlow={documentFlow.title}
|
||||
recipients={recipients}
|
||||
fields={fields}
|
||||
document={document}
|
||||
numberOfSteps={Object.keys(documentFlow).length}
|
||||
onSubmit={onAddTitleFormSubmit}
|
||||
/>
|
||||
)}
|
||||
|
||||
{step === 'signers' && (
|
||||
<AddSignersFormPartial
|
||||
key={recipients.length}
|
||||
documentFlow={documentFlow.signers}
|
||||
document={document}
|
||||
recipients={recipients}
|
||||
fields={fields}
|
||||
numberOfSteps={Object.keys(documentFlow).length}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
'use server';
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { updateTitle } from '@documenso/lib/server-only/document/update-title';
|
||||
import type { TAddTitleFormSchema } from '@documenso/ui/primitives/document-flow/add-title.types';
|
||||
|
||||
export type AddTitleActionInput = TAddTitleFormSchema & {
|
||||
documentId: number;
|
||||
};
|
||||
|
||||
export const addTitle = async ({ documentId, title }: AddTitleActionInput) => {
|
||||
'use server';
|
||||
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
|
||||
await updateTitle({
|
||||
documentId,
|
||||
userId: user.id,
|
||||
title: title,
|
||||
});
|
||||
};
|
||||
21
packages/lib/server-only/document/update-title.ts
Normal file
21
packages/lib/server-only/document/update-title.ts
Normal file
@ -0,0 +1,21 @@
|
||||
'use server';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type UpdateTitleOptions = {
|
||||
userId: number;
|
||||
documentId: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const updateTitle = async ({ userId, documentId, title }: UpdateTitleOptions) => {
|
||||
return await prisma.document.update({
|
||||
where: {
|
||||
id: documentId,
|
||||
userId,
|
||||
},
|
||||
data: {
|
||||
title,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -8,6 +8,7 @@ import { getDocumentById } from '@documenso/lib/server-only/document/get-documen
|
||||
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { updateTitle } from '@documenso/lib/server-only/document/update-title';
|
||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||
|
||||
@ -21,6 +22,7 @@ import {
|
||||
ZSendDocumentMutationSchema,
|
||||
ZSetFieldsForDocumentMutationSchema,
|
||||
ZSetRecipientsForDocumentMutationSchema,
|
||||
ZSetTitleForDocumentMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
export const documentRouter = router({
|
||||
@ -113,6 +115,20 @@ export const documentRouter = router({
|
||||
}
|
||||
}),
|
||||
|
||||
setTitleForDocument: authenticatedProcedure
|
||||
.input(ZSetTitleForDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { documentId, title } = input;
|
||||
|
||||
const userId = ctx.user.id;
|
||||
|
||||
return await updateTitle({
|
||||
title,
|
||||
userId,
|
||||
documentId,
|
||||
});
|
||||
}),
|
||||
|
||||
setRecipientsForDocument: authenticatedProcedure
|
||||
.input(ZSetRecipientsForDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@ -21,6 +21,13 @@ export const ZCreateDocumentMutationSchema = z.object({
|
||||
|
||||
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
|
||||
|
||||
export const ZSetTitleForDocumentMutationSchema = z.object({
|
||||
documentId: z.number(),
|
||||
title: z.string().min(1),
|
||||
});
|
||||
|
||||
export type TSetTitleForDocumentMutationSchema = z.infer<typeof ZSetTitleForDocumentMutationSchema>;
|
||||
|
||||
export const ZSetRecipientsForDocumentMutationSchema = z.object({
|
||||
documentId: z.number(),
|
||||
recipients: z.array(
|
||||
|
||||
@ -9,7 +9,8 @@ import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
||||
|
||||
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { Field, Recipient, SendStatus } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, Field, Recipient, SendStatus } from '@documenso/prisma/client';
|
||||
import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
@ -29,6 +30,7 @@ export type AddSignersFormProps = {
|
||||
documentFlow: DocumentFlowStep;
|
||||
recipients: Recipient[];
|
||||
fields: Field[];
|
||||
document: DocumentWithData;
|
||||
numberOfSteps: number;
|
||||
onSubmit: (_data: TAddSignersFormSchema) => void;
|
||||
};
|
||||
@ -37,6 +39,7 @@ export const AddSignersFormPartial = ({
|
||||
documentFlow,
|
||||
numberOfSteps,
|
||||
recipients,
|
||||
document,
|
||||
fields: _fields,
|
||||
onSubmit,
|
||||
}: AddSignersFormProps) => {
|
||||
@ -223,6 +226,7 @@ export const AddSignersFormPartial = ({
|
||||
/>
|
||||
|
||||
<DocumentFlowFormContainerActions
|
||||
canGoBack={document.status === DocumentStatus.DRAFT}
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
onGoBackClick={documentFlow.onBackStep}
|
||||
|
||||
88
packages/ui/primitives/document-flow/add-title.tsx
Normal file
88
packages/ui/primitives/document-flow/add-title.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
'use client';
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import type { Field, Recipient } from '@documenso/prisma/client';
|
||||
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
||||
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import { Label } from '@documenso/ui/primitives/label';
|
||||
|
||||
import type { TAddTitleFormSchema } from './add-title.types';
|
||||
import {
|
||||
DocumentFlowFormContainerActions,
|
||||
DocumentFlowFormContainerContent,
|
||||
DocumentFlowFormContainerFooter,
|
||||
DocumentFlowFormContainerStep,
|
||||
} from './document-flow-root';
|
||||
import type { DocumentFlowStep } from './types';
|
||||
|
||||
export type AddTitleFormProps = {
|
||||
documentFlow: DocumentFlowStep;
|
||||
recipients: Recipient[];
|
||||
fields: Field[];
|
||||
document: DocumentWithData;
|
||||
numberOfSteps: number;
|
||||
onSubmit: (_data: TAddTitleFormSchema) => void;
|
||||
};
|
||||
|
||||
export const AddTitleFormPartial = ({
|
||||
documentFlow,
|
||||
recipients: _recipients,
|
||||
fields: _fields,
|
||||
document,
|
||||
numberOfSteps,
|
||||
onSubmit,
|
||||
}: AddTitleFormProps) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<TAddTitleFormSchema>({
|
||||
defaultValues: {
|
||||
title: document.title,
|
||||
},
|
||||
});
|
||||
|
||||
const onFormSubmit = handleSubmit(onSubmit);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentFlowFormContainerContent>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<div>
|
||||
<Label htmlFor="title">
|
||||
Title<span className="text-destructive ml-1 inline-block font-medium">*</span>
|
||||
</Label>
|
||||
|
||||
<Input
|
||||
id="title"
|
||||
className="bg-background mt-2"
|
||||
disabled={isSubmitting}
|
||||
{...register('title', { required: "Title can't be empty" })}
|
||||
/>
|
||||
|
||||
<FormErrorMessage className="mt-2" error={errors.title} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFlowFormContainerContent>
|
||||
|
||||
<DocumentFlowFormContainerFooter>
|
||||
<DocumentFlowFormContainerStep
|
||||
title={documentFlow.title}
|
||||
step={documentFlow.stepIndex}
|
||||
maxStep={numberOfSteps}
|
||||
/>
|
||||
|
||||
<DocumentFlowFormContainerActions
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
onGoBackClick={documentFlow.onBackStep}
|
||||
onGoNextClick={() => void onFormSubmit()}
|
||||
/>
|
||||
</DocumentFlowFormContainerFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
7
packages/ui/primitives/document-flow/add-title.types.ts
Normal file
7
packages/ui/primitives/document-flow/add-title.types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZAddTitleFormSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
});
|
||||
|
||||
export type TAddTitleFormSchema = z.infer<typeof ZAddTitleFormSchema>;
|
||||
@ -3,6 +3,8 @@ import { z } from 'zod';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
|
||||
export const ZDocumentFlowFormSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
|
||||
signers: z
|
||||
.array(
|
||||
z.object({
|
||||
@ -52,6 +54,6 @@ export interface DocumentFlowStep {
|
||||
title: string;
|
||||
description: string;
|
||||
stepIndex: number;
|
||||
onBackStep?: () => void;
|
||||
onNextStep?: () => void;
|
||||
onBackStep?: () => unknown;
|
||||
onNextStep?: () => unknown;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user