mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
fix: duplicate templates (#1104)
## Description Fix the issue where duplicate templates can be created ## Changes Made - Prevent duplicate templates by correctly disabling elements - Clear the PDF when the form is reset - General UI changes to new template dialog for consistency - Add description for new template dialog - Add close button for new template dialog ## Testing Performed Manual testing ## Checklist - [X] I have tested these changes locally and they work as expected. - [X] I have followed the project's coding style guidelines. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced new components in the template creation dialog for better usability: `DialogClose`, `DialogDescription`, and `DialogFooter`. - Enhanced file upload handling and display. - Added a cancel button and a create template button to the dialog footer for improved navigation. - **Refactor** - Updated the dialog content and form layout for a more intuitive user experience. - Refactored form structure and labels for clarity. - **Removed Features** - Removed the `Label` component from the dialog. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@ -18,7 +18,10 @@ import { Button } from '@documenso/ui/primitives/button';
|
|||||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
@ -34,7 +37,6 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@documenso/ui/primitives/form/form';
|
} from '@documenso/ui/primitives/form/form';
|
||||||
import { Input } from '@documenso/ui/primitives/input';
|
import { Input } from '@documenso/ui/primitives/input';
|
||||||
import { Label } from '@documenso/ui/primitives/label';
|
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
const ZCreateTemplateFormSchema = z.object({
|
const ZCreateTemplateFormSchema = z.object({
|
||||||
@ -61,8 +63,7 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
|||||||
resolver: zodResolver(ZCreateTemplateFormSchema),
|
resolver: zodResolver(ZCreateTemplateFormSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: createTemplate, isLoading: isCreatingTemplate } =
|
const { mutateAsync: createTemplate } = trpc.template.createTemplate.useMutation();
|
||||||
trpc.template.createTemplate.useMutation();
|
|
||||||
|
|
||||||
const [showNewTemplateDialog, setShowNewTemplateDialog] = useState(false);
|
const [showNewTemplateDialog, setShowNewTemplateDialog] = useState(false);
|
||||||
const [uploadedFile, setUploadedFile] = useState<{ file: File; fileBase64: string } | null>();
|
const [uploadedFile, setUploadedFile] = useState<{ file: File; fileBase64: string } | null>();
|
||||||
@ -140,6 +141,7 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showNewTemplateDialog) {
|
if (!showNewTemplateDialog) {
|
||||||
form.reset();
|
form.reset();
|
||||||
|
setUploadedFile(null);
|
||||||
}
|
}
|
||||||
}, [form, showNewTemplateDialog]);
|
}, [form, showNewTemplateDialog]);
|
||||||
|
|
||||||
@ -154,20 +156,23 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
|||||||
|
|
||||||
<DialogContent className="w-full max-w-xl">
|
<DialogContent className="w-full max-w-xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="mb-4">New Template</DialogTitle>
|
<DialogTitle>New Template</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Templates allow you to quickly generate documents with pre-filled recipients and fields.
|
||||||
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div>
|
<Form {...form}>
|
||||||
<Form {...form}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="flex flex-col gap-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Name your template</FormLabel>
|
<FormLabel>Template name</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input id="email" type="text" className="bg-background mt-1.5" {...field} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
<span className="text-muted-foreground text-xs">
|
<span className="text-muted-foreground text-xs">
|
||||||
@ -180,55 +185,57 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div className="mt-1.5">
|
||||||
<Label htmlFor="template">Upload a Document</Label>
|
{uploadedFile ? (
|
||||||
|
<Card gradient className="h-[40vh]">
|
||||||
|
<CardContent className="flex h-full flex-col items-center justify-center p-2">
|
||||||
|
<button
|
||||||
|
onClick={() => resetForm()}
|
||||||
|
title="Remove Template"
|
||||||
|
className="text-muted-foreground absolute right-2.5 top-2.5 rounded-sm opacity-60 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none"
|
||||||
|
>
|
||||||
|
<X className="h-6 w-6" />
|
||||||
|
<span className="sr-only">Remove Template</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className="my-3">
|
<div className="border-muted-foreground/20 group-hover:border-documenso/80 dark:bg-muted/80 z-10 flex aspect-[3/4] w-24 flex-col gap-y-1 rounded-lg border bg-white/80 px-2 py-4 backdrop-blur-sm">
|
||||||
{uploadedFile ? (
|
<div className="bg-muted-foreground/20 group-hover:bg-documenso h-2 w-full rounded-[2px]" />
|
||||||
<Card gradient className="h-[40vh]">
|
<div className="bg-muted-foreground/20 group-hover:bg-documenso h-2 w-5/6 rounded-[2px]" />
|
||||||
<CardContent className="flex h-full flex-col items-center justify-center p-2">
|
<div className="bg-muted-foreground/20 group-hover:bg-documenso h-2 w-full rounded-[2px]" />
|
||||||
<button
|
</div>
|
||||||
onClick={() => resetForm()}
|
|
||||||
title="Remove Template"
|
|
||||||
className="text-muted-foreground absolute right-2.5 top-2.5 rounded-sm opacity-60 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none"
|
|
||||||
>
|
|
||||||
<X className="h-6 w-6" />
|
|
||||||
<span className="sr-only">Remove Template</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div className="border-muted-foreground/20 group-hover:border-documenso/80 dark:bg-muted/80 z-10 flex aspect-[3/4] w-24 flex-col gap-y-1 rounded-lg border bg-white/80 px-2 py-4 backdrop-blur-sm">
|
<p className="group-hover:text-foreground text-muted-foreground mt-4 font-medium">
|
||||||
<div className="bg-muted-foreground/20 group-hover:bg-documenso h-2 w-full rounded-[2px]" />
|
Uploaded Document
|
||||||
<div className="bg-muted-foreground/20 group-hover:bg-documenso h-2 w-5/6 rounded-[2px]" />
|
</p>
|
||||||
<div className="bg-muted-foreground/20 group-hover:bg-documenso h-2 w-full rounded-[2px]" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="group-hover:text-foreground text-muted-foreground mt-4 font-medium">
|
<span className="text-muted-foreground/80 mt-1 text-sm">
|
||||||
Uploaded Document
|
{uploadedFile.file.name}
|
||||||
</p>
|
</span>
|
||||||
|
</CardContent>
|
||||||
<span className="text-muted-foreground/80 mt-1 text-sm">
|
</Card>
|
||||||
{uploadedFile.file.name}
|
) : (
|
||||||
</span>
|
<DocumentDropzone className="h-[40vh]" onDrop={onFileDrop} type="template" />
|
||||||
</CardContent>
|
)}
|
||||||
</Card>
|
|
||||||
) : (
|
|
||||||
<DocumentDropzone
|
|
||||||
className="mt-1.5 h-[40vh]"
|
|
||||||
onDrop={onFileDrop}
|
|
||||||
type="template"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex w-full justify-end">
|
<DialogFooter>
|
||||||
<Button loading={isCreatingTemplate} type="submit">
|
<DialogClose asChild>
|
||||||
Create Template
|
<Button type="button" variant="secondary">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
loading={form.formState.isSubmitting}
|
||||||
|
disabled={!uploadedFile}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Create template
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</DialogFooter>
|
||||||
</form>
|
</fieldset>
|
||||||
</Form>
|
</form>
|
||||||
</div>
|
</Form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user