fix: allow users to download templates (#2746)

This commit is contained in:
David Nguyen
2026-04-30 16:50:07 +10:00
committed by GitHub
parent 84fc866cfb
commit c428170b5c
4 changed files with 265 additions and 98 deletions
@@ -1,14 +1,10 @@
import { useState } from 'react';
import { Trans } from '@lingui/react/macro';
import {
DocumentStatus,
EnvelopeType,
type Recipient,
type TemplateDirectLink,
} from '@prisma/client';
import { DocumentStatus, EnvelopeType, type TemplateDirectLink } from '@prisma/client';
import {
Copy,
Download,
Edit,
FolderIcon,
MoreHorizontal,
@@ -30,6 +26,7 @@ import {
} from '@documenso/ui/primitives/dropdown-menu';
import { EnvelopeDeleteDialog } from '../dialogs/envelope-delete-dialog';
import { EnvelopeDownloadDialog } from '../dialogs/envelope-download-dialog';
import { EnvelopeDuplicateDialog } from '../dialogs/envelope-duplicate-dialog';
import { EnvelopeRenameDialog } from '../dialogs/envelope-rename-dialog';
import { TemplateBulkSendDialog } from '../dialogs/template-bulk-send-dialog';
@@ -77,87 +74,94 @@ export const TemplatesTableActionDropdown = ({
<DropdownMenuContent className="w-52" align="start" forceMount>
<DropdownMenuLabel>Action</DropdownMenuLabel>
<DropdownMenuItem disabled={!canMutate} asChild>
<Link to={formatPath}>
<Edit className="mr-2 h-4 w-4" />
<Trans>Edit</Trans>
</Link>
</DropdownMenuItem>
{canMutate && (
<DropdownMenuItem onClick={() => setRenameDialogOpen(true)}>
<Pencil className="mr-2 h-4 w-4" />
<Trans>Rename</Trans>
</DropdownMenuItem>
)}
{canMutate && (
<EnvelopeDuplicateDialog
envelopeId={row.envelopeId}
envelopeType={EnvelopeType.TEMPLATE}
trigger={
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
<div>
<Copy className="mr-2 h-4 w-4" />
<Trans>Duplicate</Trans>
</div>
</DropdownMenuItem>
}
/>
)}
{canMutate && (
<TemplateDirectLinkDialog
templateId={row.id}
recipients={row.recipients}
directLink={row.directLink}
trigger={
<div
data-testid="template-direct-link"
className="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground"
>
<Share2Icon className="mr-2 h-4 w-4" />
<Trans>Direct link</Trans>
<EnvelopeDownloadDialog
envelopeId={row.envelopeId}
envelopeStatus={DocumentStatus.DRAFT}
trigger={
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
<div>
<Download className="mr-2 h-4 w-4" />
<Trans>Download</Trans>
</div>
}
/>
)}
<DropdownMenuItem disabled={!canMutate} onClick={() => setMoveToFolderDialogOpen(true)}>
<FolderIcon className="mr-2 h-4 w-4" />
<Trans>Move to Folder</Trans>
</DropdownMenuItem>
</DropdownMenuItem>
}
/>
{canMutate && (
<TemplateBulkSendDialog
templateId={row.id}
recipients={row.recipients}
trigger={
<div className="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground">
<Upload className="mr-2 h-4 w-4" />
<Trans>Bulk Send via CSV</Trans>
</div>
}
/>
)}
<>
<DropdownMenuItem asChild>
<Link to={formatPath}>
<Edit className="mr-2 h-4 w-4" />
<Trans>Edit</Trans>
</Link>
</DropdownMenuItem>
{canMutate && (
<EnvelopeDeleteDialog
id={row.envelopeId}
type={EnvelopeType.TEMPLATE}
status={DocumentStatus.DRAFT}
title={row.title}
canManageDocument={canMutate}
onDelete={onDelete}
trigger={
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
<div>
<Trash2 className="mr-2 h-4 w-4" />
<Trans>Delete</Trans>
<DropdownMenuItem onClick={() => setRenameDialogOpen(true)}>
<Pencil className="mr-2 h-4 w-4" />
<Trans>Rename</Trans>
</DropdownMenuItem>
<EnvelopeDuplicateDialog
envelopeId={row.envelopeId}
envelopeType={EnvelopeType.TEMPLATE}
trigger={
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
<div>
<Copy className="mr-2 h-4 w-4" />
<Trans>Duplicate</Trans>
</div>
</DropdownMenuItem>
}
/>
<TemplateDirectLinkDialog
templateId={row.id}
recipients={row.recipients}
directLink={row.directLink}
trigger={
<div
data-testid="template-direct-link"
className="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground"
>
<Share2Icon className="mr-2 h-4 w-4" />
<Trans>Direct link</Trans>
</div>
</DropdownMenuItem>
}
/>
}
/>
<DropdownMenuItem onClick={() => setMoveToFolderDialogOpen(true)}>
<FolderIcon className="mr-2 h-4 w-4" />
<Trans>Move to Folder</Trans>
</DropdownMenuItem>
<TemplateBulkSendDialog
templateId={row.id}
recipients={row.recipients}
trigger={
<div className="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground">
<Upload className="mr-2 h-4 w-4" />
<Trans>Bulk Send via CSV</Trans>
</div>
}
/>
<EnvelopeDeleteDialog
id={row.envelopeId}
type={EnvelopeType.TEMPLATE}
status={DocumentStatus.DRAFT}
title={row.title}
canManageDocument={canMutate}
onDelete={onDelete}
trigger={
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
<div>
<Trash2 className="mr-2 h-4 w-4" />
<Trans>Delete</Trans>
</div>
</DropdownMenuItem>
}
/>
</>
)}
</DropdownMenuContent>
@@ -248,20 +248,18 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
<Trans>Template</Trans>
</h3>
{isOwnTeamTemplate && (
<div>
<TemplatesTableActionDropdown
row={{
...envelope,
id: mapSecondaryIdToTemplateId(envelope.secondaryId),
envelopeId: envelope.id,
}}
teamId={team?.id}
templateRootPath={templateRootPath}
onDelete={async () => navigate(templateRootPath)}
/>
</div>
)}
<div>
<TemplatesTableActionDropdown
row={{
...envelope,
id: mapSecondaryIdToTemplateId(envelope.secondaryId),
envelopeId: envelope.id,
}}
teamId={team?.id}
templateRootPath={templateRootPath}
onDelete={async () => navigate(templateRootPath)}
/>
</div>
</div>
<p className="mt-2 px-4 text-sm text-muted-foreground">