mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 17:35:05 +10:00
committed by
GitHub
parent
0b9a23c550
commit
6f650e1c2f
@@ -0,0 +1,130 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { Trans, useLingui } from '@lingui/react/macro';
|
||||||
|
|
||||||
|
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||||
|
import { DOCUMENT_TITLE_MAX_LENGTH } from '@documenso/trpc/server/document-router/schema';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@documenso/ui/primitives/dialog';
|
||||||
|
import { Input } from '@documenso/ui/primitives/input';
|
||||||
|
import { Label } from '@documenso/ui/primitives/label';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
export type EnvelopeRenameDialogProps = {
|
||||||
|
id: string;
|
||||||
|
initialTitle: string;
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (_open: boolean) => void;
|
||||||
|
onSuccess?: () => Promise<void>;
|
||||||
|
envelopeType?: 'document' | 'template';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EnvelopeRenameDialog = ({
|
||||||
|
id,
|
||||||
|
initialTitle,
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
onSuccess,
|
||||||
|
envelopeType = 'document',
|
||||||
|
}: EnvelopeRenameDialogProps) => {
|
||||||
|
const { toast } = useToast();
|
||||||
|
const { t } = useLingui();
|
||||||
|
|
||||||
|
const [title, setTitle] = useState(initialTitle);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setTitle(initialTitle);
|
||||||
|
}
|
||||||
|
}, [open, initialTitle]);
|
||||||
|
|
||||||
|
const isTemplate = envelopeType === 'template';
|
||||||
|
|
||||||
|
const { mutate: updateEnvelope, isPending } = trpcReact.envelope.update.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
await onSuccess?.();
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: isTemplate ? t`Template Renamed` : t`Document Renamed`,
|
||||||
|
description: isTemplate
|
||||||
|
? t`Your template has been successfully renamed.`
|
||||||
|
: t`Your document has been successfully renamed.`,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
onOpenChange(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
description: t`Something went wrong. Please try again.`,
|
||||||
|
variant: 'destructive',
|
||||||
|
duration: 7500,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const trimmedTitle = title.trim();
|
||||||
|
|
||||||
|
const onRename = () => {
|
||||||
|
if (!trimmedTitle || trimmedTitle === initialTitle) {
|
||||||
|
onOpenChange(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEnvelope({
|
||||||
|
envelopeId: id,
|
||||||
|
data: {
|
||||||
|
title: trimmedTitle,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={(value) => !isPending && onOpenChange(value)}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
{isTemplate ? <Trans>Rename Template</Trans> : <Trans>Rename Document</Trans>}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="py-2">
|
||||||
|
<Label htmlFor="title" className="sr-only">
|
||||||
|
<Trans>Title</Trans>
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="title"
|
||||||
|
value={title}
|
||||||
|
placeholder={t`Enter a new title`}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
disabled={isPending}
|
||||||
|
maxLength={DOCUMENT_TITLE_MAX_LENGTH}
|
||||||
|
className="w-full"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="button" variant="secondary" onClick={() => onOpenChange(false)}>
|
||||||
|
<Trans>Cancel</Trans>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={isPending || !trimmedTitle || trimmedTitle === initialTitle}
|
||||||
|
loading={isPending}
|
||||||
|
onClick={() => void onRename()}
|
||||||
|
>
|
||||||
|
<Trans>Rename</Trans>
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
|
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { useSearchParams } from 'react-router';
|
import { useSearchParams } from 'react-router';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn';
|
import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn';
|
||||||
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
|
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
FileOutputIcon,
|
FileOutputIcon,
|
||||||
Loader,
|
Loader,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
|
Pencil,
|
||||||
ScrollTextIcon,
|
ScrollTextIcon,
|
||||||
Share,
|
Share,
|
||||||
Trash2,
|
Trash2,
|
||||||
@@ -18,8 +19,12 @@ import { Link, useNavigate } from 'react-router';
|
|||||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||||
import type { TEnvelope } from '@documenso/lib/types/envelope';
|
import type { TEnvelope } from '@documenso/lib/types/envelope';
|
||||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||||
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
|
import {
|
||||||
|
getEnvelopeItemPermissions,
|
||||||
|
mapSecondaryIdToDocumentId,
|
||||||
|
} from '@documenso/lib/utils/envelope';
|
||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
|
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||||
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -33,6 +38,7 @@ import { DocumentDeleteDialog } from '~/components/dialogs/document-delete-dialo
|
|||||||
import { DocumentDuplicateDialog } from '~/components/dialogs/document-duplicate-dialog';
|
import { DocumentDuplicateDialog } from '~/components/dialogs/document-duplicate-dialog';
|
||||||
import { DocumentResendDialog } from '~/components/dialogs/document-resend-dialog';
|
import { DocumentResendDialog } from '~/components/dialogs/document-resend-dialog';
|
||||||
import { EnvelopeDownloadDialog } from '~/components/dialogs/envelope-download-dialog';
|
import { EnvelopeDownloadDialog } from '~/components/dialogs/envelope-download-dialog';
|
||||||
|
import { EnvelopeRenameDialog } from '~/components/dialogs/envelope-rename-dialog';
|
||||||
import { EnvelopeSaveAsTemplateDialog } from '~/components/dialogs/envelope-save-as-template-dialog';
|
import { EnvelopeSaveAsTemplateDialog } from '~/components/dialogs/envelope-save-as-template-dialog';
|
||||||
import { DocumentRecipientLinkCopyDialog } from '~/components/general/document/document-recipient-link-copy-dialog';
|
import { DocumentRecipientLinkCopyDialog } from '~/components/general/document/document-recipient-link-copy-dialog';
|
||||||
import { useCurrentTeam } from '~/providers/team';
|
import { useCurrentTeam } from '~/providers/team';
|
||||||
@@ -47,8 +53,11 @@ export const DocumentPageViewDropdown = ({ envelope }: DocumentPageViewDropdownP
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
|
|
||||||
|
const trpcUtils = trpcReact.useUtils();
|
||||||
|
|
||||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
||||||
|
const [isRenameDialogOpen, setRenameDialogOpen] = useState(false);
|
||||||
|
|
||||||
const recipient = envelope.recipients.find((recipient) => recipient.email === user.email);
|
const recipient = envelope.recipients.find((recipient) => recipient.email === user.email);
|
||||||
|
|
||||||
@@ -60,6 +69,8 @@ export const DocumentPageViewDropdown = ({ envelope }: DocumentPageViewDropdownP
|
|||||||
const isCurrentTeamDocument = team && envelope.teamId === team.id;
|
const isCurrentTeamDocument = team && envelope.teamId === team.id;
|
||||||
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
|
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
|
||||||
|
|
||||||
|
const { canTitleBeChanged } = getEnvelopeItemPermissions(envelope, []);
|
||||||
|
|
||||||
const documentsPath = formatDocumentsPath(team.url);
|
const documentsPath = formatDocumentsPath(team.url);
|
||||||
|
|
||||||
const nonSignedRecipients = envelope.recipients.filter((item) => item.signingStatus !== 'SIGNED');
|
const nonSignedRecipients = envelope.recipients.filter((item) => item.signingStatus !== 'SIGNED');
|
||||||
@@ -84,6 +95,13 @@ export const DocumentPageViewDropdown = ({ envelope }: DocumentPageViewDropdownP
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{canManageDocument && canTitleBeChanged && (
|
||||||
|
<DropdownMenuItem onClick={() => setRenameDialogOpen(true)}>
|
||||||
|
<Pencil className="mr-2 h-4 w-4" />
|
||||||
|
<Trans>Rename</Trans>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
<EnvelopeDownloadDialog
|
<EnvelopeDownloadDialog
|
||||||
envelopeId={envelope.id}
|
envelopeId={envelope.id}
|
||||||
envelopeStatus={envelope.status}
|
envelopeStatus={envelope.status}
|
||||||
@@ -189,6 +207,16 @@ export const DocumentPageViewDropdown = ({ envelope }: DocumentPageViewDropdownP
|
|||||||
onOpenChange={setDuplicateDialogOpen}
|
onOpenChange={setDuplicateDialogOpen}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<EnvelopeRenameDialog
|
||||||
|
id={envelope.id}
|
||||||
|
initialTitle={envelope.title}
|
||||||
|
open={isRenameDialogOpen}
|
||||||
|
onOpenChange={setRenameDialogOpen}
|
||||||
|
onSuccess={async () => {
|
||||||
|
await trpcUtils.envelope.get.invalidate();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|||||||
import { msg } from '@lingui/core/macro';
|
import { msg } from '@lingui/core/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { DocumentStatus, RecipientRole } from '@prisma/client';
|
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
|
||||||
import {
|
import {
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Copy,
|
Copy,
|
||||||
@@ -23,7 +23,9 @@ import { Link } from 'react-router';
|
|||||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||||
import type { TDocumentMany as TDocumentRow } from '@documenso/lib/types/document';
|
import type { TDocumentMany as TDocumentRow } from '@documenso/lib/types/document';
|
||||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||||
|
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
|
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||||
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -41,6 +43,7 @@ import { DocumentRecipientLinkCopyDialog } from '~/components/general/document/d
|
|||||||
import { useCurrentTeam } from '~/providers/team';
|
import { useCurrentTeam } from '~/providers/team';
|
||||||
|
|
||||||
import { EnvelopeDownloadDialog } from '../dialogs/envelope-download-dialog';
|
import { EnvelopeDownloadDialog } from '../dialogs/envelope-download-dialog';
|
||||||
|
import { EnvelopeRenameDialog } from '../dialogs/envelope-rename-dialog';
|
||||||
|
|
||||||
export type DocumentsTableActionDropdownProps = {
|
export type DocumentsTableActionDropdownProps = {
|
||||||
row: TDocumentRow;
|
row: TDocumentRow;
|
||||||
@@ -55,9 +58,11 @@ export const DocumentsTableActionDropdown = ({
|
|||||||
const team = useCurrentTeam();
|
const team = useCurrentTeam();
|
||||||
|
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
const trpcUtils = trpcReact.useUtils();
|
||||||
|
|
||||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
||||||
|
const [isRenameDialogOpen, setRenameDialogOpen] = useState(false);
|
||||||
|
|
||||||
const recipient = row.recipients.find((recipient) => recipient.email === user.email);
|
const recipient = row.recipients.find((recipient) => recipient.email === user.email);
|
||||||
|
|
||||||
@@ -70,6 +75,16 @@ export const DocumentsTableActionDropdown = ({
|
|||||||
const isCurrentTeamDocument = team && row.team?.url === team.url;
|
const isCurrentTeamDocument = team && row.team?.url === team.url;
|
||||||
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
|
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
|
||||||
|
|
||||||
|
const { canTitleBeChanged } = getEnvelopeItemPermissions(
|
||||||
|
{
|
||||||
|
completedAt: row.completedAt,
|
||||||
|
deletedAt: row.deletedAt,
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
status: row.status,
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const documentsPath = formatDocumentsPath(team.url);
|
const documentsPath = formatDocumentsPath(team.url);
|
||||||
const formatPath = `${documentsPath}/${row.envelopeId}/edit`;
|
const formatPath = `${documentsPath}/${row.envelopeId}/edit`;
|
||||||
|
|
||||||
@@ -123,6 +138,13 @@ export const DocumentsTableActionDropdown = ({
|
|||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
{canManageDocument && canTitleBeChanged && (
|
||||||
|
<DropdownMenuItem onClick={() => setRenameDialogOpen(true)}>
|
||||||
|
<Pencil className="mr-2 h-4 w-4" />
|
||||||
|
<Trans>Rename</Trans>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
<EnvelopeDownloadDialog
|
<EnvelopeDownloadDialog
|
||||||
envelopeId={row.envelopeId}
|
envelopeId={row.envelopeId}
|
||||||
envelopeStatus={row.status}
|
envelopeStatus={row.status}
|
||||||
@@ -221,6 +243,16 @@ export const DocumentsTableActionDropdown = ({
|
|||||||
open={isDuplicateDialogOpen}
|
open={isDuplicateDialogOpen}
|
||||||
onOpenChange={setDuplicateDialogOpen}
|
onOpenChange={setDuplicateDialogOpen}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<EnvelopeRenameDialog
|
||||||
|
id={row.envelopeId}
|
||||||
|
initialTitle={row.title}
|
||||||
|
open={isRenameDialogOpen}
|
||||||
|
onOpenChange={setRenameDialogOpen}
|
||||||
|
onSuccess={async () => {
|
||||||
|
await trpcUtils.document.findDocumentsInternal.invalidate();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,9 +2,19 @@ import { useState } from 'react';
|
|||||||
|
|
||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import type { Recipient, TemplateDirectLink } from '@prisma/client';
|
import type { Recipient, TemplateDirectLink } from '@prisma/client';
|
||||||
import { Copy, Edit, FolderIcon, MoreHorizontal, Share2Icon, Trash2, Upload } from 'lucide-react';
|
import {
|
||||||
|
Copy,
|
||||||
|
Edit,
|
||||||
|
FolderIcon,
|
||||||
|
MoreHorizontal,
|
||||||
|
Pencil,
|
||||||
|
Share2Icon,
|
||||||
|
Trash2,
|
||||||
|
Upload,
|
||||||
|
} from 'lucide-react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
|
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -13,6 +23,7 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@documenso/ui/primitives/dropdown-menu';
|
} from '@documenso/ui/primitives/dropdown-menu';
|
||||||
|
|
||||||
|
import { EnvelopeRenameDialog } from '../dialogs/envelope-rename-dialog';
|
||||||
import { TemplateBulkSendDialog } from '../dialogs/template-bulk-send-dialog';
|
import { TemplateBulkSendDialog } from '../dialogs/template-bulk-send-dialog';
|
||||||
import { TemplateDeleteDialog } from '../dialogs/template-delete-dialog';
|
import { TemplateDeleteDialog } from '../dialogs/template-delete-dialog';
|
||||||
import { TemplateDirectLinkDialog } from '../dialogs/template-direct-link-dialog';
|
import { TemplateDirectLinkDialog } from '../dialogs/template-direct-link-dialog';
|
||||||
@@ -41,8 +52,11 @@ export const TemplatesTableActionDropdown = ({
|
|||||||
teamId,
|
teamId,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: TemplatesTableActionDropdownProps) => {
|
}: TemplatesTableActionDropdownProps) => {
|
||||||
|
const trpcUtils = trpcReact.useUtils();
|
||||||
|
|
||||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
||||||
|
const [isRenameDialogOpen, setRenameDialogOpen] = useState(false);
|
||||||
const [isMoveToFolderDialogOpen, setMoveToFolderDialogOpen] = useState(false);
|
const [isMoveToFolderDialogOpen, setMoveToFolderDialogOpen] = useState(false);
|
||||||
|
|
||||||
const isTeamTemplate = row.teamId === teamId;
|
const isTeamTemplate = row.teamId === teamId;
|
||||||
@@ -66,6 +80,13 @@ export const TemplatesTableActionDropdown = ({
|
|||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
{canMutate && (
|
||||||
|
<DropdownMenuItem onClick={() => setRenameDialogOpen(true)}>
|
||||||
|
<Pencil className="mr-2 h-4 w-4" />
|
||||||
|
<Trans>Rename</Trans>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
<DropdownMenuItem disabled={!canMutate} onClick={() => setDuplicateDialogOpen(true)}>
|
<DropdownMenuItem disabled={!canMutate} onClick={() => setDuplicateDialogOpen(true)}>
|
||||||
<Copy className="mr-2 h-4 w-4" />
|
<Copy className="mr-2 h-4 w-4" />
|
||||||
<Trans>Duplicate</Trans>
|
<Trans>Duplicate</Trans>
|
||||||
@@ -132,6 +153,17 @@ export const TemplatesTableActionDropdown = ({
|
|||||||
onOpenChange={setMoveToFolderDialogOpen}
|
onOpenChange={setMoveToFolderDialogOpen}
|
||||||
currentFolderId={row.folderId}
|
currentFolderId={row.folderId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<EnvelopeRenameDialog
|
||||||
|
id={row.envelopeId}
|
||||||
|
initialTitle={row.title}
|
||||||
|
open={isRenameDialogOpen}
|
||||||
|
onOpenChange={setRenameDialogOpen}
|
||||||
|
envelopeType="template"
|
||||||
|
onSuccess={async () => {
|
||||||
|
await trpcUtils.template.findTemplates.invalidate();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { DocumentVisibility } from '@prisma/client';
|
import { DocumentVisibility } from '@prisma/client';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const DOCUMENT_TITLE_MAX_LENGTH = 255;
|
||||||
|
|
||||||
export const ZDocumentTitleSchema = z
|
export const ZDocumentTitleSchema = z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(255)
|
.max(DOCUMENT_TITLE_MAX_LENGTH)
|
||||||
.describe('The title of the document.');
|
.describe('The title of the document.');
|
||||||
|
|
||||||
export const ZDocumentExternalIdSchema = z
|
export const ZDocumentExternalIdSchema = z
|
||||||
|
|||||||
Reference in New Issue
Block a user