mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 09:12:02 +10:00
feat: save subject and message on blur
This commit is contained in:
@ -38,6 +38,7 @@ import {
|
|||||||
ZResendDocumentMutationSchema,
|
ZResendDocumentMutationSchema,
|
||||||
ZSearchDocumentsMutationSchema,
|
ZSearchDocumentsMutationSchema,
|
||||||
ZSendDocumentMutationSchema,
|
ZSendDocumentMutationSchema,
|
||||||
|
ZSetDocumentEmailSettingsMutationSchema,
|
||||||
ZSetPasswordForDocumentMutationSchema,
|
ZSetPasswordForDocumentMutationSchema,
|
||||||
ZSetSettingsForDocumentMutationSchema,
|
ZSetSettingsForDocumentMutationSchema,
|
||||||
ZSetTitleForDocumentMutationSchema,
|
ZSetTitleForDocumentMutationSchema,
|
||||||
@ -275,6 +276,32 @@ export const documentRouter = router({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
setDocumentEmailSettings: authenticatedProcedure
|
||||||
|
.input(ZSetDocumentEmailSettingsMutationSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const { documentId, subject, message } = input;
|
||||||
|
|
||||||
|
const userId = ctx.user.id;
|
||||||
|
|
||||||
|
return await upsertDocumentMeta({
|
||||||
|
documentId,
|
||||||
|
userId,
|
||||||
|
subject,
|
||||||
|
message,
|
||||||
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message:
|
||||||
|
'We were unable to update the email settings for this document. Please try again later.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
setPasswordForDocument: authenticatedProcedure
|
setPasswordForDocument: authenticatedProcedure
|
||||||
.input(ZSetPasswordForDocumentMutationSchema)
|
.input(ZSetPasswordForDocumentMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@ -139,6 +139,12 @@ export const ZSendDocumentMutationSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ZSetDocumentEmailSettingsMutationSchema = z.object({
|
||||||
|
documentId: z.number(),
|
||||||
|
subject: z.string().optional(),
|
||||||
|
message: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
export const ZSetPasswordForDocumentMutationSchema = z.object({
|
export const ZSetPasswordForDocumentMutationSchema = z.object({
|
||||||
documentId: z.number(),
|
documentId: z.number(),
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
|
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
|
||||||
import type { Field, Recipient } from '@documenso/prisma/client';
|
import type { Field, Recipient } from '@documenso/prisma/client';
|
||||||
import { DocumentStatus } from '@documenso/prisma/client';
|
import { DocumentStatus } from '@documenso/prisma/client';
|
||||||
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
||||||
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper';
|
import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import { Button } from '../button';
|
||||||
import { FormErrorMessage } from '../form/form-error-message';
|
import { FormErrorMessage } from '../form/form-error-message';
|
||||||
import { Input } from '../input';
|
import { Input } from '../input';
|
||||||
import { Label } from '../label';
|
import { Label } from '../label';
|
||||||
@ -44,6 +52,7 @@ export const AddSubjectFormPartial = ({
|
|||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
} = useForm<TAddSubjectFormSchema>({
|
} = useForm<TAddSubjectFormSchema>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@ -55,6 +64,87 @@ export const AddSubjectFormPartial = ({
|
|||||||
resolver: zodResolver(ZAddSubjectFormSchema),
|
resolver: zodResolver(ZAddSubjectFormSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [oldEmailData, setOldEmailData] = useState<{ subject: string; message: string } | null>(
|
||||||
|
() =>
|
||||||
|
document.documentMeta
|
||||||
|
? {
|
||||||
|
subject: document.documentMeta.subject ?? '',
|
||||||
|
message: document.documentMeta.message ?? '',
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
|
const utils = trpc.useUtils();
|
||||||
|
|
||||||
|
const { mutateAsync: setEmailSettingsForDocument } =
|
||||||
|
trpc.document.setDocumentEmailSettings.useMutation({
|
||||||
|
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
|
onSuccess: () => {
|
||||||
|
const data = utils.document.getDocumentWithDetailsById.getData({
|
||||||
|
id: document.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
setOldEmailData({
|
||||||
|
subject: data?.documentMeta?.subject ?? '',
|
||||||
|
message: data?.documentMeta?.message ?? '',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleOnBlur = async (field: 'subject' | 'message', value: string) => {
|
||||||
|
try {
|
||||||
|
await setEmailSettingsForDocument({
|
||||||
|
documentId: document.id,
|
||||||
|
[field]: value,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.refresh();
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Email settings updated',
|
||||||
|
description: `The email settings for the document have been updated.`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'An error occurred while updating the email settings.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUndoButton = async () => {
|
||||||
|
try {
|
||||||
|
if (oldEmailData) {
|
||||||
|
await setEmailSettingsForDocument({
|
||||||
|
documentId: document.id,
|
||||||
|
subject: oldEmailData.subject,
|
||||||
|
message: oldEmailData.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
setValue('meta.subject', oldEmailData.subject);
|
||||||
|
setValue('meta.message', oldEmailData.message);
|
||||||
|
|
||||||
|
setOldEmailData(null);
|
||||||
|
router.refresh();
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Changes reverted',
|
||||||
|
description: 'The latest change has been reverted to the original value.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'An error occurred while undoing the latest change.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onFormSubmit = handleSubmit(onSubmit);
|
const onFormSubmit = handleSubmit(onSubmit);
|
||||||
const { currentStep, totalSteps, previousStep } = useStep();
|
const { currentStep, totalSteps, previousStep } = useStep();
|
||||||
|
|
||||||
@ -64,6 +154,7 @@ export const AddSubjectFormPartial = ({
|
|||||||
title={documentFlow.title}
|
title={documentFlow.title}
|
||||||
description={documentFlow.description}
|
description={documentFlow.description}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DocumentFlowFormContainerContent>
|
<DocumentFlowFormContainerContent>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{isDocumentPdfLoaded &&
|
{isDocumentPdfLoaded &&
|
||||||
@ -81,7 +172,11 @@ export const AddSubjectFormPartial = ({
|
|||||||
id="subject"
|
id="subject"
|
||||||
className="bg-background mt-2"
|
className="bg-background mt-2"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
{...register('meta.subject')}
|
{...register('meta.subject', {
|
||||||
|
onBlur: (event) => {
|
||||||
|
void handleOnBlur('subject', event.target.value);
|
||||||
|
},
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormErrorMessage className="mt-2" error={errors.meta?.subject} />
|
<FormErrorMessage className="mt-2" error={errors.meta?.subject} />
|
||||||
@ -96,7 +191,11 @@ export const AddSubjectFormPartial = ({
|
|||||||
id="message"
|
id="message"
|
||||||
className="bg-background mt-2 h-32 resize-none"
|
className="bg-background mt-2 h-32 resize-none"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
{...register('meta.message')}
|
{...register('meta.message', {
|
||||||
|
onBlur: (event) => {
|
||||||
|
void handleOnBlur('message', event.target.value);
|
||||||
|
},
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormErrorMessage
|
<FormErrorMessage
|
||||||
@ -105,6 +204,11 @@ export const AddSubjectFormPartial = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Hide the button after 5 seconds */}
|
||||||
|
{oldEmailData && (
|
||||||
|
<Button onClick={() => void handleUndoButton()}>Undo latest change</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
<DocumentSendEmailMessageHelper />
|
<DocumentSendEmailMessageHelper />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user