fix: refactor prisma relations (#1581)

This commit is contained in:
David Nguyen
2025-01-13 13:41:53 +11:00
committed by GitHub
parent 48b55758e3
commit 7d0a9c6439
143 changed files with 687 additions and 790 deletions

View File

@ -59,7 +59,7 @@ export default async function AdminDocumentDetailsPage({ params }: AdminDocument
<Trans>Admin Actions</Trans>
</h2>
<AdminActions className="mt-2" document={document} recipients={document.Recipient} />
<AdminActions className="mt-2" document={document} recipients={document.recipients} />
<hr className="my-4" />
<h2 className="text-lg font-semibold">
@ -68,7 +68,7 @@ export default async function AdminDocumentDetailsPage({ params }: AdminDocument
<div className="mt-4">
<Accordion type="multiple" className="space-y-4">
{document.Recipient.map((recipient) => (
{document.recipients.map((recipient) => (
<AccordionItem
key={recipient.id}
value={recipient.id.toString()}

View File

@ -39,9 +39,9 @@ type TAdminUpdateRecipientFormSchema = z.infer<typeof ZAdminUpdateRecipientFormS
export type RecipientItemProps = {
recipient: Recipient & {
Field: Array<
fields: Array<
Field & {
Signature: Signature | null;
signature: Signature | null;
}
>;
};
@ -89,13 +89,13 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
accessorKey: 'signature',
cell: ({ row }) => (
<div>
{row.original.Signature?.typedSignature && (
<span>{row.original.Signature.typedSignature}</span>
{row.original.signature?.typedSignature && (
<span>{row.original.signature.typedSignature}</span>
)}
{row.original.Signature?.signatureImageAsBase64 && (
{row.original.signature?.signatureImageAsBase64 && (
<img
src={row.original.Signature.signatureImageAsBase64}
src={row.original.signature.signatureImageAsBase64}
alt="Signature"
className="h-12 w-full dark:invert"
/>
@ -103,7 +103,7 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
</div>
),
},
] satisfies DataTableColumnDef<(typeof recipient)['Field'][number]>[];
] satisfies DataTableColumnDef<(typeof recipient)['fields'][number]>[];
}, []);
const onUpdateRecipientFormSubmit = async ({ name, email }: TAdminUpdateRecipientFormSchema) => {
@ -190,7 +190,7 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
<Trans>Fields</Trans>
</h2>
<DataTable columns={columns} data={recipient.Field} />
<DataTable columns={columns} data={recipient.fields} />
</div>
);
};

View File

@ -86,14 +86,14 @@ export const AdminDocumentResults = () => {
header: _(msg`Owner`),
accessorKey: 'owner',
cell: ({ row }) => {
const avatarFallbackText = row.original.User.name
? extractInitials(row.original.User.name)
: row.original.User.email.slice(0, 1).toUpperCase();
const avatarFallbackText = row.original.user.name
? extractInitials(row.original.user.name)
: row.original.user.email.slice(0, 1).toUpperCase();
return (
<Tooltip delayDuration={200}>
<TooltipTrigger>
<Link href={`/admin/users/${row.original.User.id}`}>
<Link href={`/admin/users/${row.original.user.id}`}>
<Avatar className="dark:border-border h-12 w-12 border-2 border-solid border-white">
<AvatarFallback className="text-xs text-gray-400">
{avatarFallbackText}
@ -110,8 +110,8 @@ export const AdminDocumentResults = () => {
</Avatar>
<div className="text-muted-foreground flex flex-col text-sm">
<span>{row.original.User.name}</span>
<span>{row.original.User.email}</span>
<span>{row.original.user.name}</span>
<span>{row.original.user.email}</span>
</div>
</TooltipContent>
</Tooltip>

View File

@ -22,8 +22,8 @@ type UserData = {
name: string | null;
email: string;
roles: Role[];
Subscription?: SubscriptionLite[] | null;
Document: DocumentLite[];
subscriptions?: SubscriptionLite[] | null;
documents: DocumentLite[];
};
type SubscriptionLite = Pick<
@ -81,7 +81,7 @@ export const UsersDataTable = ({
header: _(msg`Subscription`),
accessorKey: 'subscription',
cell: ({ row }) => {
const foundIndividualSubscription = (row.original.Subscription ?? []).find((sub) =>
const foundIndividualSubscription = (row.original.subscriptions ?? []).find((sub) =>
individualPriceIds.includes(sub.priceId),
);
@ -92,7 +92,7 @@ export const UsersDataTable = ({
header: _(msg`Documents`),
accessorKey: 'documents',
cell: ({ row }) => {
return <div>{row.original.Document.length}</div>;
return <div>{row.original.documents?.length}</div>;
},
},
{

View File

@ -18,8 +18,8 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type DocumentPageViewButtonProps = {
document: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
Recipient: Recipient[];
user: Pick<User, 'id' | 'name' | 'email'>;
recipients: Recipient[];
team: Pick<Team, 'id' | 'url'> | null;
};
team?: Pick<Team, 'id' | 'url'>;
@ -34,7 +34,7 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps
return null;
}
const recipient = document.Recipient.find((recipient) => recipient.email === session.user.email);
const recipient = document.recipients.find((recipient) => recipient.email === session.user.email);
const isRecipient = !!recipient;
const isPending = document.status === DocumentStatus.PENDING;

View File

@ -41,8 +41,8 @@ import { DuplicateDocumentDialog } from '../duplicate-document-dialog';
export type DocumentPageViewDropdownProps = {
document: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
Recipient: Recipient[];
user: Pick<User, 'id' | 'name' | 'email'>;
recipients: Recipient[];
team: Pick<Team, 'id' | 'url'> | null;
};
team?: Pick<Team, 'id' | 'url'> & { teamEmail: TeamEmail | null };
@ -60,9 +60,9 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
return null;
}
const recipient = document.Recipient.find((recipient) => recipient.email === session.user.email);
const recipient = document.recipients.find((recipient) => recipient.email === session.user.email);
const isOwner = document.User.id === session.user.id;
const isOwner = document.user.id === session.user.id;
const isDraft = document.status === DocumentStatus.DRAFT;
const isPending = document.status === DocumentStatus.PENDING;
const isDeleted = document.deletedAt !== null;
@ -94,7 +94,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
}
};
const nonSignedRecipients = document.Recipient.filter((item) => item.signingStatus !== 'SIGNED');
const nonSignedRecipients = document.recipients.filter((item) => item.signingStatus !== 'SIGNED');
return (
<DropdownMenu>
@ -149,7 +149,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
{canManageDocument && (
<DocumentRecipientLinkCopyDialog
recipients={document.Recipient}
recipients={document.recipients}
trigger={
<DropdownMenuItem
disabled={!isPending || isDeleted}

View File

@ -12,8 +12,8 @@ import type { Document, Recipient, User } from '@documenso/prisma/client';
export type DocumentPageViewInformationProps = {
userId: number;
document: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
Recipient: Recipient[];
user: Pick<User, 'id' | 'name' | 'email'>;
recipients: Recipient[];
};
};
@ -29,7 +29,8 @@ export const DocumentPageViewInformation = ({
return [
{
description: msg`Uploaded by`,
value: userId === document.userId ? _(msg`You`) : document.User.name ?? document.User.email,
value:
userId === document.userId ? _(msg`You`) : (document.user.name ?? document.user.email),
},
{
description: msg`Created`,

View File

@ -28,7 +28,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type DocumentPageViewRecipientsProps = {
document: Document & {
Recipient: Recipient[];
recipients: Recipient[];
};
documentRootPath: string;
};
@ -40,7 +40,7 @@ export const DocumentPageViewRecipients = ({
const { _ } = useLingui();
const { toast } = useToast();
const recipients = document.Recipient;
const recipients = document.recipients;
return (
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">

View File

@ -71,7 +71,7 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
const documentVisibility = document?.visibility;
const currentTeamMemberRole = team?.currentTeamMember?.role;
const isRecipient = document?.Recipient.find((recipient) => recipient.email === user.email);
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
let canAccessDocument = true;
if (team && !isRecipient && document?.userId !== user.id) {
@ -131,7 +131,7 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
const documentWithRecipients = {
...document,
Recipient: recipients,
recipients,
};
return (

View File

@ -71,7 +71,7 @@ export const EditDocumentForm = ({
},
);
const { Recipient: recipients, Field: fields } = document;
const { recipients, fields } = document;
const { mutateAsync: updateDocument } = trpc.document.setSettingsForDocument.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
@ -105,7 +105,7 @@ export const EditDocumentForm = ({
{
documentId: initialDocument.id,
},
(oldData) => ({ ...(oldData || initialDocument), Field: newFields }),
(oldData) => ({ ...(oldData || initialDocument), fields: newFields }),
);
},
});
@ -117,7 +117,7 @@ export const EditDocumentForm = ({
{
documentId: initialDocument.id,
},
(oldData) => ({ ...(oldData || initialDocument), Recipient: newRecipients }),
(oldData) => ({ ...(oldData || initialDocument), recipients: newRecipients }),
);
},
});

View File

@ -52,7 +52,7 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
const documentVisibility = document?.visibility;
const currentTeamMemberRole = team?.currentTeamMember?.role;
const isRecipient = document?.Recipient.find((recipient) => recipient.email === user.email);
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
let canAccessDocument = true;
if (!isRecipient && document?.userId !== user.id) {
@ -78,7 +78,7 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
redirect(`${documentRootPath}/${documentId}`);
}
const { documentMeta, Recipient: recipients } = document;
const { documentMeta, recipients } = document;
if (documentMeta?.password) {
const key = DOCUMENSO_ENCRYPTION_KEY;

View File

@ -77,9 +77,9 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
},
{
description: msg`Created by`,
value: document.User.name
? `${document.User.name} (${document.User.email})`
: document.User.email,
value: document.user.name
? `${document.user.name} (${document.user.email})`
: document.user.email,
},
{
description: msg`Date created`,

View File

@ -19,8 +19,8 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type DataTableActionButtonProps = {
row: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
Recipient: Recipient[];
user: Pick<User, 'id' | 'name' | 'email'>;
recipients: Recipient[];
team: Pick<Team, 'id' | 'url'> | null;
};
team?: Pick<Team, 'id' | 'url'>;
@ -35,9 +35,9 @@ export const DataTableActionButton = ({ row, team }: DataTableActionButtonProps)
return null;
}
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
const recipient = row.recipients.find((recipient) => recipient.email === session.user.email);
const isOwner = row.User.id === session.user.id;
const isOwner = row.user.id === session.user.id;
const isRecipient = !!recipient;
const isDraft = row.status === DocumentStatus.DRAFT;
const isPending = row.status === DocumentStatus.PENDING;

View File

@ -46,8 +46,8 @@ import { MoveDocumentDialog } from './move-document-dialog';
export type DataTableActionDropdownProps = {
row: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
Recipient: Recipient[];
user: Pick<User, 'id' | 'name' | 'email'>;
recipients: Recipient[];
team: Pick<Team, 'id' | 'url'> | null;
};
team?: Pick<Team, 'id' | 'url'> & { teamEmail?: string };
@ -66,9 +66,9 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
return null;
}
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
const recipient = row.recipients.find((recipient) => recipient.email === session.user.email);
const isOwner = row.User.id === session.user.id;
const isOwner = row.user.id === session.user.id;
// const isRecipient = !!recipient;
const isDraft = row.status === DocumentStatus.DRAFT;
const isPending = row.status === DocumentStatus.PENDING;
@ -109,7 +109,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
}
};
const nonSignedRecipients = row.Recipient.filter((item) => item.signingStatus !== 'SIGNED');
const nonSignedRecipients = row.recipients.filter((item) => item.signingStatus !== 'SIGNED');
return (
<DropdownMenu>
@ -194,7 +194,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
{canManageDocument && (
<DocumentRecipientLinkCopyDialog
recipients={row.Recipient}
recipients={row.recipients}
trigger={
<DropdownMenuItem disabled={!isPending} asChild onSelect={(e) => e.preventDefault()}>
<div>

View File

@ -10,9 +10,9 @@ import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
export type DataTableTitleProps = {
row: Document & {
User: Pick<User, 'id' | 'name' | 'email'>;
user: Pick<User, 'id' | 'name' | 'email'>;
team: Pick<Team, 'url'> | null;
Recipient: Recipient[];
recipients: Recipient[];
};
teamUrl?: string;
};
@ -24,9 +24,9 @@ export const DataTableTitle = ({ row, teamUrl }: DataTableTitleProps) => {
return null;
}
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
const recipient = row.recipients.find((recipient) => recipient.email === session.user.email);
const isOwner = row.User.id === session.user.id;
const isOwner = row.user.id === session.user.id;
const isRecipient = !!recipient;
const isCurrentTeamDocument = teamUrl && row.team?.url === teamUrl;

View File

@ -57,14 +57,14 @@ export const DocumentsDataTable = ({
{
id: 'sender',
header: _(msg`Sender`),
cell: ({ row }) => row.original.User.name ?? row.original.User.email,
cell: ({ row }) => row.original.user.name ?? row.original.user.email,
},
{
header: _(msg`Recipient`),
accessorKey: 'recipient',
cell: ({ row }) => (
<StackAvatarsWithTooltip
recipients={row.original.Recipient}
recipients={row.original.recipients}
documentStatus={row.original.status}
/>
),

View File

@ -69,7 +69,7 @@ export const EditTemplateForm = ({
},
);
const { Recipient: recipients, Field: fields, templateDocumentData } = template;
const { recipients, fields, templateDocumentData } = template;
const documentFlow: Record<EditTemplateStep, DocumentFlowStep> = {
settings: {

View File

@ -11,7 +11,7 @@ import { Button } from '@documenso/ui/primitives/button';
import { TemplateDirectLinkDialog } from '../template-direct-link-dialog';
export type TemplateDirectLinkDialogWrapperProps = {
template: Template & { directLink?: TemplateDirectLink | null; Recipient: Recipient[] };
template: Template & { directLink?: TemplateDirectLink | null; recipients: Recipient[] };
};
export const TemplateDirectLinkDialogWrapper = ({

View File

@ -116,7 +116,7 @@ export const TemplatePageViewDocumentsTable = ({
accessorKey: 'recipient',
cell: ({ row }) => (
<StackAvatarsWithTooltip
recipients={row.original.Recipient}
recipients={row.original.recipients}
documentStatus={row.original.status}
/>
),

View File

@ -12,7 +12,7 @@ import type { Template, User } from '@documenso/prisma/client';
export type TemplatePageViewInformationProps = {
userId: number;
template: Template & {
User: Pick<User, 'id' | 'name' | 'email'>;
user: Pick<User, 'id' | 'name' | 'email'>;
};
};
@ -28,7 +28,8 @@ export const TemplatePageViewInformation = ({
return [
{
description: msg`Uploaded by`,
value: userId === template.userId ? _(msg`You`) : template.User.name ?? template.User.email,
value:
userId === template.userId ? _(msg`You`) : (template.user.name ?? template.user.email),
},
{
description: msg`Created`,

View File

@ -123,7 +123,7 @@ export const TemplatePageViewRecentActivity = ({
{match(document.source)
.with(DocumentSource.DOCUMENT, DocumentSource.TEMPLATE, () => (
<Trans>
Document created by <span className="font-bold">{document.User.name}</span>
Document created by <span className="font-bold">{document.user.name}</span>
</Trans>
))
.with(DocumentSource.TEMPLATE_DIRECT_LINK, () => (

View File

@ -10,7 +10,7 @@ import { AvatarWithText } from '@documenso/ui/primitives/avatar';
export type TemplatePageViewRecipientsProps = {
template: Template & {
Recipient: Recipient[];
recipients: Recipient[];
};
templateRootPath: string;
};
@ -21,7 +21,7 @@ export const TemplatePageViewRecipients = ({
}: TemplatePageViewRecipientsProps) => {
const { _ } = useLingui();
const recipients = template.Recipient;
const recipients = template.recipients;
return (
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">

View File

@ -54,10 +54,10 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
redirect(templateRootPath);
}
const { templateDocumentData, Field, Recipient: recipients, templateMeta } = template;
const { templateDocumentData, fields, recipients, templateMeta } = template;
// Remap to fit the DocumentReadOnlyFields component.
const readOnlyFields = Field.map((field) => {
const readOnlyFields = fields.map((field) => {
const recipient = recipients.find((recipient) => recipient.id === field.recipientId) || {
name: '',
email: '',
@ -66,8 +66,8 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
return {
...field,
Recipient: recipient,
Signature: null,
recipient,
signature: null,
};
});
@ -165,7 +165,7 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
<UseTemplateDialog
templateId={template.id}
templateSigningOrder={template.templateMeta?.signingOrder}
recipients={template.Recipient}
recipients={template.recipients}
documentRootPath={documentRootPath}
trigger={
<Button className="w-full">

View File

@ -25,7 +25,7 @@ import { TemplateDirectLinkDialog } from './template-direct-link-dialog';
export type DataTableActionDropdownProps = {
row: Template & {
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
Recipient: Recipient[];
recipients: Recipient[];
};
templateRootPath: string;
teamId?: number;

View File

@ -146,7 +146,7 @@ export const TemplatesDataTable = ({
templateId={row.original.id}
templateSigningOrder={row.original.templateMeta?.signingOrder}
documentDistributionMethod={row.original.templateMeta?.distributionMethod}
recipients={row.original.Recipient}
recipients={row.original.recipients}
documentRootPath={documentRootPath}
/>

View File

@ -46,12 +46,10 @@ import {
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useOptionalCurrentTeam } from '~/providers/team';
type TemplateDirectLinkDialogProps = {
template: Template & {
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
Recipient: Recipient[];
recipients: Recipient[];
};
open: boolean;
onOpenChange: (_open: boolean) => void;
@ -68,8 +66,6 @@ export const TemplateDirectLinkDialog = ({
const { quota, remaining } = useLimits();
const { _ } = useLingui();
const team = useOptionalCurrentTeam();
const [, copy] = useCopyToClipboard();
const router = useRouter();
@ -81,8 +77,8 @@ export const TemplateDirectLinkDialog = ({
);
const validDirectTemplateRecipients = useMemo(
() => template.Recipient.filter((recipient) => recipient.role !== RecipientRole.CC),
[template.Recipient],
() => template.recipients.filter((recipient) => recipient.role !== RecipientRole.CC),
[template.recipients],
);
const {
@ -326,7 +322,7 @@ export const TemplateDirectLinkDialog = ({
</div>
{/* Prevent creating placeholder direct template recipient if the email already exists. */}
{!template.Recipient.some(
{!template.recipients.some(
(recipient) => recipient.email === DIRECT_TEMPLATE_RECIPIENT_EMAIL,
) && (
<DialogFooter className="mx-auto">

View File

@ -104,7 +104,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
<span className="font-medium">{_(msg`Owner`)}</span>
<span className="mt-1 block break-words">
{document.User.name} ({document.User.email})
{document.user.name} ({document.user.email})
</span>
</p>
@ -140,7 +140,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
<p className="font-medium">{_(msg`Recipients`)}</p>
<ul className="mt-1 list-inside list-disc">
{document.Recipient.map((recipient) => (
{document.recipients.map((recipient) => (
<li key={recipient.id}>
<span className="text-muted-foreground">
[{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}]

View File

@ -86,7 +86,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
});
const isOwner = (email: string) => {
return email.toLowerCase() === document.User.email.toLowerCase();
return email.toLowerCase() === document.user.email.toLowerCase();
};
const getDevice = (userAgent?: string | null) => {
@ -104,7 +104,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
};
const getAuthenticationLevel = (recipientId: number) => {
const recipient = document.Recipient.find((recipient) => recipient.id === recipientId);
const recipient = document.recipients.find((recipient) => recipient.id === recipientId);
if (!recipient) {
return 'Unknown';
@ -157,9 +157,11 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
};
const getRecipientSignatureField = (recipientId: number) => {
return document.Recipient.find((recipient) => recipient.id === recipientId)?.Field.find(
(field) => field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE,
);
return document.recipients
.find((recipient) => recipient.id === recipientId)
?.fields.find(
(field) => field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE,
);
};
return (
@ -181,7 +183,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
</TableHeader>
<TableBody className="print:text-xs">
{document.Recipient.map((recipient, i) => {
{document.recipients.map((recipient, i) => {
const logs = getRecipientAuditLogs(recipient.id);
const signature = getRecipientSignatureField(recipient.id);
@ -209,17 +211,17 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
boxShadow: `0px 0px 0px 4.88px rgba(122, 196, 85, 0.1), 0px 0px 0px 1.22px rgba(122, 196, 85, 0.6), 0px 0px 0px 0.61px rgba(122, 196, 85, 1)`,
}}
>
{signature.Signature?.signatureImageAsBase64 && (
{signature.signature?.signatureImageAsBase64 && (
<img
src={`${signature.Signature?.signatureImageAsBase64}`}
src={`${signature.signature?.signatureImageAsBase64}`}
alt="Signature"
className="max-h-12 max-w-full"
/>
)}
{signature.Signature?.typedSignature && (
{signature.signature?.typedSignature && (
<p className="font-signature text-center text-sm">
{signature.Signature?.typedSignature}
{signature.signature?.typedSignature}
</p>
)}
</div>

View File

@ -7,7 +7,8 @@ import { useSession } from 'next-auth/react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import type { Field, Recipient } from '@documenso/prisma/client';
import type { Recipient } from '@documenso/prisma/client';
import type { Field } from '@documenso/prisma/client';
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
import {
DocumentFlowFormContainerActions,
@ -40,8 +41,8 @@ export type TConfigureDirectTemplateFormSchema = z.infer<typeof ZConfigureDirect
export type ConfigureDirectTemplateFormProps = {
flowStep: DocumentFlowStep;
isDocumentPdfLoaded: boolean;
template: Omit<TemplateWithDetails, 'User'>;
directTemplateRecipient: Recipient & { Field: Field[] };
template: Omit<TemplateWithDetails, 'user'>;
directTemplateRecipient: Recipient & { fields: Field[] };
initialEmail?: string;
onSubmit: (_data: TConfigureDirectTemplateFormSchema) => void;
};
@ -57,10 +58,10 @@ export const ConfigureDirectTemplateFormPartial = ({
const { _ } = useLingui();
const { data: session } = useSession();
const { Recipient } = template;
const { recipients } = template;
const { derivedRecipientAccessAuth } = useRequiredDocumentAuthContext();
const recipientsWithBlankDirectRecipientEmail = Recipient.map((recipient) => {
const recipientsWithBlankDirectRecipientEmail = recipients.map((recipient) => {
if (recipient.id === directTemplateRecipient.id) {
return {
...recipient,
@ -74,7 +75,7 @@ export const ConfigureDirectTemplateFormPartial = ({
const form = useForm<TConfigureDirectTemplateFormSchema>({
resolver: zodResolver(
ZConfigureDirectTemplateFormSchema.superRefine((items, ctx) => {
if (template.Recipient.map((recipient) => recipient.email).includes(items.email)) {
if (template.recipients.map((recipient) => recipient.email).includes(items.email)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: _(msg`Email cannot already exist in the template`),
@ -96,7 +97,7 @@ export const ConfigureDirectTemplateFormPartial = ({
<DocumentFlowFormContainerContent>
{isDocumentPdfLoaded &&
directTemplateRecipient.Field.map((field, index) => (
directTemplateRecipient.fields.map((field, index) => (
<ShowFieldItem
key={index}
field={field}

View File

@ -28,9 +28,9 @@ import type { DirectTemplateLocalField } from './sign-direct-template';
import { SignDirectTemplateForm } from './sign-direct-template';
export type TemplatesDirectPageViewProps = {
template: Omit<TemplateWithDetails, 'User'>;
template: Omit<TemplateWithDetails, 'user'>;
directTemplateToken: string;
directTemplateRecipient: Recipient & { Field: Field[] };
directTemplateRecipient: Recipient & { fields: Field[] };
};
type DirectTemplateStep = 'configure' | 'sign';
@ -164,7 +164,7 @@ export const DirectTemplatePageView = ({
<SignDirectTemplateForm
flowStep={directTemplateFlow.sign}
directRecipient={recipient}
directRecipientFields={directTemplateRecipient.Field}
directRecipientFields={directTemplateRecipient.fields}
template={template}
onSubmit={onSignDirectTemplateSubmit}
/>

View File

@ -41,7 +41,7 @@ export default async function TemplatesDirectPage({ params }: TemplatesDirectPag
notFound();
}
const directTemplateRecipient = template.Recipient.find(
const directTemplateRecipient = template.recipients.find(
(recipient) => recipient.id === template.directLink?.directTemplateRecipientId,
);
@ -81,7 +81,7 @@ export default async function TemplatesDirectPage({ params }: TemplatesDirectPag
<div className="text-muted-foreground mb-8 mt-2.5 flex items-center gap-x-2">
<UsersIcon className="h-4 w-4" />
<p className="text-muted-foreground/80">
<Plural value={template.Recipient.length} one="# recipient" other="# recipients" />
<Plural value={template.recipients.length} one="# recipient" other="# recipients" />
</p>
</div>

View File

@ -55,13 +55,13 @@ export type SignDirectTemplateFormProps = {
flowStep: DocumentFlowStep;
directRecipient: Recipient;
directRecipientFields: Field[];
template: Omit<TemplateWithDetails, 'User'>;
template: Omit<TemplateWithDetails, 'user'>;
onSubmit: (_data: DirectTemplateLocalField[]) => Promise<void>;
};
export type DirectTemplateLocalField = Field & {
signedValue?: TSignFieldWithTokenMutationSchema;
Signature?: Signature;
signature?: Signature;
};
export const SignDirectTemplateForm = ({
@ -95,7 +95,7 @@ export const SignDirectTemplateForm = ({
};
if (field.type === FieldType.SIGNATURE) {
tempField.Signature = {
tempField.signature = {
id: 1,
created: new Date(),
recipientId: 1,
@ -127,7 +127,7 @@ export const SignDirectTemplateForm = ({
customText: '',
inserted: false,
signedValue: undefined,
Signature: undefined,
signature: undefined,
};
}),
);

View File

@ -52,15 +52,15 @@ export async function GET(_request: Request, { params: { slug } }: SharePageOpen
const isRecipient = 'Signature' in recipientOrSender;
const signatureImage = match(recipientOrSender)
.with({ Signature: P.array(P._) }, (recipient) => {
return recipient.Signature?.[0]?.signatureImageAsBase64 || null;
.with({ signatures: P.array(P._) }, (recipient) => {
return recipient.signatures?.[0]?.signatureImageAsBase64 || null;
})
.otherwise((sender) => {
return sender.signature || null;
});
const signatureName = match(recipientOrSender)
.with({ Signature: P.array(P._) }, (recipient) => {
.with({ signatures: P.array(P._) }, (recipient) => {
return recipient.name || recipient.email;
})
.otherwise((sender) => {

View File

@ -74,7 +74,7 @@ export const SignatureField = ({
isLoading: isRemoveSignedFieldWithTokenLoading,
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const { Signature: signature } = field;
const { signature } = field;
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;

View File

@ -56,8 +56,8 @@ export const SigningPageView = ({
const shouldUseTeamDetails =
document.teamId && document.team?.teamGlobalSettings?.includeSenderDetails === false;
let senderName = document.User.name ?? '';
let senderEmail = `(${document.User.email})`;
let senderName = document.user.name ?? '';
let senderEmail = `(${document.user.email})`;
if (shouldUseTeamDetails) {
senderName = document.team?.name ?? '';

View File

@ -118,7 +118,7 @@ export const EmbedDirectTemplateClientPage = ({
});
if (field.type === FieldType.SIGNATURE) {
newField.Signature = {
newField.signature = {
id: 1,
created: new Date(),
recipientId: 1,
@ -163,7 +163,7 @@ export const EmbedDirectTemplateClientPage = ({
customText: '',
inserted: false,
signedValue: undefined,
Signature: undefined,
signature: undefined,
});
}),
);

View File

@ -73,7 +73,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
const { directTemplateRecipientId } = template.directLink;
const recipient = template.Recipient.find(
const recipient = template.recipients.find(
(recipient) => recipient.id === directTemplateRecipientId,
);
@ -81,7 +81,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
return notFound();
}
const fields = template.Field.filter((field) => field.recipientId === directTemplateRecipientId);
const fields = template.fields.filter((field) => field.recipientId === directTemplateRecipientId);
const team = template.teamId
? await getTeamById({ teamId: template.teamId, userId: template.userId }).catch(() => null)

View File

@ -61,7 +61,7 @@ export const DocumentReadOnlyFields = ({
trigger={
<Avatar className="dark:border-foreground h-8 w-8 border-2 border-solid border-gray-200/50 transition-colors hover:border-gray-200">
<AvatarFallback className="bg-neutral-50 text-xs text-gray-400">
{extractInitials(field.Recipient.name || field.Recipient.email)}
{extractInitials(field.recipient.name || field.recipient.email)}
</AvatarFallback>
</Avatar>
}
@ -73,12 +73,12 @@ export const DocumentReadOnlyFields = ({
<Badge
className="mx-auto mb-1 py-0.5"
variant={
field.Recipient.signingStatus === SigningStatus.SIGNED
field.recipient.signingStatus === SigningStatus.SIGNED
? 'default'
: 'secondary'
}
>
{field.Recipient.signingStatus === SigningStatus.SIGNED ? (
{field.recipient.signingStatus === SigningStatus.SIGNED ? (
<>
<SignatureIcon className="mr-1 h-3 w-3" />
<Trans>Signed</Trans>
@ -97,9 +97,9 @@ export const DocumentReadOnlyFields = ({
</p>
<p className="text-muted-foreground mt-1 text-center text-xs">
{field.Recipient.name
? `${field.Recipient.name} (${field.Recipient.email})`
: field.Recipient.email}{' '}
{field.recipient.name
? `${field.recipient.name} (${field.recipient.email})`
: field.recipient.email}{' '}
</p>
<button
@ -113,18 +113,18 @@ export const DocumentReadOnlyFields = ({
</div>
<div className="text-muted-foreground dark:text-background/70 break-all text-sm">
{field.Recipient.signingStatus === SigningStatus.SIGNED &&
{field.recipient.signingStatus === SigningStatus.SIGNED &&
match(field)
.with({ type: FieldType.SIGNATURE }, (field) =>
field.Signature?.signatureImageAsBase64 ? (
field.signature?.signatureImageAsBase64 ? (
<img
src={field.Signature.signatureImageAsBase64}
src={field.signature.signatureImageAsBase64}
alt="Signature"
className="h-full w-full object-contain dark:invert"
/>
) : (
<p className="font-signature text-muted-foreground text-lg duration-200 sm:text-xl md:text-2xl">
{field.Signature?.typedSignature}
{field.signature?.typedSignature}
</p>
),
)
@ -153,7 +153,7 @@ export const DocumentReadOnlyFields = ({
.with({ type: FieldType.FREE_SIGNATURE }, () => null)
.exhaustive()}
{field.Recipient.signingStatus === SigningStatus.NOT_SIGNED && (
{field.recipient.signingStatus === SigningStatus.NOT_SIGNED && (
<p
className={cn('text-muted-foreground text-lg duration-200', {
'font-signature sm:text-xl md:text-2xl':

View File

@ -1,6 +1,5 @@
import { createNextRoute } from '@ts-rest/next';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
@ -63,7 +62,6 @@ import {
import { ApiContractV1 } from './contract';
import { authenticatedMiddleware } from './middleware/authenticated';
import { ZTemplateWithDataSchema } from './schema';
export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
getDocuments: authenticatedMiddleware(async (args, user, team) => {
@ -419,11 +417,16 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
teamId: team?.id,
});
const parsed = ZTemplateWithDataSchema.parse(template);
return {
status: 200,
body: parsed,
body: {
...template,
Field: template.fields.map((field) => ({
...field,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : null,
})),
Recipient: template.recipients,
},
};
} catch (err) {
return AppError.toRestAPIError(err);
@ -442,12 +445,17 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
teamId: team?.id,
});
const parsed = z.array(ZTemplateWithDataSchema).parse(templates);
return {
status: 200,
body: {
templates: parsed,
templates: templates.map((template) => ({
...template,
Field: template.fields.map((field) => ({
...field,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : null,
})),
Recipient: template.recipients,
})),
totalPages,
},
};
@ -540,7 +548,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
status: 200,
body: {
documentId: document.id,
recipients: document.Recipient.map((recipient) => ({
recipients: document.recipients.map((recipient) => ({
recipientId: recipient.id,
name: recipient.name,
email: recipient.email,
@ -634,7 +642,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
status: 200,
body: {
documentId: document.id,
recipients: document.Recipient.map((recipient) => ({
recipients: document.recipients.map((recipient) => ({
recipientId: recipient.id,
name: recipient.name,
email: recipient.email,
@ -693,7 +701,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
});
}
const { Recipient: recipients, ...sentDocument } = await sendDocument({
const { recipients, ...sentDocument } = await sendDocument({
documentId: document.id,
userId: user.id,
teamId: team?.id,
@ -1074,7 +1082,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
fieldMeta: result.data,
},
include: {
Recipient: true,
recipient: true,
},
});
@ -1089,7 +1097,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
},
data: {
fieldId: field.secondaryId,
fieldRecipientEmail: field.Recipient?.email ?? '',
fieldRecipientEmail: field.recipient?.email ?? '',
fieldRecipientId: recipientId,
fieldType: field.type,
},
@ -1108,7 +1116,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
pageWidth: Number(field.width),
pageHeight: Number(field.height),
customText: field.customText,
fieldMeta: ZFieldMetaSchema.parse(field.fieldMeta),
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
inserted: field.inserted,
};
}),

View File

@ -28,7 +28,7 @@ test('[DOCUMENT_AUTH]: should allow signing when no auth setup', async ({ page }
// Check that both are granted access.
for (const recipient of recipients) {
const { token, Field } = recipient;
const { token, fields } = recipient;
const signUrl = `/sign/${token}`;
@ -45,7 +45,7 @@ test('[DOCUMENT_AUTH]: should allow signing when no auth setup', async ({ page }
await page.mouse.up();
}
for (const field of Field) {
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {
@ -80,7 +80,7 @@ test('[DOCUMENT_AUTH]: should allow signing with valid global auth', async ({ pa
const recipient = recipients[0];
const { token, Field } = recipient;
const { token, fields } = recipient;
const signUrl = `/sign/${token}`;
@ -102,7 +102,7 @@ test('[DOCUMENT_AUTH]: should allow signing with valid global auth', async ({ pa
await page.mouse.up();
}
for (const field of Field) {
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {
@ -170,12 +170,12 @@ test('[DOCUMENT_AUTH]: should deny signing fields when required for global auth'
// Check that both are denied access.
for (const recipient of recipients) {
const { token, Field } = recipient;
const { token, fields } = recipient;
await page.goto(`/sign/${token}`);
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
for (const field of Field) {
for (const field of fields) {
if (field.type !== FieldType.SIGNATURE) {
continue;
}
@ -229,7 +229,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
});
for (const recipient of recipients) {
const { token, Field } = recipient;
const { token, fields } = recipient;
const { actionAuth } = ZRecipientAuthOptionsSchema.parse(recipient.authOptions);
// This document has no global action auth, so only account should require auth.
@ -241,7 +241,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
if (isAuthRequired) {
for (const field of Field) {
for (const field of fields) {
if (field.type !== FieldType.SIGNATURE) {
continue;
}
@ -271,7 +271,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
await page.mouse.up();
}
for (const field of Field) {
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {
@ -340,7 +340,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
});
for (const recipient of recipients) {
const { token, Field } = recipient;
const { token, fields } = recipient;
const { actionAuth } = ZRecipientAuthOptionsSchema.parse(recipient.authOptions);
// This document HAS global action auth, so account and inherit should require auth.
@ -352,7 +352,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
if (isAuthRequired) {
for (const field of Field) {
for (const field of fields) {
if (field.type !== FieldType.SIGNATURE) {
continue;
}
@ -382,7 +382,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
await page.mouse.up();
}
for (const field of Field) {
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {

View File

@ -24,7 +24,7 @@ import { apiSignin } from '../fixtures/authentication';
const getDocumentByToken = async (token: string) => {
return await prisma.document.findFirstOrThrow({
where: {
Recipient: {
recipients: {
some: {
token,
},
@ -357,7 +357,7 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) =
});
for (const recipient of recipients) {
const { token, Field, role } = recipient;
const { token, fields, role } = recipient;
const signUrl = `/sign/${token}`;
@ -378,7 +378,7 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) =
await page.mouse.up();
}
for (const field of Field) {
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
@ -479,8 +479,8 @@ test('[DOCUMENT_FLOW]: should be able to sign a document with custom date', asyn
fields: [FieldType.DATE],
});
const { token, Field } = recipients[0];
const [recipientField] = Field;
const { token, fields } = recipients[0];
const [recipientField] = fields;
await page.goto(`/sign/${token}`);
await page.waitForURL(`/sign/${token}`);
@ -496,7 +496,7 @@ test('[DOCUMENT_FLOW]: should be able to sign a document with custom date', asyn
const field = await prisma.field.findFirst({
where: {
Recipient: {
recipient: {
email: 'user1@example.com',
},
documentId: Number(document.id),
@ -580,14 +580,14 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
const createdDocument = await prisma.document.findFirst({
where: { title: documentTitle },
include: { Recipient: true },
include: { recipients: true },
});
expect(createdDocument).not.toBeNull();
expect(createdDocument?.Recipient.length).toBe(3);
expect(createdDocument?.recipients.length).toBe(3);
for (let i = 0; i < 3; i++) {
const recipient = createdDocument?.Recipient.find(
const recipient = createdDocument?.recipients.find(
(r) => r.email === `user${i + 1}@example.com`,
);
expect(recipient).not.toBeNull();

View File

@ -129,7 +129,7 @@ test('[DOCUMENTS]: deleting a pending document should remove it from recipients'
// signout
await apiSignout({ page });
for (const recipient of pendingDocument.Recipient) {
for (const recipient of pendingDocument.recipients) {
await apiSignin({
page,
email: recipient.email,

View File

@ -45,7 +45,7 @@ test.describe('Signing Certificate Tests', () => {
await page.mouse.up();
}
for (const field of recipient.Field) {
for (const field of recipient.fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
@ -122,7 +122,7 @@ test.describe('Signing Certificate Tests', () => {
await page.mouse.up();
}
for (const field of recipient.Field) {
for (const field of recipient.fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
@ -199,7 +199,7 @@ test.describe('Signing Certificate Tests', () => {
await page.mouse.up();
}
for (const field of recipient.Field) {
for (const field of recipient.fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');

View File

@ -124,7 +124,7 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
id: documentId,
},
include: {
Recipient: true,
recipients: true,
documentMeta: true,
},
});
@ -144,8 +144,8 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
expect(document.documentMeta?.subject).toEqual('SUBJECT');
expect(document.documentMeta?.timezone).toEqual('Etc/UTC');
const recipientOne = document.Recipient[0];
const recipientTwo = document.Recipient[1];
const recipientOne = document.recipients[0];
const recipientTwo = document.recipients[1];
const recipientOneAuth = extractDocumentAuthMethods({
documentAuth: document.authOptions,
@ -259,7 +259,7 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
id: documentId,
},
include: {
Recipient: true,
recipients: true,
documentMeta: true,
},
});
@ -281,8 +281,8 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
expect(document.documentMeta?.subject).toEqual('SUBJECT');
expect(document.documentMeta?.timezone).toEqual('Etc/UTC');
const recipientOne = document.Recipient[0];
const recipientTwo = document.Recipient[1];
const recipientOne = document.recipients[0];
const recipientTwo = document.recipients[1];
const recipientOneAuth = extractDocumentAuthMethods({
documentAuth: document.authOptions,

View File

@ -260,7 +260,7 @@ test('[DIRECT_TEMPLATES]: use direct template link with 2 recipients', async ({
const secondRecipient = await seedUser();
const createTemplateOptions = {
Recipient: {
recipients: {
createMany: {
data: [
{

View File

@ -43,7 +43,7 @@ const handleUserLimits = async ({ email }: HandleUserLimitsOptions) => {
email,
},
include: {
Subscription: true,
subscriptions: true,
},
});
@ -54,7 +54,7 @@ const handleUserLimits = async ({ email }: HandleUserLimitsOptions) => {
let quota = structuredClone(FREE_PLAN_LIMITS);
let remaining = structuredClone(FREE_PLAN_LIMITS);
const activeSubscriptions = user.Subscription.filter(
const activeSubscriptions = user.subscriptions.filter(
({ status }) => status === SubscriptionStatus.ACTIVE,
);

View File

@ -14,7 +14,7 @@ type TransferStripeSubscriptionOptions = {
/**
* The user to transfer the subscription to.
*/
user: User & { Subscription: Subscription[] };
user: User & { subscriptions: Subscription[] };
/**
* The team the subscription is associated with.
@ -54,7 +54,7 @@ export const transferTeamSubscription = async ({
]);
const teamSubscriptionRequired = !subscriptionsContainsActivePlan(
user.Subscription,
user.subscriptions,
teamRelatedPlanPriceIds,
);

View File

@ -10,7 +10,6 @@ import { createTeamFromPendingTeam } from '@documenso/lib/server-only/team/creat
import { getFlag } from '@documenso/lib/universal/get-feature-flag';
import { prisma } from '@documenso/prisma';
import { onEarlyAdoptersCheckout } from './on-early-adopters-checkout';
import { onSubscriptionDeleted } from './on-subscription-deleted';
import { onSubscriptionUpdated } from './on-subscription-updated';
@ -56,10 +55,6 @@ export const stripeWebhookHandler = async (
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const session = event.data.object as Stripe.Checkout.Session;
if (session.metadata?.source === 'marketing') {
await onEarlyAdoptersCheckout({ session });
}
const customerId =
typeof session.customer === 'string' ? session.customer : session.customer?.id;

View File

@ -1,140 +0,0 @@
import type Stripe from 'stripe';
import { hashSync } from '@documenso/lib/server-only/auth/hash';
import { sealDocument } from '@documenso/lib/server-only/document/seal-document';
import { redis } from '@documenso/lib/server-only/redis';
import { stripe } from '@documenso/lib/server-only/stripe';
import { alphaid, nanoid } from '@documenso/lib/universal/id';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { prisma } from '@documenso/prisma';
import {
DocumentSource,
DocumentStatus,
FieldType,
ReadStatus,
SendStatus,
SigningStatus,
} from '@documenso/prisma/client';
import { ZEarlyAdopterCheckoutMetadataSchema } from './early-adopter-checkout-metadata';
export type OnEarlyAdoptersCheckoutOptions = {
session: Stripe.Checkout.Session;
};
export const onEarlyAdoptersCheckout = async ({ session }: OnEarlyAdoptersCheckoutOptions) => {
try {
const safeMetadata = ZEarlyAdopterCheckoutMetadataSchema.safeParse(session.metadata);
if (!safeMetadata.success) {
return;
}
const { email, name, signatureText, signatureDataUrl: signatureDataUrlRef } = safeMetadata.data;
const user = await prisma.user.findFirst({
where: {
email: email.toLowerCase(),
},
});
if (user) {
return;
}
const tempPassword = nanoid(12);
const newUser = await prisma.user.create({
data: {
name,
email: email.toLowerCase(),
password: hashSync(tempPassword),
signature: signatureDataUrlRef,
},
});
const customerId =
typeof session.customer === 'string' ? session.customer : session.customer?.id;
if (customerId) {
await stripe.customers.update(customerId, {
metadata: {
userId: newUser.id,
},
});
}
await redis.set(`user:${newUser.id}:temp-password`, tempPassword, {
// expire in 1 week
ex: 60 * 60 * 24 * 7,
});
const signatureDataUrl = await redis.get<string>(`signature:${session.client_reference_id}`);
const documentBuffer = await fetch(
new URL('@documenso/assets/documenso-supporter-pledge.pdf', import.meta.url),
).then(async (res) => res.arrayBuffer());
const { id: documentDataId } = await putPdfFile({
name: 'Documenso Supporter Pledge.pdf',
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(documentBuffer),
});
const document = await prisma.document.create({
data: {
title: 'Documenso Supporter Pledge.pdf',
status: DocumentStatus.COMPLETED,
userId: newUser.id,
documentDataId,
source: DocumentSource.DOCUMENT,
},
});
const recipient = await prisma.recipient.create({
data: {
name,
email: email.toLowerCase(),
token: alphaid(),
readStatus: ReadStatus.OPENED,
sendStatus: SendStatus.SENT,
signingStatus: SigningStatus.SIGNED,
signedAt: new Date(),
documentId: document.id,
},
});
await prisma.field.create({
data: {
type: FieldType.SIGNATURE,
recipientId: recipient.id,
documentId: document.id,
page: 1,
positionX: 12.2781,
positionY: 81.5789,
height: 6.8649,
width: 29.5857,
inserted: true,
customText: '',
Signature: {
create: {
typedSignature: signatureDataUrl ? null : signatureText || name,
signatureImageAsBase64: signatureDataUrl,
recipientId: recipient.id,
},
},
},
});
await sealDocument({
documentId: document.id,
sendEmail: false,
});
} catch (error) {
// We don't want to break the checkout process if something goes wrong here.
// This is an additive experience for early adopters, breaking their ability
// join would be far worse than not having a signed pledge.
console.error('early-supporter-error', error);
}
};

View File

@ -35,12 +35,12 @@ export const isUserEnterprise = async ({
select: {
owner: {
include: {
Subscription: true,
subscriptions: true,
},
},
},
})
.then((team) => team.owner.Subscription);
.then((team) => team.owner.subscriptions);
} else {
subscriptions = await prisma.user
.findFirstOrThrow({
@ -48,10 +48,10 @@ export const isUserEnterprise = async ({
id: userId,
},
select: {
Subscription: true,
subscriptions: true,
},
})
.then((user) => user.Subscription);
.then((user) => user.subscriptions);
}
if (subscriptions.length === 0) {

View File

@ -32,12 +32,12 @@ export const isDocumentPlatform = async ({
select: {
owner: {
include: {
Subscription: true,
subscriptions: true,
},
},
},
})
.then((team) => team.owner.Subscription);
.then((team) => team.owner.subscriptions);
} else {
subscriptions = await prisma.user
.findFirstOrThrow({
@ -45,10 +45,10 @@ export const isDocumentPlatform = async ({
id: userId,
},
select: {
Subscription: true,
subscriptions: true,
},
})
.then((user) => user.Subscription);
.then((user) => user.subscriptions);
}
if (subscriptions.length === 0) {

View File

@ -36,19 +36,19 @@ export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
const document = await prisma.document.findFirst({
where: {
id: documentId,
Recipient: {
recipients: {
some: {
id: recipientId,
},
},
},
include: {
Recipient: {
recipients: {
where: {
id: recipientId,
},
},
User: true,
user: true,
documentMeta: true,
team: {
include: {
@ -62,7 +62,7 @@ export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
throw new Error('Document not found');
}
if (document.Recipient.length === 0) {
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
@ -74,9 +74,9 @@ export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
return;
}
const [recipient] = document.Recipient;
const [recipient] = document.recipients;
const { email: recipientEmail, name: recipientName } = recipient;
const { User: owner } = document;
const { user: owner } = document;
const recipientReference = recipientName || recipientEmail;

View File

@ -33,7 +33,7 @@ export const run = async ({
id: documentId,
},
include: {
User: true,
user: true,
documentMeta: true,
team: {
select: {
@ -53,7 +53,7 @@ export const run = async ({
}),
]);
const { documentMeta, team, User: documentOwner } = document;
const { documentMeta, user: documentOwner } = document;
const isEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
@ -70,7 +70,7 @@ export const run = async ({
const recipientTemplate = createElement(DocumentRejectionConfirmedEmail, {
recipientName: recipient.name,
documentName: document.title,
documentOwnerName: document.User.name || document.User.email,
documentOwnerName: document.user.name || document.user.email,
reason: recipient.rejectionReason || '',
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
});

View File

@ -20,7 +20,10 @@ import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
import { ZWebhookDocumentSchema } from '../../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../../types/webhook-payload';
import { getFile } from '../../../universal/upload/get-file';
import { putPdfFile } from '../../../universal/upload/put-file';
import { fieldsContainUnsignedRequiredField } from '../../../utils/advanced-fields-helpers';
@ -40,7 +43,7 @@ export const run = async ({
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
Recipient: {
recipients: {
every: {
signingStatus: SigningStatus.SIGNED,
},
@ -48,7 +51,7 @@ export const run = async ({
},
include: {
documentMeta: true,
Recipient: true,
recipients: true,
team: {
select: {
teamGlobalSettings: {
@ -102,7 +105,7 @@ export const run = async ({
documentId: document.id,
},
include: {
Signature: true,
signature: true,
},
});
@ -243,13 +246,13 @@ export const run = async ({
include: {
documentData: true,
documentMeta: true,
Recipient: true,
recipients: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: ZWebhookDocumentSchema.parse(updatedDocument),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined,
});

View File

@ -248,7 +248,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
credentialId: Buffer.from(requestBodyCrediential.id, 'base64'),
},
include: {
User: {
user: {
select: {
id: true,
email: true,
@ -263,7 +263,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
throw new AppError(AppErrorCode.NOT_SETUP);
}
const user = passkey.User;
const user = passkey.user;
const { rpId, origin } = getAuthenticatorOptions();

View File

@ -30,14 +30,14 @@ export const findDocuments = async ({ query, page = 1, perPage = 10 }: FindDocum
createdAt: 'desc',
},
include: {
User: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
Recipient: true,
recipients: true,
},
}),
prisma.document.count({

View File

@ -11,18 +11,18 @@ export const getEntireDocument = async ({ id }: GetEntireDocumentOptions) => {
},
include: {
documentMeta: true,
User: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
Recipient: {
recipients: {
include: {
Field: {
fields: {
include: {
Signature: true,
signature: true,
},
},
},

View File

@ -28,7 +28,7 @@ export async function getSigningVolume({
status: 'ACTIVE',
OR: [
{
User: {
user: {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
@ -47,11 +47,11 @@ export async function getSigningVolume({
prisma.subscription.findMany({
where: whereClause,
include: {
User: {
user: {
select: {
name: true,
email: true,
Document: {
documents: {
where: {
status: DocumentStatus.COMPLETED,
deletedAt: null,
@ -63,7 +63,7 @@ export async function getSigningVolume({
team: {
select: {
name: true,
document: {
documents: {
where: {
status: DocumentStatus.COMPLETED,
deletedAt: null,
@ -74,7 +74,7 @@ export async function getSigningVolume({
},
orderBy:
sortBy === 'name'
? [{ User: { name: sortOrder } }, { team: { name: sortOrder } }, { createdAt: 'desc' }]
? [{ user: { name: sortOrder } }, { team: { name: sortOrder } }, { createdAt: 'desc' }]
: sortBy === 'createdAt'
? [{ createdAt: sortOrder }]
: undefined,
@ -88,9 +88,9 @@ export async function getSigningVolume({
const leaderboardWithVolume: SigningVolume[] = subscriptions.map((subscription) => {
const name =
subscription.User?.name || subscription.team?.name || subscription.User?.email || 'Unknown';
const userSignedDocs = subscription.User?.Document?.length || 0;
const teamSignedDocs = subscription.team?.document?.length || 0;
subscription.user?.name || subscription.team?.name || subscription.user?.email || 'Unknown';
const userSignedDocs = subscription.user?.documents?.length || 0;
const teamSignedDocs = subscription.team?.documents?.length || 0;
return {
id: subscription.id,
name,

View File

@ -10,7 +10,7 @@ export const getUsersCount = async () => {
export const getUsersWithSubscriptionsCount = async () => {
return await prisma.user.count({
where: {
Subscription: {
subscriptions: {
some: {
status: SubscriptionStatus.ACTIVE,
},
@ -22,7 +22,7 @@ export const getUsersWithSubscriptionsCount = async () => {
export const getUserWithAtLeastOneDocumentPerMonth = async () => {
return await prisma.user.count({
where: {
Document: {
documents: {
some: {
createdAt: {
gte: DateTime.now().minus({ months: 1 }).toJSDate(),
@ -36,7 +36,7 @@ export const getUserWithAtLeastOneDocumentPerMonth = async () => {
export const getUserWithAtLeastOneDocumentSignedPerMonth = async () => {
return await prisma.user.count({
where: {
Document: {
documents: {
some: {
status: {
equals: DocumentStatus.COMPLETED,

View File

@ -23,7 +23,7 @@ export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailPro
id: userId,
},
include: {
VerificationToken: {
verificationTokens: {
orderBy: {
createdAt: 'desc',
},
@ -32,7 +32,7 @@ export const sendConfirmationEmail = async ({ userId }: SendConfirmationEmailPro
},
});
const [verificationToken] = user.VerificationToken;
const [verificationToken] = user.verificationTokens;
if (!verificationToken?.token) {
throw new Error('Verification token not found for the user');

View File

@ -20,7 +20,7 @@ export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions)
id: userId,
},
include: {
PasswordResetToken: {
passwordResetTokens: {
orderBy: {
createdAt: 'desc',
},
@ -33,7 +33,7 @@ export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions)
throw new Error('User not found');
}
const token = user.PasswordResetToken[0].token;
const token = user.passwordResetTokens[0].token;
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const resetPasswordLink = `${NEXT_PUBLIC_WEBAPP_URL()}/reset-password/${token}`;

View File

@ -14,7 +14,10 @@ import {
import { jobs } from '../../jobs/client';
import type { TRecipientActionAuth } from '../../types/document-auth';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { sendPendingEmail } from './send-pending-email';
@ -31,7 +34,7 @@ const getDocument = async ({ token, documentId }: CompleteDocumentWithTokenOptio
return await prisma.document.findFirstOrThrow({
where: {
id: documentId,
Recipient: {
recipients: {
some: {
token,
},
@ -39,7 +42,7 @@ const getDocument = async ({ token, documentId }: CompleteDocumentWithTokenOptio
},
include: {
documentMeta: true,
Recipient: {
recipients: {
where: {
token,
},
@ -59,11 +62,11 @@ export const completeDocumentWithToken = async ({
throw new Error(`Document ${document.id} must be pending`);
}
if (document.Recipient.length === 0) {
if (document.recipients.length === 0) {
throw new Error(`Document ${document.id} has no recipient with token ${token}`);
}
const [recipient] = document.Recipient;
const [recipient] = document.recipients;
if (recipient.signingStatus === SigningStatus.SIGNED) {
throw new Error(`Recipient ${recipient.id} has already signed`);
@ -195,7 +198,7 @@ export const completeDocumentWithToken = async ({
const haveAllRecipientsSigned = await prisma.document.findFirst({
where: {
id: document.id,
Recipient: {
recipients: {
every: {
OR: [{ signingStatus: SigningStatus.SIGNED }, { role: RecipientRole.CC }],
},
@ -219,13 +222,13 @@ export const completeDocumentWithToken = async ({
},
include: {
documentMeta: true,
Recipient: true,
recipients: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SIGNED,
data: ZWebhookDocumentSchema.parse(updatedDocument),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined,
});

View File

@ -13,7 +13,10 @@ import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import { DocumentSchema } from '@documenso/prisma/generated/zod';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import { getFile } from '../../universal/upload/get-file';
import { putPdfFile } from '../../universal/upload/put-file';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@ -178,7 +181,7 @@ export const createDocument = async ({
},
include: {
documentMeta: true,
Recipient: true,
recipients: true,
},
});
@ -188,7 +191,7 @@ export const createDocument = async ({
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: ZWebhookDocumentSchema.parse(createdDocument),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(createdDocument)),
userId,
teamId,
});

View File

@ -58,7 +58,7 @@ export const deleteDocument = async ({
id,
},
include: {
Recipient: true,
recipients: true,
documentMeta: true,
team: {
include: {
@ -77,7 +77,7 @@ export const deleteDocument = async ({
const isUserOwner = document.userId === userId;
const isUserTeamMember = document.team?.members.some((member) => member.userId === userId);
const userRecipient = document.Recipient.find((recipient) => recipient.email === user.email);
const userRecipient = document.recipients.find((recipient) => recipient.email === user.email);
if (!isUserOwner && !isUserTeamMember && !userRecipient) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
@ -128,7 +128,7 @@ export const deleteDocument = async ({
type HandleDocumentOwnerDeleteOptions = {
document: Document & {
Recipient: Recipient[];
recipients: Recipient[];
documentMeta: DocumentMeta | null;
};
team?:
@ -210,7 +210,7 @@ const handleDocumentOwnerDelete = async ({
// Send cancellation emails to recipients.
await Promise.all(
document.Recipient.map(async (recipient) => {
document.recipients.map(async (recipient) => {
if (recipient.sendStatus !== SendStatus.SENT) {
return;
}

View File

@ -56,7 +56,7 @@ export const duplicateDocument = async ({
const createDocumentArguments: Prisma.DocumentCreateArgs = {
data: {
title: document.title,
User: {
user: {
connect: {
id: document.userId,
},

View File

@ -45,12 +45,12 @@ export type FindDocumentsOptions = {
export const ZFindDocumentsResponseSchema = ZFindResultResponse.extend({
data: DocumentSchema.extend({
User: UserSchema.pick({
user: UserSchema.pick({
id: true,
name: true,
email: true,
}),
Recipient: RecipientSchema.array(),
recipients: RecipientSchema.array(),
team: TeamSchema.pick({
id: true,
url: true,
@ -112,8 +112,8 @@ export const findDocuments = async ({
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: query, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: query, mode: 'insensitive' } } } },
{ recipients: { some: { name: { contains: query, mode: 'insensitive' } } } },
{ recipients: { some: { email: { contains: query, mode: 'insensitive' } } } },
],
};
@ -137,7 +137,7 @@ export const findDocuments = async ({
{
OR: [
{
Recipient: {
recipients: {
some: {
email: user.email,
},
@ -174,7 +174,7 @@ export const findDocuments = async ({
deletedAt: null,
},
{
Recipient: {
recipients: {
some: {
email: user.email,
documentDeletedAt: null,
@ -195,13 +195,13 @@ export const findDocuments = async ({
deletedAt: null,
},
{
User: {
user: {
email: team.teamEmail.email,
},
deletedAt: null,
},
{
Recipient: {
recipients: {
some: {
email: team.teamEmail.email,
documentDeletedAt: null,
@ -266,14 +266,14 @@ export const findDocuments = async ({
[orderByColumn]: orderByDirection,
},
include: {
User: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
Recipient: true,
recipients: true,
team: {
select: {
id: true,
@ -313,7 +313,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
},
{
status: ExtendedDocumentStatus.COMPLETED,
Recipient: {
recipients: {
some: {
email: user.email,
},
@ -321,7 +321,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
},
{
status: ExtendedDocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: user.email,
},
@ -333,7 +333,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
status: {
not: ExtendedDocumentStatus.DRAFT,
},
Recipient: {
recipients: {
some: {
email: user.email,
signingStatus: SigningStatus.NOT_SIGNED,
@ -357,7 +357,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
},
{
status: ExtendedDocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: user.email,
signingStatus: SigningStatus.SIGNED,
@ -378,7 +378,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
},
{
status: ExtendedDocumentStatus.COMPLETED,
Recipient: {
recipients: {
some: {
email: user.email,
},
@ -443,7 +443,7 @@ const findTeamDocumentsFilter = (
status: {
not: ExtendedDocumentStatus.DRAFT,
},
Recipient: {
recipients: {
some: {
email: teamEmail,
},
@ -453,7 +453,7 @@ const findTeamDocumentsFilter = (
// Filter to display all documents that have been sent by the team email.
filter.OR.push({
User: {
user: {
email: teamEmail,
},
OR: visibilityFilters,
@ -472,7 +472,7 @@ const findTeamDocumentsFilter = (
status: {
not: ExtendedDocumentStatus.DRAFT,
},
Recipient: {
recipients: {
some: {
email: teamEmail,
signingStatus: SigningStatus.NOT_SIGNED,
@ -498,7 +498,7 @@ const findTeamDocumentsFilter = (
if (teamEmail && filter.OR) {
filter.OR.push({
status: ExtendedDocumentStatus.DRAFT,
User: {
user: {
email: teamEmail,
},
OR: visibilityFilters,
@ -523,7 +523,7 @@ const findTeamDocumentsFilter = (
status: ExtendedDocumentStatus.PENDING,
OR: [
{
Recipient: {
recipients: {
some: {
email: teamEmail,
signingStatus: SigningStatus.SIGNED,
@ -535,7 +535,7 @@ const findTeamDocumentsFilter = (
OR: visibilityFilters,
},
{
User: {
user: {
email: teamEmail,
},
OR: visibilityFilters,
@ -560,7 +560,7 @@ const findTeamDocumentsFilter = (
if (teamEmail && filter.OR) {
filter.OR.push(
{
Recipient: {
recipients: {
some: {
email: teamEmail,
},
@ -568,7 +568,7 @@ const findTeamDocumentsFilter = (
OR: visibilityFilters,
},
{
User: {
user: {
email: teamEmail,
},
OR: visibilityFilters,

View File

@ -26,14 +26,14 @@ export const getDocumentById = async ({ documentId, userId, teamId }: GetDocumen
include: {
documentData: true,
documentMeta: true,
User: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
Recipient: {
recipients: {
select: {
email: true,
},
@ -119,14 +119,14 @@ export const getDocumentWhereInput = async ({
if (team.teamEmail) {
documentWhereInput.OR.push(
{
Recipient: {
recipients: {
some: {
email: team.teamEmail.email,
},
},
},
{
User: {
user: {
email: team.teamEmail.email,
},
},
@ -154,7 +154,7 @@ export const getDocumentWhereInput = async ({
{
OR: [
{
Recipient: {
recipients: {
some: {
email: user.email,
},

View File

@ -41,7 +41,7 @@ export const getDocumentByToken = async ({ token }: GetDocumentByTokenOptions) =
const result = await prisma.document.findFirstOrThrow({
where: {
Recipient: {
recipients: {
some: {
token,
},
@ -66,17 +66,17 @@ export const getDocumentAndSenderByToken = async ({
const result = await prisma.document.findFirstOrThrow({
where: {
Recipient: {
recipients: {
some: {
token,
},
},
},
include: {
User: true,
user: true,
documentData: true,
documentMeta: true,
Recipient: {
recipients: {
where: {
token,
},
@ -96,9 +96,9 @@ export const getDocumentAndSenderByToken = async ({
});
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const { password: _password, ...User } = result.User;
const { password: _password, ...user } = result.user;
const recipient = result.Recipient[0];
const recipient = result.recipients[0];
// Sanity check, should not be possible.
if (!recipient) {
@ -125,7 +125,7 @@ export const getDocumentAndSenderByToken = async ({
return {
...result,
User,
user,
};
};
@ -144,14 +144,14 @@ export const getDocumentAndRecipientByToken = async ({
const result = await prisma.document.findFirstOrThrow({
where: {
Recipient: {
recipients: {
some: {
token,
},
},
},
include: {
Recipient: {
recipients: {
where: {
token,
},
@ -160,7 +160,7 @@ export const getDocumentAndRecipientByToken = async ({
},
});
const [recipient] = result.Recipient;
const [recipient] = result.recipients;
// Sanity check, should not be possible.
if (!recipient) {
@ -185,8 +185,5 @@ export const getDocumentAndRecipientByToken = async ({
});
}
return {
...result,
Recipient: result.Recipient,
};
return result;
};

View File

@ -21,8 +21,8 @@ export type GetDocumentWithDetailsByIdOptions = {
export const ZGetDocumentWithDetailsByIdResponseSchema = DocumentSchema.extend({
documentData: DocumentDataSchema,
documentMeta: DocumentMetaSchema.nullable(),
Recipient: RecipientSchema.array(),
Field: FieldSchema.array(),
recipients: RecipientSchema.array(),
fields: FieldSchema.array(),
});
export type TGetDocumentWithDetailsByIdResponse = z.infer<
@ -45,8 +45,8 @@ export const getDocumentWithDetailsById = async ({
include: {
documentData: true,
documentMeta: true,
Recipient: true,
Field: true,
recipients: true,
fields: true,
},
});

View File

@ -85,8 +85,8 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: search, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
{ recipients: { some: { name: { contains: search, mode: 'insensitive' } } } },
{ recipients: { some: { email: { contains: search, mode: 'insensitive' } } } },
],
};
@ -113,7 +113,7 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
},
where: {
status: ExtendedDocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: user.email,
signingStatus: SigningStatus.NOT_SIGNED,
@ -132,7 +132,7 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
},
where: {
createdAt,
User: {
user: {
email: {
not: user.email,
},
@ -140,7 +140,7 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
OR: [
{
status: ExtendedDocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: user.email,
signingStatus: SigningStatus.SIGNED,
@ -150,7 +150,7 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
},
{
status: ExtendedDocumentStatus.COMPLETED,
Recipient: {
recipients: {
some: {
email: user.email,
signingStatus: SigningStatus.SIGNED,
@ -191,8 +191,8 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: options.search, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: options.search, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: options.search, mode: 'insensitive' } } } },
{ recipients: { some: { name: { contains: options.search, mode: 'insensitive' } } } },
{ recipients: { some: { email: { contains: options.search, mode: 'insensitive' } } } },
],
};
@ -234,7 +234,7 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
{
OR: [
{ userId: options.userId },
{ Recipient: { some: { email: options.currentUserEmail } } },
{ recipients: { some: { email: options.currentUserEmail } } },
],
},
],
@ -257,7 +257,7 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
teamId,
},
{
User: {
user: {
email: teamEmail,
},
},
@ -274,7 +274,7 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
userId: userIdWhereClause,
createdAt,
status: ExtendedDocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: teamEmail,
signingStatus: SigningStatus.NOT_SIGNED,
@ -296,7 +296,7 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
OR: [
{
status: ExtendedDocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: teamEmail,
signingStatus: SigningStatus.SIGNED,
@ -307,7 +307,7 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
},
{
status: ExtendedDocumentStatus.COMPLETED,
Recipient: {
recipients: {
some: {
email: teamEmail,
signingStatus: SigningStatus.SIGNED,

View File

@ -6,7 +6,10 @@ import { prisma } from '@documenso/prisma';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@ -31,17 +34,17 @@ export async function rejectDocumentWithToken({
documentId,
},
include: {
Document: {
document: {
include: {
User: true,
Recipient: true,
user: true,
recipients: true,
documentMeta: true,
},
},
},
});
const document = recipient?.Document;
const document = recipient?.document;
if (!recipient || !document) {
throw new TRPCError({
@ -97,7 +100,7 @@ export async function rejectDocumentWithToken({
id: document.id,
},
include: {
Recipient: true,
recipients: true,
documentMeta: true,
},
});
@ -109,7 +112,7 @@ export async function rejectDocumentWithToken({
// Trigger webhook for document rejection
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_REJECTED,
data: ZWebhookDocumentSchema.parse(updatedDocument),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: document.userId,
teamId: document.teamId ?? undefined,
});

View File

@ -54,7 +54,7 @@ export const resendDocument = async ({
const document = await prisma.document.findUnique({
where: documentWhereInput,
include: {
Recipient: {
recipients: {
where: {
id: {
in: recipients,
@ -80,7 +80,7 @@ export const resendDocument = async ({
throw new Error('Document not found');
}
if (document.Recipient.length === 0) {
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
@ -101,7 +101,7 @@ export const resendDocument = async ({
}
await Promise.all(
document.Recipient.map(async (recipient) => {
document.recipients.map(async (recipient) => {
if (recipient.role === RecipientRole.CC) {
return;
}

View File

@ -14,7 +14,10 @@ import {
} from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getFile } from '../../universal/upload/get-file';
import { putPdfFile } from '../../universal/upload/put-file';
@ -43,7 +46,7 @@ export const sealDocument = async ({
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
Recipient: {
recipients: {
every: {
signingStatus: SigningStatus.SIGNED,
},
@ -52,7 +55,7 @@ export const sealDocument = async ({
include: {
documentData: true,
documentMeta: true,
Recipient: true,
recipients: true,
team: {
select: {
teamGlobalSettings: {
@ -89,7 +92,7 @@ export const sealDocument = async ({
documentId: document.id,
},
include: {
Signature: true,
signature: true,
},
});
@ -206,13 +209,13 @@ export const sealDocument = async ({
include: {
documentData: true,
documentMeta: true,
Recipient: true,
recipients: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: ZWebhookDocumentSchema.parse(updatedDocument),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: document.userId,
teamId: document.teamId ?? undefined,
});

View File

@ -35,7 +35,7 @@ export const searchDocumentsWithKeyword = async ({
deletedAt: null,
},
{
Recipient: {
recipients: {
some: {
email: {
contains: query,
@ -48,7 +48,7 @@ export const searchDocumentsWithKeyword = async ({
},
{
status: DocumentStatus.COMPLETED,
Recipient: {
recipients: {
some: {
email: user.email,
},
@ -60,7 +60,7 @@ export const searchDocumentsWithKeyword = async ({
},
{
status: DocumentStatus.PENDING,
Recipient: {
recipients: {
some: {
email: user.email,
},
@ -91,7 +91,7 @@ export const searchDocumentsWithKeyword = async ({
],
},
include: {
Recipient: true,
recipients: true,
team: {
select: {
url: true,
@ -140,7 +140,7 @@ export const searchDocumentsWithKeyword = async ({
return canAccessDocument;
})
.map((document) => {
const { Recipient, ...documentWithoutRecipient } = document;
const { recipients, ...documentWithoutRecipient } = document;
let documentPath;
@ -149,13 +149,13 @@ export const searchDocumentsWithKeyword = async ({
} else if (document.teamId && document.team) {
documentPath = `${formatDocumentsPath(document.team.url)}/${document.id}`;
} else {
documentPath = getSigningLink(Recipient, user);
documentPath = getSigningLink(recipients, user);
}
return {
...documentWithoutRecipient,
path: documentPath,
value: [document.id, document.title, ...document.Recipient.map((r) => r.email)].join(' '),
value: [document.id, document.title, ...document.recipients.map((r) => r.email)].join(' '),
};
});

View File

@ -32,8 +32,8 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
include: {
documentData: true,
documentMeta: true,
Recipient: true,
User: true,
recipients: true,
user: true,
team: {
select: {
id: true,
@ -50,11 +50,11 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
const isDirectTemplate = document?.source === DocumentSource.TEMPLATE_DIRECT_LINK;
if (document.Recipient.length === 0) {
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
const { User: owner } = document;
const { user: owner } = document;
const completedDocument = await getFile(document.documentData);
@ -83,7 +83,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
// - Recipient emails are disabled
if (
isOwnerDocumentCompletedEmailEnabled &&
(!document.Recipient.find((recipient) => recipient.email === owner.email) ||
(!document.recipients.find((recipient) => recipient.email === owner.email) ||
!isDocumentCompletedEmailEnabled)
) {
const template = createElement(DocumentCompletedEmailTemplate, {
@ -150,7 +150,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
}
await Promise.all(
document.Recipient.map(async (recipient) => {
document.recipients.map(async (recipient) => {
const customEmailTemplate = {
'signer.name': recipient.name,
'signer.email': recipient.email,

View File

@ -23,7 +23,7 @@ export const sendDeleteEmail = async ({ documentId, reason }: SendDeleteEmailOpt
id: documentId,
},
include: {
User: true,
user: true,
documentMeta: true,
team: {
include: {
@ -45,7 +45,7 @@ export const sendDeleteEmail = async ({ documentId, reason }: SendDeleteEmailOpt
return;
}
const { email, name } = document.User;
const { email, name } = document.user;
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';

View File

@ -21,7 +21,10 @@ import {
import { jobs } from '../../jobs/client';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import { getFile } from '../../universal/upload/get-file';
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@ -36,7 +39,7 @@ export type SendDocumentOptions = {
export const ZSendDocumentResponseSchema = DocumentSchema.extend({
documentMeta: DocumentMetaSchema.nullable(),
Recipient: RecipientSchema.array(),
recipients: RecipientSchema.array(),
});
export type TSendDocumentResponse = z.infer<typeof ZSendDocumentResponseSchema>;
@ -68,7 +71,7 @@ export const sendDocument = async ({
}),
},
include: {
Recipient: {
recipients: {
orderBy: [{ signingOrder: { sort: 'asc', nulls: 'last' } }, { id: 'asc' }],
},
documentMeta: true,
@ -80,7 +83,7 @@ export const sendDocument = async ({
throw new Error('Document not found');
}
if (document.Recipient.length === 0) {
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
@ -90,13 +93,13 @@ export const sendDocument = async ({
const signingOrder = document.documentMeta?.signingOrder || DocumentSigningOrder.PARALLEL;
let recipientsToNotify = document.Recipient;
let recipientsToNotify = document.recipients;
if (signingOrder === DocumentSigningOrder.SEQUENTIAL) {
// Get the currently active recipient.
recipientsToNotify = document.Recipient.filter(
(r) => r.signingStatus === SigningStatus.NOT_SIGNED && r.role !== RecipientRole.CC,
).slice(0, 1);
recipientsToNotify = document.recipients
.filter((r) => r.signingStatus === SigningStatus.NOT_SIGNED && r.role !== RecipientRole.CC)
.slice(0, 1);
// Secondary filter so we aren't resending if the current active recipient has already
// received the document.
@ -194,7 +197,7 @@ export const sendDocument = async ({
);
}
const allRecipientsHaveNoActionToTake = document.Recipient.every(
const allRecipientsHaveNoActionToTake = document.recipients.every(
(recipient) =>
recipient.role === RecipientRole.CC || recipient.signingStatus === SigningStatus.SIGNED,
);
@ -215,7 +218,7 @@ export const sendDocument = async ({
},
include: {
documentMeta: true,
Recipient: true,
recipients: true,
},
});
}
@ -241,14 +244,14 @@ export const sendDocument = async ({
},
include: {
documentMeta: true,
Recipient: true,
recipients: true,
},
});
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SENT,
data: ZWebhookDocumentSchema.parse(updatedDocument),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId,
teamId,
});

View File

@ -21,14 +21,14 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE
const document = await prisma.document.findFirst({
where: {
id: documentId,
Recipient: {
recipients: {
some: {
id: recipientId,
},
},
},
include: {
Recipient: {
recipients: {
where: {
id: recipientId,
},
@ -46,7 +46,7 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE
throw new Error('Document not found');
}
if (document.Recipient.length === 0) {
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
@ -58,7 +58,7 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE
return;
}
const [recipient] = document.Recipient;
const [recipient] = document.recipients;
const { email, name } = recipient;

View File

@ -30,9 +30,9 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo
id,
},
include: {
Recipient: true,
recipients: true,
documentMeta: true,
User: true,
user: true,
team: {
include: {
teamGlobalSettings: true,
@ -45,7 +45,7 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo
throw new Error('Document not found');
}
const { status, User: user } = document;
const { status, user } = document;
const isDocumentDeletedEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
@ -54,11 +54,11 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo
// if the document is pending, send cancellation emails to all recipients
if (
status === DocumentStatus.PENDING &&
document.Recipient.length > 0 &&
document.recipients.length > 0 &&
isDocumentDeletedEmailEnabled
) {
await Promise.all(
document.Recipient.map(async (recipient) => {
document.recipients.map(async (recipient) => {
if (recipient.sendStatus !== SendStatus.SENT) {
return;
}

View File

@ -6,7 +6,10 @@ import { ReadStatus, SendStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type ViewedDocumentOptions = {
@ -71,7 +74,7 @@ export const viewedDocument = async ({
},
include: {
documentMeta: true,
Recipient: true,
recipients: true,
},
});
@ -81,7 +84,7 @@ export const viewedDocument = async ({
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_OPENED,
data: ZWebhookDocumentSchema.parse(document),
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(document)),
userId: document.userId,
teamId: document.teamId ?? undefined,
});

View File

@ -61,8 +61,8 @@ export const createDocumentFields = async ({
}),
},
include: {
Recipient: true,
Field: true,
recipients: true,
fields: true,
},
});
@ -80,7 +80,7 @@ export const createDocumentFields = async ({
// Field validation.
const validatedFields = fields.map((field) => {
const recipient = document.Recipient.find((recipient) => recipient.id === field.recipientId);
const recipient = document.recipients.find((recipient) => recipient.id === field.recipientId);
// Each field MUST have a recipient associated with it.
if (!recipient) {
@ -90,7 +90,7 @@ export const createDocumentFields = async ({
}
// Check whether the recipient associated with the field can have new fields created.
if (!canRecipientFieldsBeModified(recipient, document.Field)) {
if (!canRecipientFieldsBeModified(recipient, document.fields)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message:
'Recipient type cannot have fields, or they have already interacted with the document.',

View File

@ -145,7 +145,7 @@ export const createField = async ({
fieldMeta: result.data,
},
include: {
Recipient: true,
recipient: true,
},
});
@ -160,7 +160,7 @@ export const createField = async ({
},
data: {
fieldId: field.secondaryId,
fieldRecipientEmail: field.Recipient?.email ?? '',
fieldRecipientEmail: field.recipient?.email ?? '',
fieldRecipientId: recipientId,
fieldType: field.type,
},

View File

@ -56,8 +56,8 @@ export const createTemplateFields = async ({
}),
},
include: {
Recipient: true,
Field: true,
recipients: true,
fields: true,
},
});
@ -69,7 +69,7 @@ export const createTemplateFields = async ({
// Field validation.
const validatedFields = fields.map((field) => {
const recipient = template.Recipient.find((recipient) => recipient.id === field.recipientId);
const recipient = template.recipients.find((recipient) => recipient.id === field.recipientId);
// Each field MUST have a recipient associated with it.
if (!recipient) {
@ -79,7 +79,7 @@ export const createTemplateFields = async ({
}
// Check whether the recipient associated with the field can have new fields created.
if (!canRecipientFieldsBeModified(recipient, template.Field)) {
if (!canRecipientFieldsBeModified(recipient, template.fields)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message:
'Recipient type cannot have fields, or they have already interacted with the template.',

View File

@ -59,12 +59,12 @@ export const deleteDocumentField = async ({
}),
},
include: {
Recipient: {
recipients: {
where: {
id: field.recipientId,
},
include: {
Field: true,
fields: true,
},
},
},
@ -82,7 +82,7 @@ export const deleteDocumentField = async ({
});
}
const recipient = document.Recipient.find((recipient) => recipient.id === field.recipientId);
const recipient = document.recipients.find((recipient) => recipient.id === field.recipientId);
if (!recipient) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
@ -91,7 +91,7 @@ export const deleteDocumentField = async ({
}
// Check whether the recipient associated with the field can have new fields created.
if (!canRecipientFieldsBeModified(recipient, recipient.Field)) {
if (!canRecipientFieldsBeModified(recipient, recipient.fields)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Recipient has already interacted with the document.',
});

View File

@ -22,7 +22,7 @@ export const deleteField = async ({
const field = await prisma.field.delete({
where: {
id: fieldId,
Document: {
document: {
id: documentId,
...(teamId
? {
@ -42,7 +42,7 @@ export const deleteField = async ({
},
},
include: {
Recipient: true,
recipient: true,
},
});
@ -78,7 +78,7 @@ export const deleteField = async ({
},
data: {
fieldId: field.secondaryId,
fieldRecipientEmail: field.Recipient?.email ?? '',
fieldRecipientEmail: field.recipient?.email ?? '',
fieldRecipientId: field.recipientId ?? -1,
fieldType: field.type,
},

View File

@ -16,7 +16,7 @@ export const deleteTemplateField = async ({
const field = await prisma.field.findFirst({
where: {
id: fieldId,
Template: teamId
template: teamId
? {
team: {
id: teamId,

View File

@ -11,14 +11,14 @@ export const getCompletedFieldsForDocument = async ({
return await prisma.field.findMany({
where: {
documentId,
Recipient: {
recipient: {
signingStatus: SigningStatus.SIGNED,
},
inserted: true,
},
include: {
Signature: true,
Recipient: {
signature: true,
recipient: {
select: {
name: true,
email: true,

View File

@ -8,21 +8,21 @@ export type GetCompletedFieldsForTokenOptions = {
export const getCompletedFieldsForToken = async ({ token }: GetCompletedFieldsForTokenOptions) => {
return await prisma.field.findMany({
where: {
Document: {
Recipient: {
document: {
recipients: {
some: {
token,
},
},
},
Recipient: {
recipient: {
signingStatus: SigningStatus.SIGNED,
},
inserted: true,
},
include: {
Signature: true,
Recipient: {
signature: true,
recipient: {
select: {
name: true,
email: true,

View File

@ -29,7 +29,7 @@ export const getFieldById = async ({
id: fieldId,
documentId,
templateId,
Document: {
document: {
OR:
teamId === undefined
? [

View File

@ -16,7 +16,7 @@ export const getFieldsForDocument = async ({
const fields = await prisma.field.findMany({
where: {
documentId,
Document: teamId
document: teamId
? {
team: {
id: teamId,
@ -33,8 +33,8 @@ export const getFieldsForDocument = async ({
},
},
include: {
Signature: true,
Recipient: {
signature: true,
recipient: {
select: {
name: true,
email: true,

View File

@ -7,12 +7,12 @@ export type GetFieldsForTokenOptions = {
export const getFieldsForToken = async ({ token }: GetFieldsForTokenOptions) => {
return await prisma.field.findMany({
where: {
Recipient: {
recipient: {
token,
},
},
include: {
Signature: true,
signature: true,
},
});
};

View File

@ -20,17 +20,17 @@ export const removeSignedFieldWithToken = async ({
const field = await prisma.field.findFirstOrThrow({
where: {
id: fieldId,
Recipient: {
recipient: {
token,
},
},
include: {
Document: true,
Recipient: true,
document: true,
recipient: true,
},
});
const { Document: document, Recipient: recipient } = field;
const { document, recipient } = field;
if (!document) {
throw new Error(`Document not found for field ${field.id}`);

View File

@ -70,7 +70,7 @@ export const setFieldsForDocument = async ({
}),
},
include: {
Recipient: true,
recipients: true,
},
});
@ -91,7 +91,7 @@ export const setFieldsForDocument = async ({
documentId,
},
include: {
Recipient: true,
recipient: true,
},
});
@ -102,7 +102,7 @@ export const setFieldsForDocument = async ({
const linkedFields = fields.map((field) => {
const existing = existingFields.find((existingField) => existingField.id === field.id);
const recipient = document.Recipient.find(
const recipient = document.recipients.find(
(recipient) => recipient.email.toLowerCase() === field.signerEmail.toLowerCase(),
);
@ -237,12 +237,12 @@ export const setFieldsForDocument = async ({
customText: '',
inserted: false,
fieldMeta: parsedFieldMeta,
Document: {
document: {
connect: {
id: documentId,
},
},
Recipient: {
recipient: {
connect: {
documentId_email: {
documentId,
@ -318,7 +318,7 @@ export const setFieldsForDocument = async ({
metadata: requestMetadata,
data: {
fieldId: field.secondaryId,
fieldRecipientEmail: field.Recipient?.email ?? '',
fieldRecipientEmail: field.recipient?.email ?? '',
fieldRecipientId: field.recipientId ?? -1,
fieldType: field.type,
},

View File

@ -77,7 +77,7 @@ export const setFieldsForTemplate = async ({
templateId,
},
include: {
Recipient: true,
recipient: true,
},
});
@ -182,12 +182,12 @@ export const setFieldsForTemplate = async ({
customText: '',
inserted: false,
fieldMeta: parsedFieldMeta,
Template: {
template: {
connect: {
id: templateId,
},
},
Recipient: {
recipient: {
connect: {
templateId_email: {
templateId,

View File

@ -59,17 +59,17 @@ export const signFieldWithToken = async ({
const field = await prisma.field.findFirstOrThrow({
where: {
id: fieldId,
Recipient: {
recipient: {
token,
},
},
include: {
Document: true,
Recipient: true,
document: true,
recipient: true,
},
});
const { Document: document, Recipient: recipient } = field;
const { document, recipient } = field;
if (!document) {
throw new Error(`Document not found for field ${field.id}`);
@ -213,7 +213,7 @@ export const signFieldWithToken = async ({
// Dirty but I don't want to deal with type information
Object.assign(updatedField, {
Signature: signature,
signature,
});
}

View File

@ -64,8 +64,8 @@ export const updateDocumentFields = async ({
}),
},
include: {
Recipient: true,
Field: true,
recipients: true,
fields: true,
},
});
@ -82,7 +82,7 @@ export const updateDocumentFields = async ({
}
const fieldsToUpdate = fields.map((field) => {
const originalField = document.Field.find((existingField) => existingField.id === field.id);
const originalField = document.fields.find((existingField) => existingField.id === field.id);
if (!originalField) {
throw new AppError(AppErrorCode.NOT_FOUND, {
@ -90,7 +90,7 @@ export const updateDocumentFields = async ({
});
}
const recipient = document.Recipient.find(
const recipient = document.recipients.find(
(recipient) => recipient.id === originalField.recipientId,
);
@ -102,7 +102,7 @@ export const updateDocumentFields = async ({
}
// Check whether the recipient associated with the field can be modified.
if (!canRecipientFieldsBeModified(recipient, document.Field)) {
if (!canRecipientFieldsBeModified(recipient, document.fields)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message:
'Cannot modify a field where the recipient has already interacted with the document',

View File

@ -44,7 +44,7 @@ export const updateField = async ({
const oldField = await prisma.field.findFirstOrThrow({
where: {
id: fieldId,
Document: {
document: {
id: documentId,
...(teamId
? {
@ -86,7 +86,7 @@ export const updateField = async ({
fieldMeta: newFieldMeta,
},
include: {
Recipient: true,
recipient: true,
},
});
@ -127,7 +127,7 @@ export const updateField = async ({
},
data: {
fieldId: updatedField.secondaryId,
fieldRecipientEmail: updatedField.Recipient?.email ?? '',
fieldRecipientEmail: updatedField.recipient?.email ?? '',
fieldRecipientId: recipientId ?? -1,
fieldType: updatedField.type,
changes: diffFieldChanges(oldField, updatedField),

View File

@ -56,8 +56,8 @@ export const updateTemplateFields = async ({
}),
},
include: {
Recipient: true,
Field: true,
recipients: true,
fields: true,
},
});
@ -68,7 +68,7 @@ export const updateTemplateFields = async ({
}
const fieldsToUpdate = fields.map((field) => {
const originalField = template.Field.find((existingField) => existingField.id === field.id);
const originalField = template.fields.find((existingField) => existingField.id === field.id);
if (!originalField) {
throw new AppError(AppErrorCode.NOT_FOUND, {
@ -76,7 +76,7 @@ export const updateTemplateFields = async ({
});
}
const recipient = template.Recipient.find(
const recipient = template.recipients.find(
(recipient) => recipient.id === originalField.recipientId,
);
@ -88,7 +88,7 @@ export const updateTemplateFields = async ({
}
// Check whether the recipient associated with the field can be modified.
if (!canRecipientFieldsBeModified(recipient, template.Field)) {
if (!canRecipientFieldsBeModified(recipient, template.fields)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message:
'Cannot modify a field where the recipient has already interacted with the document',

View File

@ -98,8 +98,8 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
type: P.union(FieldType.SIGNATURE, FieldType.FREE_SIGNATURE),
},
async (field) => {
if (field.Signature?.signatureImageAsBase64) {
const image = await pdf.embedPng(field.Signature?.signatureImageAsBase64 ?? '');
if (field.signature?.signatureImageAsBase64) {
const image = await pdf.embedPng(field.signature?.signatureImageAsBase64 ?? '');
let imageWidth = image.width;
let imageHeight = image.height;
@ -136,7 +136,7 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
rotate: degrees(pageRotationInDegrees),
});
} else {
const signatureText = field.Signature?.typedSignature ?? '';
const signatureText = field.signature?.typedSignature ?? '';
const longestLineInTextForWidth = signatureText
.split('\n')

View File

@ -61,7 +61,7 @@ export const getPublicProfileByUrl = async ({
},
include: {
profile: true,
Template: {
templates: {
where: {
directLink: {
enabled: true,
@ -73,7 +73,7 @@ export const getPublicProfileByUrl = async ({
},
},
// Subscriptions and teamMembers are used to calculate the badges.
Subscription: {
subscriptions: {
where: {
status: SubscriptionStatus.ACTIVE,
},
@ -133,7 +133,7 @@ export const getPublicProfileByUrl = async ({
if (IS_BILLING_ENABLED()) {
const earlyAdopterPriceIds = await getCommunityPlanPriceIds();
const activeEarlyAdopterSub = user.Subscription.find(
const activeEarlyAdopterSub = user.subscriptions.find(
(subscription) =>
subscription.status === SubscriptionStatus.ACTIVE &&
earlyAdopterPriceIds.includes(subscription.priceId),
@ -154,7 +154,7 @@ export const getPublicProfileByUrl = async ({
url: profileUrl,
avatarImageId: user.avatarImageId,
name: user.name || '',
templates: user.Template.filter(
templates: user.templates.filter(
(template): template is PublicDirectLinkTemplate =>
template.directLink?.enabled === true && template.type === TemplateType.PUBLIC,
),

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