Compare commits

..

2 Commits

Author SHA1 Message Date
07c852744b v1.9.0-rc.6 2025-01-08 20:18:09 +11:00
4fab98c633 feat: allow switching document when using templates (#1571)
Adds the ability to upload a custom document when using a template.

This is useful when you have a given fixed template with placeholder
values that you want to decorate with Documenso fields but will then
create a final specialised document when sending it out to a given
recipient.
2025-01-07 16:13:35 +11:00
139 changed files with 779 additions and 867 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@documenso/web",
"version": "1.9.0-rc.5",
"version": "1.9.0-rc.6",
"private": true,
"license": "AGPL-3.0",
"scripts": {

View File

@ -10,7 +10,7 @@ import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
import type { Document, Recipient, User } from '@documenso/prisma/client';
export type DocumentPageViewInformationProps = {
userId: string;
userId: number;
document: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
Recipient: Recipient[];

View File

@ -16,7 +16,7 @@ import { cn } from '@documenso/ui/lib/utils';
export type DocumentPageViewRecentActivityProps = {
documentId: number;
userId: string;
userId: number;
};
export const DocumentPageViewRecentActivity = ({

View File

@ -10,7 +10,7 @@ import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
import type { Template, User } from '@documenso/prisma/client';
export type TemplatePageViewInformationProps = {
userId: string;
userId: number;
template: Template & {
User: Pick<User, 'id' | 'name' | 'email'>;
};

View File

@ -7,15 +7,17 @@ import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { InfoIcon, Plus } from 'lucide-react';
import { InfoIcon, Plus, Upload, X } from 'lucide-react';
import { useFieldArray, useForm } from 'react-hook-form';
import * as z from 'zod';
import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app';
import {
TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX,
TEMPLATE_RECIPIENT_NAME_PLACEHOLDER_REGEX,
} from '@documenso/lib/constants/template';
import { AppError } from '@documenso/lib/errors/app-error';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import type { Recipient } from '@documenso/prisma/client';
import { DocumentDistributionMethod, DocumentSigningOrder } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
@ -50,6 +52,11 @@ import { useOptionalCurrentTeam } from '~/providers/team';
const ZAddRecipientsForNewDocumentSchema = z
.object({
distributeDocument: z.boolean(),
useCustomDocument: z.boolean().default(false),
customDocumentData: z
.any()
.refine((data) => data instanceof File || data === undefined)
.optional(),
recipients: z.array(
z.object({
id: z.number(),
@ -119,6 +126,8 @@ export function UseTemplateDialog({
resolver: zodResolver(ZAddRecipientsForNewDocumentSchema),
defaultValues: {
distributeDocument: false,
useCustomDocument: false,
customDocumentData: undefined,
recipients: recipients
.sort((a, b) => (a.signingOrder || 0) - (b.signingOrder || 0))
.map((recipient) => {
@ -145,11 +154,19 @@ export function UseTemplateDialog({
const onSubmit = async (data: TAddRecipientsForNewDocumentSchema) => {
try {
let customDocumentDataId: string | undefined = undefined;
if (data.useCustomDocument && data.customDocumentData) {
const customDocumentData = await putPdfFile(data.customDocumentData);
customDocumentDataId = customDocumentData.id;
}
const { id } = await createDocumentFromTemplate({
templateId,
teamId: team?.id,
recipients: data.recipients,
distributeDocument: data.distributeDocument,
customDocumentDataId,
});
toast({
@ -300,89 +317,245 @@ export function UseTemplateDialog({
/>
</div>
))}
{recipients.length > 0 && (
<div className="mt-4 flex flex-row items-center">
<FormField
control={form.control}
name="distributeDocument"
render={({ field }) => (
<FormItem>
<div className="flex flex-row items-center">
<Checkbox
id="distributeDocument"
className="h-5 w-5"
checked={field.value}
onCheckedChange={field.onChange}
/>
{documentDistributionMethod === DocumentDistributionMethod.EMAIL && (
<label
className="text-muted-foreground ml-2 flex items-center text-sm"
htmlFor="distributeDocument"
>
<Trans>Send document</Trans>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="mx-1 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground z-[99999] max-w-md space-y-2 p-4">
<p>
<Trans>
The document will be immediately sent to recipients if this
is checked.
</Trans>
</p>
<p>
<Trans>
Otherwise, the document will be created as a draft.
</Trans>
</p>
</TooltipContent>
</Tooltip>
</label>
)}
{documentDistributionMethod === DocumentDistributionMethod.NONE && (
<label
className="text-muted-foreground ml-2 flex items-center text-sm"
htmlFor="distributeDocument"
>
<Trans>Create as pending</Trans>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="mx-1 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground z-[99999] max-w-md space-y-2 p-4">
<p>
<Trans>
Create the document as pending and ready to sign.
</Trans>
</p>
<p>
<Trans>We won't send anything to notify recipients.</Trans>
</p>
<p className="mt-2">
<Trans>
We will generate signing links for you, which you can send
to the recipients through your method of choice.
</Trans>
</p>
</TooltipContent>
</Tooltip>
</label>
)}
</div>
</FormItem>
)}
/>
</div>
)}
<FormField
control={form.control}
name="useCustomDocument"
render={({ field }) => (
<FormItem>
<div className="flex flex-row items-center">
<Checkbox
id="useCustomDocument"
className="h-5 w-5"
checked={field.value}
onCheckedChange={(checked) => {
field.onChange(checked);
if (!checked) {
form.setValue('customDocumentData', undefined);
}
}}
/>
<label
className="text-muted-foreground ml-2 flex items-center text-sm"
htmlFor="useCustomDocument"
>
<Trans>Upload custom document</Trans>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="mx-1 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground z-[99999] max-w-md space-y-2 p-4">
<p>
<Trans>
Upload a custom document to use instead of the template's default
document
</Trans>
</p>
</TooltipContent>
</Tooltip>
</label>
</div>
</FormItem>
)}
/>
{form.watch('useCustomDocument') && (
<div className="my-4">
<FormField
control={form.control}
name="customDocumentData"
render={({ field }) => (
<FormItem>
<FormControl>
<div className="w-full space-y-4">
<label
className={cn(
'text-muted-foreground hover:border-muted-foreground/50 group relative flex min-h-[150px] cursor-pointer flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 px-6 py-10 transition-colors',
{
'border-destructive hover:border-destructive':
form.formState.errors.customDocumentData,
},
)}
>
<div className="text-center">
{!field.value && (
<>
<Upload className="text-muted-foreground/50 mx-auto h-10 w-10" />
<div className="mt-4 flex text-sm leading-6">
<span className="text-muted-foreground relative">
<Trans>
<span className="text-primary font-semibold">
Click to upload
</span>{' '}
or drag and drop
</Trans>
</span>
</div>
<p className="text-muted-foreground/80 text-xs">
PDF files only
</p>
</>
)}
{field.value && (
<div className="text-muted-foreground space-y-1">
<p className="text-sm font-medium">{field.value.name}</p>
<p className="text-muted-foreground/60 text-xs">
{(field.value.size / (1024 * 1024)).toFixed(2)} MB
</p>
</div>
)}
</div>
<input
type="file"
className="absolute h-full w-full opacity-0"
accept=".pdf,application/pdf"
onChange={(e) => {
const file = e.target.files?.[0];
if (!file) {
field.onChange(undefined);
return;
}
if (file.type !== 'application/pdf') {
form.setError('customDocumentData', {
type: 'manual',
message: _(msg`Please select a PDF file`),
});
return;
}
if (file.size > APP_DOCUMENT_UPLOAD_SIZE_LIMIT * 1024 * 1024) {
form.setError('customDocumentData', {
type: 'manual',
message: _(
msg`File size exceeds the limit of ${APP_DOCUMENT_UPLOAD_SIZE_LIMIT} MB`,
),
});
return;
}
field.onChange(file);
}}
/>
{field.value && (
<div className="absolute right-2 top-2">
<Button
type="button"
variant="destructive"
className="h-6 w-6 p-0"
onClick={(e) => {
e.preventDefault();
field.onChange(undefined);
}}
>
<X className="h-4 w-4" />
<div className="sr-only">
<Trans>Clear file</Trans>
</div>
</Button>
</div>
)}
</label>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</div>
{recipients.length > 0 && (
<div className="mt-4 flex flex-row items-center">
<FormField
control={form.control}
name="distributeDocument"
render={({ field }) => (
<FormItem>
<div className="flex flex-row items-center">
<Checkbox
id="distributeDocument"
className="h-5 w-5"
checked={field.value}
onCheckedChange={field.onChange}
/>
{documentDistributionMethod === DocumentDistributionMethod.EMAIL && (
<label
className="text-muted-foreground ml-2 flex items-center text-sm"
htmlFor="distributeDocument"
>
<Trans>Send document</Trans>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="mx-1 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground z-[99999] max-w-md space-y-2 p-4">
<p>
<Trans>
The document will be immediately sent to recipients if this is
checked.
</Trans>
</p>
<p>
<Trans>
Otherwise, the document will be created as a draft.
</Trans>
</p>
</TooltipContent>
</Tooltip>
</label>
)}
{documentDistributionMethod === DocumentDistributionMethod.NONE && (
<label
className="text-muted-foreground ml-2 flex items-center text-sm"
htmlFor="distributeDocument"
>
<Trans>Create as pending</Trans>
<Tooltip>
<TooltipTrigger type="button">
<InfoIcon className="mx-1 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground z-[99999] max-w-md space-y-2 p-4">
<p>
<Trans>Create the document as pending and ready to sign.</Trans>
</p>
<p>
<Trans>We won't send anything to notify recipients.</Trans>
</p>
<p className="mt-2">
<Trans>
We will generate signing links for you, which you can send to
the recipients through your method of choice.
</Trans>
</p>
</TooltipContent>
</Tooltip>
</label>
)}
</div>
</FormItem>
)}
/>
</div>
)}
<DialogFooter>
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button type="button" variant="secondary">
<Trans>Close</Trans>

View File

@ -45,7 +45,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type TransferTeamDialogProps = {
teamId: number;
teamName: string;
ownerUserId: string;
ownerUserId: number;
trigger?: React.ReactNode;
};
@ -98,7 +98,7 @@ export const TransferTeamDialog = ({
try {
await requestTeamOwnershipTransfer({
teamId,
newOwnerUserId,
newOwnerUserId: Number.parseInt(newOwnerUserId),
clearPaymentMethods,
});

View File

@ -34,7 +34,7 @@ import { UpdateTeamMemberDialog } from '../dialogs/update-team-member-dialog';
export type TeamMembersDataTableProps = {
currentUserTeamRole: TeamMemberRole;
teamOwneruserId: string;
teamOwnerUserId: number;
teamId: number;
teamName: string;
};

View File

@ -20,7 +20,7 @@ export type TeamsMemberPageDataTableProps = {
currentUserTeamRole: TeamMemberRole;
teamId: number;
teamName: string;
teamOwneruserId: string;
teamOwnerUserId: number;
};
export const TeamsMemberPageDataTable = ({

View File

@ -24,7 +24,7 @@ import { DocumentHistorySheetChanges } from './document-history-sheet-changes';
export type DocumentHistorySheetProps = {
documentId: number;
userId: string;
userId: number;
isMenuOpen?: boolean;
onMenuOpenChange?: (_value: boolean) => void;
children?: React.ReactNode;

193
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@documenso/root",
"version": "1.9.0-rc.5",
"version": "1.9.0-rc.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@documenso/root",
"version": "1.9.0-rc.5",
"version": "1.9.0-rc.6",
"workspaces": [
"apps/*",
"packages/*"
@ -15,12 +15,10 @@
"@documenso/pdf-sign": "^0.1.0",
"@documenso/prisma": "^0.0.0",
"@lingui/core": "^4.11.3",
"@prisma/client": "^6.1.0",
"inngest-cli": "^0.29.1",
"luxon": "^3.5.0",
"mupdf": "^1.0.0",
"next-runtime-env": "^3.2.0",
"prisma": "^6.1.0",
"react": "^18",
"zod": "3.24.1"
},
@ -135,7 +133,7 @@
},
"apps/web": {
"name": "@documenso/web",
"version": "1.9.0-rc.5",
"version": "1.9.0-rc.6",
"license": "AGPL-3.0",
"dependencies": {
"@documenso/api": "*",
@ -5638,13 +5636,15 @@
}
},
"node_modules/@prisma/client": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.1.0.tgz",
"integrity": "sha512-AbQYc5+EJKm1Ydfq3KxwcGiy7wIbm4/QbjCKWWoNROtvy7d6a3gmAGkKjK0iUCzh+rHV8xDhD5Cge8ke/kiy5Q==",
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.4.2.tgz",
"integrity": "sha512-2xsPaz4EaMKj1WS9iW6MlPhmbqtBsXAOeVttSePp8vTFTtvzh2hZbDgswwBdSCgPzmmwF+tLB259QzggvCmJqA==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/engines-version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574"
},
"engines": {
"node": ">=18.18"
"node": ">=16.13"
},
"peerDependencies": {
"prisma": "*"
@ -5676,49 +5676,15 @@
}
},
"node_modules/@prisma/engines": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.1.0.tgz",
"integrity": "sha512-GnYJbCiep3Vyr1P/415ReYrgJUjP79fBNc1wCo7NP6Eia0CzL2Ot9vK7Infczv3oK7JLrCcawOSAxFxNFsAERQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0",
"@prisma/engines-version": "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959",
"@prisma/fetch-engine": "6.1.0",
"@prisma/get-platform": "6.1.0"
}
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.4.2.tgz",
"integrity": "sha512-fqeucJ3LH0e1eyFdT0zRx+oETLancu5+n4lhiYECyEz6H2RDskPJHJYHkVc0LhkU4Uv7fuEnppKU3nVKNzMh8g==",
"hasInstallScript": true
},
"node_modules/@prisma/engines-version": {
"version": "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959.tgz",
"integrity": "sha512-PdJqmYM2Fd8K0weOOtQThWylwjsDlTig+8Pcg47/jszMuLL9iLIaygC3cjWJLda69siRW4STlCTMSgOjZzvKPQ==",
"license": "Apache-2.0"
},
"node_modules/@prisma/engines/node_modules/@prisma/debug": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.1.0.tgz",
"integrity": "sha512-0himsvcM4DGBTtvXkd2Tggv6sl2JyUYLzEGXXleFY+7Kp6rZeSS3hiTW9mwtUlXrwYbJP6pwlVNB7jYElrjWUg==",
"license": "Apache-2.0"
},
"node_modules/@prisma/engines/node_modules/@prisma/fetch-engine": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.1.0.tgz",
"integrity": "sha512-asdFi7TvPlEZ8CzSZ/+Du5wZ27q6OJbRSXh+S8ISZguu+S9KtS/gP7NeXceZyb1Jv1SM1S5YfiCv+STDsG6rrg==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0",
"@prisma/engines-version": "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959",
"@prisma/get-platform": "6.1.0"
}
},
"node_modules/@prisma/engines/node_modules/@prisma/get-platform": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.1.0.tgz",
"integrity": "sha512-ia8bNjboBoHkmKGGaWtqtlgQOhCi7+f85aOkPJKgNwWvYrT6l78KgojLekE8zMhVk0R9lWcifV0Pf8l3/15V0Q==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0"
}
"version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574.tgz",
"integrity": "sha512-wvupDL4AA1vf4TQNANg7kR7y98ITqPsk6aacfBxZKtrJKRIsWjURHkZCGcQliHdqCiW/hGreO6d6ZuSv9MhdAA=="
},
"node_modules/@prisma/fetch-engine": {
"version": "5.3.1",
@ -20122,6 +20088,14 @@
"node": ">=6"
}
},
"node_modules/kysely": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.3.tgz",
"integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/language-subtag-registry": {
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
@ -26188,22 +26162,26 @@
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
},
"node_modules/prisma": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.1.0.tgz",
"integrity": "sha512-aFI3Yi+ApUxkwCJJwyQSwpyzUX7YX3ihzuHNHOyv4GJg3X5tQsmRaJEnZ+ZyfHpMtnyahhmXVfbTZ+lS8ZtfKw==",
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.4.2.tgz",
"integrity": "sha512-GDMZwZy7mysB2oXU+angQqJ90iaPFdD0rHaZNkn+dio5NRkGLmMqmXs31//tg/qXT3iB0cTQwnGGQNuirhSTZg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/engines": "6.1.0"
"@prisma/engines": "5.4.2"
},
"bin": {
"prisma": "build/index.js"
},
"engines": {
"node": ">=18.18"
},
"optionalDependencies": {
"fsevents": "2.3.3"
"node": ">=16.13"
}
},
"node_modules/prisma-extension-kysely": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/prisma-extension-kysely/-/prisma-extension-kysely-2.1.0.tgz",
"integrity": "sha512-s1hujYjrNzfQc9Z79s93464mkTkkt9ZPqPELDRFe9jEmiIlM/JRXZtqIyyN3FM0GDkN/BDPVtpPtdC52XnjAcQ==",
"peerDependencies": {
"@prisma/client": "latest"
}
},
"node_modules/prisma-kysely": {
@ -34092,6 +34070,36 @@
"zod": "^3.20.2"
}
},
"node_modules/zod-prisma-types": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/zod-prisma-types/-/zod-prisma-types-3.1.8.tgz",
"integrity": "sha512-5oe0ays3ur4u2GtuUqlhgCraKBcsuMaMI8o7VMV4YAnFeOuVid7K2zGvjI19V0ue9PeNF2ICyVREQVohaQm5dw==",
"dev": true,
"dependencies": {
"@prisma/generator-helper": "^5.14.0",
"code-block-writer": "^12.0.0",
"lodash": "^4.17.21",
"zod": "^3.23.8"
},
"bin": {
"zod-prisma-types": "dist/bin.js"
}
},
"node_modules/zod-prisma-types/node_modules/@prisma/debug": {
"version": "5.22.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz",
"integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==",
"dev": true
},
"node_modules/zod-prisma-types/node_modules/@prisma/generator-helper": {
"version": "5.22.0",
"resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-5.22.0.tgz",
"integrity": "sha512-LwqcBQ5/QsuAaLNQZAIVIAJDJBMjHwMwn16e06IYx/3Okj/xEEfw9IvrqB2cJCl3b2mCBlh3eVH0w9WGmi4aHg==",
"dev": true,
"dependencies": {
"@prisma/debug": "5.22.0"
}
},
"node_modules/zod-to-json-schema": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz",
@ -35458,10 +35466,10 @@
"version": "0.0.0",
"license": "MIT",
"dependencies": {
"@prisma/client": "^6.1.0",
"kysely": "^0.27.5",
"prisma": "^6.1.0",
"prisma-extension-kysely": "^3.0.0",
"@prisma/client": "5.4.2",
"kysely": "^0.27.3",
"prisma": "5.4.2",
"prisma-extension-kysely": "^2.1.0",
"ts-pattern": "^5.0.6"
},
"devDependencies": {
@ -35470,42 +35478,7 @@
"prisma-kysely": "^1.8.0",
"tsx": "^4.11.0",
"typescript": "5.2.2",
"zod-prisma-types": "^3.2.1"
}
},
"packages/prisma/node_modules/@prisma/debug": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.1.0.tgz",
"integrity": "sha512-0himsvcM4DGBTtvXkd2Tggv6sl2JyUYLzEGXXleFY+7Kp6rZeSS3hiTW9mwtUlXrwYbJP6pwlVNB7jYElrjWUg==",
"dev": true,
"license": "Apache-2.0"
},
"packages/prisma/node_modules/@prisma/generator-helper": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-6.1.0.tgz",
"integrity": "sha512-drHaTKRmRsz6esHk2dpn7aPoxfttoqkYWSaI7zXsL5YQz73jww1YgJpGbPgOUiblriJAtdT4o7mibMqnf8TOsA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0"
}
},
"packages/prisma/node_modules/kysely": {
"version": "0.27.5",
"resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.5.tgz",
"integrity": "sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"packages/prisma/node_modules/prisma-extension-kysely": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/prisma-extension-kysely/-/prisma-extension-kysely-3.0.0.tgz",
"integrity": "sha512-J8gtIINuqsmbucei7PehAiuKvlPEe6SpRNXM6SdG7wXvY2aNxm5RnjnTwWITLTvzM3smarsKfMMfpMcNua82mA==",
"license": "MIT",
"peerDependencies": {
"@prisma/client": "latest"
"zod-prisma-types": "^3.1.8"
}
},
"packages/prisma/node_modules/ts-pattern": {
@ -35526,26 +35499,6 @@
"node": ">=14.17"
}
},
"packages/prisma/node_modules/zod-prisma-types": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/zod-prisma-types/-/zod-prisma-types-3.2.1.tgz",
"integrity": "sha512-qsOD8aMVx3Yg9gHctvhTRpavaJizt8xUda6qjwOcH7suvrirXax38tjs0ilKcY7GKbyY57q2rZ+uoSDnzVXpag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@prisma/generator-helper": "^6.0.1",
"code-block-writer": "^12.0.0",
"lodash": "^4.17.21",
"zod": "^3.23.8"
},
"bin": {
"zod-prisma-types": "dist/bin.js"
},
"peerDependencies": {
"@prisma/client": "^4.x.x || ^5.x.x || ^6.x.x",
"prisma": "^4.x.x || ^5.x.x || ^6.x.x"
}
},
"packages/signing": {
"name": "@documenso/signing",
"version": "0.0.0",

View File

@ -1,6 +1,6 @@
{
"private": true,
"version": "1.9.0-rc.5",
"version": "1.9.0-rc.6",
"scripts": {
"build": "turbo run build",
"build:web": "turbo run build --filter=@documenso/web",
@ -70,18 +70,14 @@
"luxon": "^3.5.0",
"mupdf": "^1.0.0",
"next-runtime-env": "^3.2.0",
"@prisma/client": "^6.1.0",
"prisma": "^6.1.0",
"react": "^18",
"zod": "3.24.1"
},
"overrides": {
"next": "14.2.6",
"@prisma/client": "^6.1.0",
"prisma": "^6.1.0",
"zod": "3.24.1"
},
"trigger.dev": {
"endpointId": "documenso-app"
}
}
}

View File

@ -1,7 +1,11 @@
import { expect, test } from '@playwright/test';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import { prisma } from '@documenso/prisma';
import { DocumentDataType } from '@documenso/prisma/client';
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
@ -13,6 +17,20 @@ test.describe.configure({ mode: 'parallel' });
const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || '';
// Create a temporary PDF file for testing
function createTempPdfFile() {
const tempDir = os.tmpdir();
const tempFilePath = path.join(tempDir, 'test.pdf');
// Create a simple PDF file with some content
const pdfContent = Buffer.from(
'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 612 792]/Parent 2 0 R>>endobj\nxref\n0 4\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\n0000000101 00000 n\ntrailer<</Size 4/Root 1 0 R>>\nstartxref\n178\n%%EOF',
);
fs.writeFileSync(tempFilePath, pdfContent);
return tempFilePath;
}
/**
* 1. Create a template with all settings filled out
* 2. Create a document from the template
@ -283,3 +301,231 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
expect(recipientOneAuth.derivedRecipientAccessAuth).toEqual('ACCOUNT');
expect(recipientTwoAuth.derivedRecipientAccessAuth).toEqual('ACCOUNT');
});
/**
* This test verifies that we can create a document from a template using a custom document
* instead of the template's default document.
*/
test('[TEMPLATE]: should create a document from a template with custom document', async ({
page,
}) => {
const user = await seedUser();
const template = await seedBlankTemplate(user);
// Create a temporary PDF file for upload
const testPdfPath = createTempPdfFile();
const pdfContent = fs.readFileSync(testPdfPath).toString('base64');
try {
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}/edit`,
});
// Set template title
await page.getByLabel('Title').fill('TEMPLATE_WITH_CUSTOM_DOC');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com');
await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Save template' }).click();
// Use template with custom document
await page.waitForURL('/templates');
await page.getByRole('button', { name: 'Use Template' }).click();
// Enable custom document upload and upload file
await page.getByLabel('Upload custom document').check();
await page.locator('input[type="file"]').setInputFiles(testPdfPath);
// Wait for upload to complete
await expect(page.getByText(path.basename(testPdfPath))).toBeVisible();
// Create document with custom document data
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the custom document data
await page.waitForURL(/documents/);
const documentId = Number(page.url().split('/').pop());
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
},
include: {
documentData: true,
},
});
expect(document.title).toEqual('TEMPLATE_WITH_CUSTOM_DOC');
expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64);
expect(document.documentData.data).toEqual(pdfContent);
expect(document.documentData.initialData).toEqual(pdfContent);
} finally {
// Clean up the temporary file
fs.unlinkSync(testPdfPath);
}
});
/**
* This test verifies that we can create a team document from a template using a custom document
* instead of the template's default document.
*/
test('[TEMPLATE]: should create a team document from a template with custom document', async ({
page,
}) => {
const { owner, ...team } = await seedTeam({
createTeamMembers: 2,
});
const template = await seedBlankTemplate(owner, {
createTemplateOptions: {
teamId: team.id,
},
});
// Create a temporary PDF file for upload
const testPdfPath = createTempPdfFile();
const pdfContent = fs.readFileSync(testPdfPath).toString('base64');
try {
await apiSignin({
page,
email: owner.email,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title
await page.getByLabel('Title').fill('TEAM_TEMPLATE_WITH_CUSTOM_DOC');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com');
await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Save template' }).click();
// Use template with custom document
await page.waitForURL(`/t/${team.url}/templates`);
await page.getByRole('button', { name: 'Use Template' }).click();
// Enable custom document upload and upload file
await page.getByLabel('Upload custom document').check();
await page.locator('input[type="file"]').setInputFiles(testPdfPath);
// Wait for upload to complete
await expect(page.getByText(path.basename(testPdfPath))).toBeVisible();
// Create document with custom document data
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the custom document data
await page.waitForURL(/documents/);
const documentId = Number(page.url().split('/').pop());
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
},
include: {
documentData: true,
},
});
expect(document.teamId).toEqual(team.id);
expect(document.title).toEqual('TEAM_TEMPLATE_WITH_CUSTOM_DOC');
expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64);
expect(document.documentData.data).toEqual(pdfContent);
expect(document.documentData.initialData).toEqual(pdfContent);
} finally {
// Clean up the temporary file
fs.unlinkSync(testPdfPath);
}
});
/**
* This test verifies that when custom document upload is not enabled,
* the document uses the template's original document data.
*/
test('[TEMPLATE]: should create a document from a template using template document when custom document is not enabled', async ({
page,
}) => {
const user = await seedUser();
const template = await seedBlankTemplate(user);
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}/edit`,
});
// Set template title
await page.getByLabel('Title').fill('TEMPLATE_WITH_ORIGINAL_DOC');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com');
await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Save template' }).click();
// Use template without custom document
await page.waitForURL('/templates');
await page.getByRole('button', { name: 'Use Template' }).click();
// Verify custom document upload is not checked by default
await expect(page.getByLabel('Upload custom document')).not.toBeChecked();
// Create document without custom document data
await page.getByRole('button', { name: 'Create as draft' }).click();
// Review that the document was created with the template's document data
await page.waitForURL(/documents/);
const documentId = Number(page.url().split('/').pop());
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
},
include: {
documentData: true,
},
});
const templateWithData = await prisma.template.findFirstOrThrow({
where: {
id: template.id,
},
include: {
templateDocumentData: true,
},
});
expect(document.title).toEqual('TEMPLATE_WITH_ORIGINAL_DOC');
expect(document.documentData.data).toEqual(templateWithData.templateDocumentData.data);
expect(document.documentData.initialData).toEqual(
templateWithData.templateDocumentData.initialData,
);
expect(document.documentData.type).toEqual(templateWithData.templateDocumentData.type);
});

View File

@ -91,7 +91,7 @@ export const getStripeCustomerIdByUser = async (user: User) => {
return await getStripeCustomerByUser(user).then((session) => session.stripeCustomer.id);
};
const syncStripeCustomerSubscriptions = async (userId: string, stripeCustomerId: string) => {
const syncStripeCustomerSubscriptions = async (userId: number, stripeCustomerId: string) => {
const stripeSubscriptions = await stripe.subscriptions.list({
customer: stripeCustomerId,
});

View File

@ -6,7 +6,7 @@ import type { Subscription } from '@documenso/prisma/client';
import { getEnterprisePlanPriceIds } from '../stripe/get-enterprise-plan-prices';
export type IsUserEnterpriseOptions = {
userId: string;
userId: number;
teamId?: number;
};

View File

@ -126,7 +126,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
}
return {
id: user.id,
id: Number(user.id),
email: user.email,
name: user.name,
emailVerified: user.emailVerified?.toISOString() ?? null,
@ -140,7 +140,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
profile(profile) {
return {
id: profile.sub,
id: Number(profile.sub),
name: profile.name || `${profile.given_name} ${profile.family_name}`.trim(),
email: profile.email,
emailVerified: profile.email_verified ? new Date().toISOString() : null,
@ -274,7 +274,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
});
return {
id: user.id,
id: Number(user.id),
email: user.email,
name: user.name,
emailVerified: user.emailVerified?.toISOString() ?? null,
@ -308,7 +308,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
const { userId, email } = parsedCredential;
if (typeof userId !== 'string' || typeof email !== 'string') {
if (typeof userId !== 'number' || typeof email !== 'string') {
throw new AppError(AppErrorCode.INVALID_REQUEST);
}
@ -323,7 +323,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
}
return {
id: user.id,
id: Number(user.id),
email: user.email,
name: user.name,
emailVerified: user.emailVerified?.toISOString() ?? null,
@ -340,7 +340,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
} satisfies JWT;
if (!merged.email || typeof merged.emailVerified !== 'string') {
const userId = merged.id ?? token.sub;
const userId = Number(merged.id ?? token.sub);
const retrieved = await prisma.user.findFirst({
where: {
@ -367,7 +367,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
const user = await prisma.user.update({
where: {
id: merged.id,
id: Number(merged.id),
},
data: {
lastSignedIn: merged.lastSignedIn,
@ -384,7 +384,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
await prisma.user.update({
where: {
id: merged.id,
id: Number(merged.id),
},
data: {
emailVerified: merged.emailVerified,
@ -407,7 +407,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
return {
...session,
user: {
id: token.id,
id: Number(token.id),
name: token.name,
email: token.email,
emailVerified: token.emailVerified ?? null,

View File

@ -9,7 +9,7 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
import { getAuthenticatorOptions } from '../../utils/authenticator';
type CreatePasskeyAuthenticationOptions = {
userId: string;
userId: number;
/**
* The ID of the passkey to request authentication for.

View File

@ -8,7 +8,7 @@ import { PASSKEY_TIMEOUT } from '../../constants/auth';
import { getAuthenticatorOptions } from '../../utils/authenticator';
type CreatePasskeyRegistrationOptions = {
userId: string;
userId: number;
};
export const createPasskeyRegistrationOptions = async ({

View File

@ -10,7 +10,7 @@ import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getAuthenticatorOptions } from '../../utils/authenticator';
type CreatePasskeyOptions = {
userId: string;
userId: number;
passkeyName: string;
verificationResponse: RegistrationResponseJSON;
requestMetadata?: RequestMetadata;

View File

@ -4,7 +4,7 @@ import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
export interface DeletePasskeyOptions {
userId: string;
userId: number;
passkeyId: string;
requestMetadata?: RequestMetadata;
}

View File

@ -5,7 +5,7 @@ import { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';
export interface FindPasskeysOptions {
userId: string;
userId: number;
query?: string;
page?: number;
perPage?: number;

View File

@ -11,7 +11,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
export interface SendConfirmationEmailProps {
userId: string;
userId: number;
}
export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailProps) => {

View File

@ -11,7 +11,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
export interface SendForgotPasswordOptions {
userId: string;
userId: number;
}
export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions) => {

View File

@ -8,7 +8,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
export interface SendResetPasswordOptions {
userId: string;
userId: number;
}
export const sendResetPassword = async ({ userId }: SendResetPasswordOptions) => {

View File

@ -4,7 +4,7 @@ import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
export interface UpdateAuthenticatorsOptions {
userId: string;
userId: number;
passkeyId: string;
name: string;
requestMetadata?: RequestMetadata;

View File

@ -25,7 +25,7 @@ export type CreateDocumentMetaOptions = {
distributionMethod?: DocumentDistributionMethod;
typedSignatureEnabled?: boolean;
language?: SupportedLanguageCodes;
userId: string;
userId: number;
requestMetadata: RequestMetadata;
};

View File

@ -21,7 +21,7 @@ import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = {
title: string;
externalId?: string | null;
userId: string;
userId: number;
teamId?: number;
documentDataId: string;
formValues?: Record<string, string | number | boolean>;

View File

@ -29,7 +29,7 @@ import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-t
export type DeleteDocumentOptions = {
id: number;
userId: string;
userId: number;
teamId?: number;
requestMetadata?: RequestMetadata;
};

View File

@ -7,7 +7,7 @@ import { getDocumentWhereInput } from './get-document-by-id';
export interface DuplicateDocumentOptions {
documentId: number;
userId: string;
userId: number;
teamId?: number;
}

View File

@ -7,7 +7,7 @@ import type { FindResultResponse } from '../../types/search-params';
import { parseDocumentAuditLogData } from '../../utils/document-audit-logs';
export interface FindDocumentAuditLogsOptions {
userId: string;
userId: number;
documentId: number;
page?: number;
perPage?: number;

View File

@ -27,7 +27,7 @@ import { maskRecipientTokensForDocument } from '../../utils/mask-recipient-token
export type PeriodSelectorValue = '' | '7d' | '14d' | '30d';
export type FindDocumentsOptions = {
userId: string;
userId: number;
teamId?: number;
templateId?: number;
source?: DocumentSource;

View File

@ -10,7 +10,7 @@ import { getTeamById } from '../team/get-team';
export type GetDocumentByIdOptions = {
documentId: number;
userId: string;
userId: number;
teamId?: number;
};
@ -58,7 +58,7 @@ export const getDocumentById = async ({ documentId, userId, teamId }: GetDocumen
export type GetDocumentWhereInputOptions = {
documentId: number;
userId: string;
userId: number;
teamId?: number;
/**

View File

@ -14,7 +14,7 @@ import { getDocumentWhereInput } from './get-document-by-id';
export type GetDocumentWithDetailsByIdOptions = {
documentId: number;
userId: string;
userId: number;
teamId?: number;
};

View File

@ -170,7 +170,7 @@ type GetTeamCountsOption = {
teamEmail?: string;
senderIds?: number[];
currentUserEmail: string;
userId: string;
userId: number;
createdAt: Prisma.DocumentWhereInput['createdAt'];
currentTeamMemberRole?: TeamMemberRole;
search?: string;

View File

@ -124,7 +124,7 @@ type VerifyPasskeyOptions = {
/**
* The ID of the user who initiated the request.
*/
userId: string;
userId: number;
/**
* The secondary ID of the verification token.

View File

@ -11,7 +11,7 @@ import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
export type MoveDocumentToTeamOptions = {
documentId: number;
teamId: number;
userId: string;
userId: number;
requestMetadata?: RequestMetadata;
};

View File

@ -26,7 +26,7 @@ import { getDocumentWhereInput } from './get-document-by-id';
export type ResendDocumentOptions = {
documentId: number;
userId: string;
userId: number;
recipients: number[];
teamId?: number;
requestMetadata: RequestMetadata;

View File

@ -8,7 +8,7 @@ import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
export type SearchDocumentsWithKeywordOptions = {
query: string;
userId: string;
userId: number;
limit?: number;
};

View File

@ -28,7 +28,7 @@ import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type SendDocumentOptions = {
documentId: number;
userId: string;
userId: number;
teamId?: number;
sendEmail?: boolean;
requestMetadata?: RequestMetadata;

View File

@ -18,7 +18,7 @@ import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../t
import { createDocumentAuthOptions, extractDocumentAuthMethods } from '../../utils/document-auth';
export type UpdateDocumentSettingsOptions = {
userId: string;
userId: number;
teamId?: number;
documentId: number;
data: {

View File

@ -7,7 +7,7 @@ import { prisma } from '@documenso/prisma';
export type UpdateDocumentOptions = {
documentId: number;
data: Prisma.DocumentUpdateInput;
userId: string;
userId: number;
teamId?: number;
};

View File

@ -6,7 +6,7 @@ import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-
import { prisma } from '@documenso/prisma';
export type UpdateTitleOptions = {
userId: string;
userId: number;
teamId?: number;
documentId: number;
title: string;

View File

@ -16,7 +16,7 @@ import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
export type CreateFieldOptions = {
documentId: number;
userId: string;
userId: number;
teamId?: number;
recipientId: number;
type: FieldType;

View File

@ -7,7 +7,7 @@ import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
export type DeleteFieldOptions = {
fieldId: number;
documentId: number;
userId: string;
userId: number;
teamId?: number;
requestMetadata?: RequestMetadata;
};

View File

@ -6,7 +6,7 @@ import { FieldSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
export type GetFieldByIdOptions = {
userId: string;
userId: number;
teamId?: number;
fieldId: number;
documentId?: number;

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
export interface GetFieldsForDocumentOptions {
documentId: number;
userId: string;
userId: number;
}
export type DocumentField = Awaited<ReturnType<typeof getFieldsForDocument>>[number];

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
export interface GetFieldsForTemplateOptions {
templateId: number;
userId: string;
userId: number;
}
export const getFieldsForTemplate = async ({ templateId, userId }: GetFieldsForTemplateOptions) => {

View File

@ -30,7 +30,7 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';
export interface SetFieldsForDocumentOptions {
userId: string;
userId: number;
documentId: number;
fields: FieldData[];
requestMetadata?: RequestMetadata;

View File

@ -19,7 +19,7 @@ import { FieldType } from '@documenso/prisma/client';
import { FieldSchema } from '@documenso/prisma/generated/zod';
export type SetFieldsForTemplateOptions = {
userId: string;
userId: number;
templateId: number;
fields: {
id?: number | null;

View File

@ -9,7 +9,7 @@ import { createDocumentAuditLogData, diffFieldChanges } from '../../utils/docume
export type UpdateFieldOptions = {
fieldId: number;
documentId: number;
userId: string;
userId: number;
teamId?: number;
recipientId?: number;
type?: FieldType;

View File

@ -6,7 +6,7 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
export type SetAvatarImageOptions = {
userId: string;
userId: number;
teamId?: number | null;
bytes?: string | null;
requestMetadata?: RequestMetadata;

View File

@ -14,7 +14,7 @@ type TimeConstants = typeof timeConstants & {
};
type CreateApiTokenInput = {
userId: string;
userId: number;
teamId?: number;
tokenName: string;
expiresIn: string | null;

View File

@ -3,7 +3,7 @@ import { TeamMemberRole } from '@documenso/prisma/client';
export type DeleteTokenByIdOptions = {
id: number;
userId: string;
userId: number;
teamId?: number;
};

View File

@ -3,7 +3,7 @@ import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
export type GetUserTokensOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -1,7 +1,7 @@
import { prisma } from '@documenso/prisma';
export type GetUserTokensOptions = {
userId: string;
userId: number;
};
export const getUserTokens = async ({ userId }: GetUserTokensOptions) => {

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
export type GetApiTokenByIdOptions = {
id: number;
userId: string;
userId: number;
};
export const getApiTokenById = async ({ id, userId }: GetApiTokenByIdOptions) => {

View File

@ -8,7 +8,7 @@ import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
export type DeleteRecipientOptions = {
documentId: number;
recipientId: number;
userId: string;
userId: number;
teamId?: number;
requestMetadata?: RequestMetadata;
};

View File

@ -7,7 +7,7 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
export type GetRecipientByIdOptions = {
recipientId: number;
userId: string;
userId: number;
teamId?: number;
};

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
export interface GetRecipientsForDocumentOptions {
documentId: number;
userId: string;
userId: number;
teamId?: number;
}

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
export interface GetRecipientsForTemplateOptions {
templateId: number;
userId: string;
userId: number;
}
export const getRecipientsForTemplate = async ({

View File

@ -34,7 +34,7 @@ import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
export interface SetRecipientsForDocumentOptions {
userId: string;
userId: number;
teamId?: number;
documentId: number;
recipients: RecipientData[];

View File

@ -19,7 +19,7 @@ import { nanoid } from '../../universal/id';
import { createRecipientAuthOptions } from '../../utils/document-auth';
export type SetRecipientsForTemplateOptions = {
userId: string;
userId: number;
teamId?: number;
templateId: number;
recipients: {

View File

@ -20,7 +20,7 @@ export type UpdateRecipientOptions = {
role?: RecipientRole;
signingOrder?: number | null;
actionAuth?: TRecipientActionAuthTypes | null;
userId: string;
userId: number;
teamId?: number;
requestMetadata?: RequestMetadata;
};

View File

@ -11,7 +11,7 @@ export type CreateSharingIdOptions =
}
| {
documentId: number;
userId: string;
userId: number;
};
export const createOrGetShareLink = async ({ documentId, ...options }: CreateSharingIdOptions) => {

View File

@ -3,7 +3,7 @@ import { prisma } from '@documenso/prisma';
import type { TSiteSettingSchema } from './schema';
export type UpsertSiteSettingOptions = TSiteSettingSchema & {
userId: string;
userId: number;
};
export const upsertSiteSetting = async ({

View File

@ -4,7 +4,7 @@ import { prisma } from '@documenso/prisma';
import { SubscriptionStatus } from '@documenso/prisma/client';
export type GetActiveSubscriptionsByUserIdOptions = {
userId: string;
userId: number;
};
export const getActiveSubscriptionsByUserId = async ({

View File

@ -3,7 +3,7 @@
import { prisma } from '@documenso/prisma';
export type GetSubscriptionsByUserIdOptions = {
userId: string;
userId: number;
};
export const getSubscriptionsByUserId = async ({ userId }: GetSubscriptionsByUserIdOptions) => {

View File

@ -6,7 +6,7 @@ import { TeamMemberInviteStatus } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client';
export type AcceptTeamInvitationOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -4,7 +4,7 @@ import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams
import { prisma } from '@documenso/prisma';
export type CreateTeamBillingPortalOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -5,7 +5,7 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
export type CreateTeamPendingCheckoutSession = {
userId: string;
userId: number;
pendingTeamId: number;
interval: 'monthly' | 'yearly';
};

View File

@ -19,7 +19,7 @@ import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
export type CreateTeamEmailVerificationOptions = {
userId: string;
userId: number;
teamId: number;
data: {
email: string;

View File

@ -20,7 +20,7 @@ import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
export type CreateTeamMemberInvitesOptions = {
userId: string;
userId: number;
userName: string;
teamId: number;
invitations: TCreateTeamMemberInvitesMutationSchema['invitations'];

View File

@ -16,7 +16,7 @@ export type CreateTeamOptions = {
/**
* ID of the user creating the Team.
*/
userId: string;
userId: number;
/**
* Name of the team to display.

View File

@ -1,7 +1,7 @@
import { prisma } from '@documenso/prisma';
export type DeclineTeamInvitationOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -2,7 +2,7 @@ import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams
import { prisma } from '@documenso/prisma';
export type DeleteTeamEmailVerificationOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -14,7 +14,7 @@ import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
export type DeleteTeamEmailOptions = {
userId: string;
userId: number;
userEmail: string;
teamId: number;
};

View File

@ -6,7 +6,7 @@ export type DeleteTeamMemberInvitationsOptions = {
/**
* The ID of the user who is initiating this action.
*/
userId: string;
userId: number;
/**
* The ID of the team to remove members from.

View File

@ -9,7 +9,7 @@ export type DeleteTeamMembersOptions = {
/**
* The ID of the user who is initiating this action.
*/
userId: string;
userId: number;
/**
* The ID of the team to remove members from.

View File

@ -1,7 +1,7 @@
import { prisma } from '@documenso/prisma';
export type DeleteTeamPendingOptions = {
userId: string;
userId: number;
pendingTeamId: number;
};

View File

@ -6,7 +6,7 @@ export type DeleteTeamTransferRequestOptions = {
/**
* The ID of the user deleting the transfer.
*/
userId: string;
userId: number;
/**
* The ID of the team whose team transfer request should be deleted.

View File

@ -17,7 +17,7 @@ import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
export type DeleteTeamOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -4,7 +4,7 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
export interface FindTeamInvoicesOptions {
userId: string;
userId: number;
teamId: number;
}

View File

@ -10,7 +10,7 @@ import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
export interface FindTeamMemberInvitesOptions {
userId: string;
userId: number;
teamId: number;
query?: string;
page?: number;

View File

@ -10,7 +10,7 @@ import type { FindResultResponse } from '../../types/search-params';
import { ZFindResultResponse } from '../../types/search-params';
export interface FindTeamMembersOptions {
userId: string;
userId: number;
teamId: number;
query?: string;
page?: number;

View File

@ -8,7 +8,7 @@ import { TeamPendingSchema } from '@documenso/prisma/generated/zod';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
export interface FindTeamsPendingOptions {
userId: string;
userId: number;
query?: string;
page?: number;
perPage?: number;

View File

@ -5,7 +5,7 @@ import { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';
export interface FindTeamsOptions {
userId: string;
userId: number;
query?: string;
page?: number;
perPage?: number;

View File

@ -4,7 +4,7 @@ import { prisma } from '@documenso/prisma';
import { TeamMemberSchema, UserSchema } from '@documenso/prisma/generated/zod';
export type GetTeamMembersOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -5,7 +5,7 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
import { updateTeamPublicProfile } from './update-team-public-profile';
export type GetTeamPublicProfileOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -70,7 +70,7 @@ export const getTeamById = async ({
};
export type GetTeamByUrlOptions = {
userId: string;
userId: number;
teamUrl: string;
};

View File

@ -4,7 +4,7 @@ import { prisma } from '@documenso/prisma';
import { TeamMemberSchema, TeamSchema } from '@documenso/prisma/generated/zod';
export type GetTeamsOptions = {
userId: string;
userId: number;
};
export const ZGetTeamsResponseSchema = TeamSchema.extend({

View File

@ -8,7 +8,7 @@ export type LeaveTeamOptions = {
/**
* The ID of the user who is leaving the team.
*/
userId: string;
userId: number;
/**
* The ID of the team the user is leaving.

View File

@ -16,7 +16,7 @@ export type RequestTeamOwnershipTransferOptions = {
/**
* The ID of the user initiating the transfer.
*/
userId: string;
userId: number;
/**
* The name of the user initiating the transfer.
@ -31,7 +31,7 @@ export type RequestTeamOwnershipTransferOptions = {
/**
* The user ID of the new owner.
*/
newOwneruserId: string;
newOwnerUserId: number;
/**
* Whether to clear any current payment methods attached to the team.

View File

@ -6,7 +6,7 @@ import { prisma } from '@documenso/prisma';
import { sendTeamEmailVerificationEmail } from './create-team-email-verification';
export type ResendTeamMemberInvitationOptions = {
userId: string;
userId: number;
teamId: number;
};

View File

@ -8,7 +8,7 @@ export type ResendTeamMemberInvitationOptions = {
/**
* The ID of the user who is initiating this action.
*/
userId: string;
userId: number;
/**
* The name of the user who is initiating this action.

View File

@ -5,7 +5,7 @@ import { TeamMemberRole } from '@documenso/prisma/client';
import { TeamGlobalSettingsSchema } from '@documenso/prisma/generated/zod';
export type UpdateTeamBrandingSettingsOptions = {
userId: string;
userId: number;
teamId: number;
settings: {

View File

@ -8,7 +8,7 @@ import { TeamGlobalSettingsSchema } from '@documenso/prisma/generated/zod';
import type { SupportedLanguageCodes } from '../../constants/i18n';
export type UpdateTeamDocumentSettingsOptions = {
userId: string;
userId: number;
teamId: number;
settings: {

View File

@ -3,7 +3,7 @@ import { prisma } from '@documenso/prisma';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams';
export type UpdateTeamEmailOptions = {
userId: string;
userId: number;
teamId: number;
data: {
name: string;

View File

@ -5,7 +5,7 @@ import { prisma } from '@documenso/prisma';
import type { TeamMemberRole } from '@documenso/prisma/client';
export type UpdateTeamMemberOptions = {
userId: string;
userId: number;
teamId: number;
teamMemberId: number;
data: {

View File

@ -1,7 +1,7 @@
import { prisma } from '@documenso/prisma';
export type UpdatePublicProfileOptions = {
userId: string;
userId: number;
teamId: number;
data: {
bio?: string;

View File

@ -6,7 +6,7 @@ import { prisma } from '@documenso/prisma';
import { Prisma } from '@documenso/prisma/client';
export type UpdateTeamOptions = {
userId: string;
userId: number;
teamId: number;
data: {
name?: string;

View File

@ -4,7 +4,7 @@ import { DocumentSource, type RecipientRole } from '@documenso/prisma/client';
export type CreateDocumentFromTemplateLegacyOptions = {
templateId: number;
userId: string;
userId: number;
teamId?: number;
recipients?: {
name?: string;

Some files were not shown because too many files have changed in this diff Show More