mirror of
https://github.com/documenso/documenso.git
synced 2025-11-26 22:44:41 +10:00
Compare commits
1 Commits
archive/1.
...
cb9bf407f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb9bf407f7 |
@@ -185,10 +185,6 @@ export const OrganisationMemberInviteDialog = ({
|
|||||||
return 'form';
|
return 'form';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullOrganisation.members.length < fullOrganisation.organisationClaim.memberCount) {
|
|
||||||
return 'form';
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is probably going to screw us over in the future.
|
// This is probably going to screw us over in the future.
|
||||||
if (fullOrganisation.organisationClaim.originalSubscriptionClaimId !== INTERNAL_CLAIM_ID.TEAM) {
|
if (fullOrganisation.organisationClaim.originalSubscriptionClaimId !== INTERNAL_CLAIM_ID.TEAM) {
|
||||||
return 'alert';
|
return 'alert';
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export type EmbedAuthenticationRequiredProps = {
|
|||||||
email?: string;
|
email?: string;
|
||||||
returnTo: string;
|
returnTo: string;
|
||||||
isGoogleSSOEnabled?: boolean;
|
isGoogleSSOEnabled?: boolean;
|
||||||
isMicrosoftSSOEnabled?: boolean;
|
|
||||||
isOIDCSSOEnabled?: boolean;
|
isOIDCSSOEnabled?: boolean;
|
||||||
oidcProviderLabel?: string;
|
oidcProviderLabel?: string;
|
||||||
};
|
};
|
||||||
@@ -18,7 +17,6 @@ export const EmbedAuthenticationRequired = ({
|
|||||||
email,
|
email,
|
||||||
returnTo,
|
returnTo,
|
||||||
// isGoogleSSOEnabled,
|
// isGoogleSSOEnabled,
|
||||||
// isMicrosoftSSOEnabled,
|
|
||||||
// isOIDCSSOEnabled,
|
// isOIDCSSOEnabled,
|
||||||
// oidcProviderLabel,
|
// oidcProviderLabel,
|
||||||
}: EmbedAuthenticationRequiredProps) => {
|
}: EmbedAuthenticationRequiredProps) => {
|
||||||
@@ -39,7 +37,6 @@ export const EmbedAuthenticationRequired = ({
|
|||||||
<SignInForm
|
<SignInForm
|
||||||
// Embed currently not supported.
|
// Embed currently not supported.
|
||||||
// isGoogleSSOEnabled={isGoogleSSOEnabled}
|
// isGoogleSSOEnabled={isGoogleSSOEnabled}
|
||||||
// isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
|
|
||||||
// isOIDCSSOEnabled={isOIDCSSOEnabled}
|
// isOIDCSSOEnabled={isOIDCSSOEnabled}
|
||||||
// oidcProviderLabel={oidcProviderLabel}
|
// oidcProviderLabel={oidcProviderLabel}
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import { ZDirectTemplateEmbedDataSchema } from '~/types/embed-direct-template-sc
|
|||||||
import { injectCss } from '~/utils/css-vars';
|
import { injectCss } from '~/utils/css-vars';
|
||||||
|
|
||||||
import type { DirectTemplateLocalField } from '../general/direct-template/direct-template-signing-form';
|
import type { DirectTemplateLocalField } from '../general/direct-template/direct-template-signing-form';
|
||||||
import { DocumentSigningAttachmentsPopover } from '../general/document-signing/document-signing-attachments-popover';
|
|
||||||
import { useRequiredDocumentSigningContext } from '../general/document-signing/document-signing-provider';
|
import { useRequiredDocumentSigningContext } from '../general/document-signing/document-signing-provider';
|
||||||
import { EmbedClientLoading } from './embed-client-loading';
|
import { EmbedClientLoading } from './embed-client-loading';
|
||||||
import { EmbedDocumentCompleted } from './embed-document-completed';
|
import { EmbedDocumentCompleted } from './embed-document-completed';
|
||||||
@@ -45,7 +44,6 @@ import { EmbedDocumentFields } from './embed-document-fields';
|
|||||||
|
|
||||||
export type EmbedDirectTemplateClientPageProps = {
|
export type EmbedDirectTemplateClientPageProps = {
|
||||||
token: string;
|
token: string;
|
||||||
envelopeId: string;
|
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
documentData: DocumentData;
|
documentData: DocumentData;
|
||||||
recipient: Recipient;
|
recipient: Recipient;
|
||||||
@@ -57,10 +55,9 @@ export type EmbedDirectTemplateClientPageProps = {
|
|||||||
|
|
||||||
export const EmbedDirectTemplateClientPage = ({
|
export const EmbedDirectTemplateClientPage = ({
|
||||||
token,
|
token,
|
||||||
envelopeId,
|
|
||||||
updatedAt,
|
updatedAt,
|
||||||
documentData,
|
documentData,
|
||||||
recipient,
|
recipient: _recipient,
|
||||||
fields,
|
fields,
|
||||||
metadata,
|
metadata,
|
||||||
hidePoweredBy = false,
|
hidePoweredBy = false,
|
||||||
@@ -324,13 +321,9 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="embed--Root relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
|
<div className="relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
|
||||||
{(!hasFinishedInit || !hasDocumentLoaded) && <EmbedClientLoading />}
|
{(!hasFinishedInit || !hasDocumentLoaded) && <EmbedClientLoading />}
|
||||||
|
|
||||||
<div className="embed--Actions mb-4 flex w-full flex-row-reverse items-baseline justify-between">
|
|
||||||
<DocumentSigningAttachmentsPopover envelopeId={envelopeId} token={recipient.token} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
|
<div className="relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
|
||||||
{/* Viewer */}
|
{/* Viewer */}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import { BrandingLogo } from '~/components/general/branding-logo';
|
|||||||
import { injectCss } from '~/utils/css-vars';
|
import { injectCss } from '~/utils/css-vars';
|
||||||
|
|
||||||
import { ZSignDocumentEmbedDataSchema } from '../../types/embed-document-sign-schema';
|
import { ZSignDocumentEmbedDataSchema } from '../../types/embed-document-sign-schema';
|
||||||
import { DocumentSigningAttachmentsPopover } from '../general/document-signing/document-signing-attachments-popover';
|
|
||||||
import { useRequiredDocumentSigningContext } from '../general/document-signing/document-signing-provider';
|
import { useRequiredDocumentSigningContext } from '../general/document-signing/document-signing-provider';
|
||||||
import { DocumentSigningRecipientProvider } from '../general/document-signing/document-signing-recipient-provider';
|
import { DocumentSigningRecipientProvider } from '../general/document-signing/document-signing-recipient-provider';
|
||||||
import { DocumentSigningRejectDialog } from '../general/document-signing/document-signing-reject-dialog';
|
import { DocumentSigningRejectDialog } from '../general/document-signing/document-signing-reject-dialog';
|
||||||
@@ -49,7 +48,6 @@ import { EmbedDocumentRejected } from './embed-document-rejected';
|
|||||||
export type EmbedSignDocumentClientPageProps = {
|
export type EmbedSignDocumentClientPageProps = {
|
||||||
token: string;
|
token: string;
|
||||||
documentId: number;
|
documentId: number;
|
||||||
envelopeId: string;
|
|
||||||
documentData: DocumentData;
|
documentData: DocumentData;
|
||||||
recipient: RecipientWithFields;
|
recipient: RecipientWithFields;
|
||||||
fields: Field[];
|
fields: Field[];
|
||||||
@@ -64,7 +62,6 @@ export type EmbedSignDocumentClientPageProps = {
|
|||||||
export const EmbedSignDocumentClientPage = ({
|
export const EmbedSignDocumentClientPage = ({
|
||||||
token,
|
token,
|
||||||
documentId,
|
documentId,
|
||||||
envelopeId,
|
|
||||||
documentData,
|
documentData,
|
||||||
recipient,
|
recipient,
|
||||||
fields,
|
fields,
|
||||||
@@ -277,17 +274,15 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
<div className="embed--Root relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
|
<div className="embed--Root relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
|
||||||
{(!hasFinishedInit || !hasDocumentLoaded) && <EmbedClientLoading />}
|
{(!hasFinishedInit || !hasDocumentLoaded) && <EmbedClientLoading />}
|
||||||
|
|
||||||
<div className="embed--Actions mb-4 flex w-full flex-row-reverse items-baseline justify-between">
|
|
||||||
<DocumentSigningAttachmentsPopover envelopeId={envelopeId} token={token} />
|
|
||||||
|
|
||||||
{allowDocumentRejection && (
|
{allowDocumentRejection && (
|
||||||
|
<div className="embed--Actions mb-4 flex w-full flex-row-reverse items-baseline justify-between">
|
||||||
<DocumentSigningRejectDialog
|
<DocumentSigningRejectDialog
|
||||||
documentId={documentId}
|
documentId={documentId}
|
||||||
token={token}
|
token={token}
|
||||||
onRejected={onDocumentRejected}
|
onRejected={onDocumentRejected}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="embed--DocumentContainer relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
|
<div className="embed--DocumentContainer relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
|
||||||
{/* Viewer */}
|
{/* Viewer */}
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ export const SignInForm = ({
|
|||||||
|
|
||||||
const [isTwoFactorAuthenticationDialogOpen, setIsTwoFactorAuthenticationDialogOpen] =
|
const [isTwoFactorAuthenticationDialogOpen, setIsTwoFactorAuthenticationDialogOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [isEmbeddedRedirect, setIsEmbeddedRedirect] = useState(false);
|
|
||||||
|
|
||||||
const [twoFactorAuthenticationMethod, setTwoFactorAuthenticationMethod] = useState<
|
const [twoFactorAuthenticationMethod, setTwoFactorAuthenticationMethod] = useState<
|
||||||
'totp' | 'backup'
|
'totp' | 'backup'
|
||||||
@@ -318,8 +317,6 @@ export const SignInForm = ({
|
|||||||
if (email) {
|
if (email) {
|
||||||
form.setValue('email', email);
|
form.setValue('email', email);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsEmbeddedRedirect(params.get('embedded') === 'true');
|
|
||||||
}, [form]);
|
}, [form]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -386,8 +383,6 @@ export const SignInForm = ({
|
|||||||
{isSubmitting ? <Trans>Signing in...</Trans> : <Trans>Sign In</Trans>}
|
{isSubmitting ? <Trans>Signing in...</Trans> : <Trans>Sign In</Trans>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{!isEmbeddedRedirect && (
|
|
||||||
<>
|
|
||||||
{hasSocialAuthEnabled && (
|
{hasSocialAuthEnabled && (
|
||||||
<div className="relative flex items-center justify-center gap-x-4 py-2 text-xs uppercase">
|
<div className="relative flex items-center justify-center gap-x-4 py-2 text-xs uppercase">
|
||||||
<div className="bg-border h-px flex-1" />
|
<div className="bg-border h-px flex-1" />
|
||||||
@@ -421,11 +416,7 @@ export const SignInForm = ({
|
|||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
onClick={onSignInWithMicrosoftClick}
|
onClick={onSignInWithMicrosoftClick}
|
||||||
>
|
>
|
||||||
<img
|
<img className="mr-2 h-4 w-4" alt="Microsoft Logo" src={'/static/microsoft.svg'} />
|
||||||
className="mr-2 h-4 w-4"
|
|
||||||
alt="Microsoft Logo"
|
|
||||||
src={'/static/microsoft.svg'}
|
|
||||||
/>
|
|
||||||
Microsoft
|
Microsoft
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -443,8 +434,6 @@ export const SignInForm = ({
|
|||||||
{oidcProviderLabel || 'OIDC'}
|
{oidcProviderLabel || 'OIDC'}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ export type SignUpFormProps = {
|
|||||||
isGoogleSSOEnabled?: boolean;
|
isGoogleSSOEnabled?: boolean;
|
||||||
isMicrosoftSSOEnabled?: boolean;
|
isMicrosoftSSOEnabled?: boolean;
|
||||||
isOIDCSSOEnabled?: boolean;
|
isOIDCSSOEnabled?: boolean;
|
||||||
returnTo?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignUpForm = ({
|
export const SignUpForm = ({
|
||||||
@@ -77,7 +76,6 @@ export const SignUpForm = ({
|
|||||||
isGoogleSSOEnabled,
|
isGoogleSSOEnabled,
|
||||||
isMicrosoftSSOEnabled,
|
isMicrosoftSSOEnabled,
|
||||||
isOIDCSSOEnabled,
|
isOIDCSSOEnabled,
|
||||||
returnTo,
|
|
||||||
}: SignUpFormProps) => {
|
}: SignUpFormProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -112,7 +110,7 @@ export const SignUpForm = ({
|
|||||||
signature,
|
signature,
|
||||||
});
|
});
|
||||||
|
|
||||||
await navigate(returnTo ? returnTo : '/unverified-account');
|
await navigate(`/unverified-account`);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`Registration Successful`),
|
title: _(msg`Registration Successful`),
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
import { Trans } from '@lingui/react/macro';
|
|
||||||
import { ExternalLink, PaperclipIcon } from 'lucide-react';
|
|
||||||
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover';
|
|
||||||
|
|
||||||
export type DocumentSigningAttachmentsPopoverProps = {
|
|
||||||
envelopeId: string;
|
|
||||||
token: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DocumentSigningAttachmentsPopover = ({
|
|
||||||
envelopeId,
|
|
||||||
token,
|
|
||||||
}: DocumentSigningAttachmentsPopoverProps) => {
|
|
||||||
const { data: attachments } = trpc.envelope.attachment.find.useQuery({
|
|
||||||
envelopeId,
|
|
||||||
token,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!attachments || attachments.data.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button variant="outline" className="gap-2">
|
|
||||||
<PaperclipIcon className="h-4 w-4" />
|
|
||||||
<span>
|
|
||||||
<Trans>Attachments</Trans>{' '}
|
|
||||||
{attachments && attachments.data.length > 0 && (
|
|
||||||
<span className="ml-1">({attachments.data.length})</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
|
|
||||||
<PopoverContent className="w-96" align="start">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<h4 className="font-medium">
|
|
||||||
<Trans>Attachments</Trans>
|
|
||||||
</h4>
|
|
||||||
<p className="text-muted-foreground mt-1 text-sm">
|
|
||||||
<Trans>Documents and resources related to this envelope.</Trans>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{attachments?.data.map((attachment) => (
|
|
||||||
<a
|
|
||||||
key={attachment.id}
|
|
||||||
href={attachment.data}
|
|
||||||
title={attachment.data}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="border-border hover:bg-muted/50 group flex items-center justify-between rounded-md border px-3 py-2.5 transition duration-200"
|
|
||||||
>
|
|
||||||
<div className="flex flex-1 items-center gap-2.5">
|
|
||||||
<div className="bg-muted rounded p-2">
|
|
||||||
<PaperclipIcon className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className="text-muted-foreground hover:text-foreground block truncate text-sm underline">
|
|
||||||
{attachment.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ExternalLink className="h-4 w-4 opacity-0 transition duration-200 group-hover:opacity-100" />
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -22,7 +22,7 @@ export const DocumentSigningAuthAccount = ({
|
|||||||
actionVerb = 'sign',
|
actionVerb = 'sign',
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: DocumentSigningAuthAccountProps) => {
|
}: DocumentSigningAuthAccountProps) => {
|
||||||
const { recipient, isDirectTemplate } = useRequiredDocumentSigningAuthContext();
|
const { recipient } = useRequiredDocumentSigningAuthContext();
|
||||||
|
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
@@ -34,10 +34,8 @@ export const DocumentSigningAuthAccount = ({
|
|||||||
try {
|
try {
|
||||||
setIsSigningOut(true);
|
setIsSigningOut(true);
|
||||||
|
|
||||||
const currentPath = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
||||||
|
|
||||||
await authClient.signOut({
|
await authClient.signOut({
|
||||||
redirectPath: `/signin?returnTo=${encodeURIComponent(currentPath)}#embedded=true&email=${isDirectTemplate ? '' : email}`,
|
redirectPath: `/signin#email=${email}`,
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
setIsSigningOut(false);
|
setIsSigningOut(false);
|
||||||
@@ -57,28 +55,16 @@ export const DocumentSigningAuthAccount = ({
|
|||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{actionTarget === 'DOCUMENT' && recipient.role === RecipientRole.VIEWER ? (
|
{actionTarget === 'DOCUMENT' && recipient.role === RecipientRole.VIEWER ? (
|
||||||
<span>
|
<span>
|
||||||
{isDirectTemplate ? (
|
|
||||||
<Trans>To mark this document as viewed, you need to be logged in.</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>
|
<Trans>
|
||||||
To mark this document as viewed, you need to be logged in as{' '}
|
To mark this document as viewed, you need to be logged in as{' '}
|
||||||
<strong>{recipient.email}</strong>
|
<strong>{recipient.email}</strong>
|
||||||
</Trans>
|
</Trans>
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
{isDirectTemplate ? (
|
{/* Todo: Translate */}
|
||||||
<Trans>
|
To {actionVerb.toLowerCase()} this {actionTarget.toLowerCase()}, you need to be logged
|
||||||
To {actionVerb.toLowerCase()} this {actionTarget.toLowerCase()}, you need to be
|
in as <strong>{recipient.email}</strong>
|
||||||
logged in.
|
|
||||||
</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>
|
|
||||||
To {actionVerb.toLowerCase()} this {actionTarget.toLowerCase()}, you need to be
|
|
||||||
logged in as <strong>{recipient.email}</strong>
|
|
||||||
</Trans>
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ export const DocumentSigningAuthDialog = ({
|
|||||||
onOpenChange,
|
onOpenChange,
|
||||||
onReauthFormSubmit,
|
onReauthFormSubmit,
|
||||||
}: DocumentSigningAuthDialogProps) => {
|
}: DocumentSigningAuthDialogProps) => {
|
||||||
const { recipient, user, isCurrentlyAuthenticating, isDirectTemplate } =
|
const { recipient, user, isCurrentlyAuthenticating } = useRequiredDocumentSigningAuthContext();
|
||||||
useRequiredDocumentSigningAuthContext();
|
|
||||||
|
|
||||||
// Filter out EXPLICIT_NONE from available auth types for the chooser
|
// Filter out EXPLICIT_NONE from available auth types for the chooser
|
||||||
const validAuthTypes = availableAuthTypes.filter(
|
const validAuthTypes = availableAuthTypes.filter(
|
||||||
@@ -169,11 +168,7 @@ export const DocumentSigningAuthDialog = ({
|
|||||||
match({ documentAuthType: selectedAuthType, user })
|
match({ documentAuthType: selectedAuthType, user })
|
||||||
.with(
|
.with(
|
||||||
{ documentAuthType: DocumentAuth.ACCOUNT },
|
{ documentAuthType: DocumentAuth.ACCOUNT },
|
||||||
{
|
{ user: P.when((user) => !user || user.email !== recipient.email) }, // Assume all current auth methods requires them to be logged in.
|
||||||
user: P.when(
|
|
||||||
(user) => !user || (user.email !== recipient.email && !isDirectTemplate),
|
|
||||||
),
|
|
||||||
}, // Assume all current auth methods requires them to be logged in.
|
|
||||||
() => <DocumentSigningAuthAccount onOpenChange={onOpenChange} />,
|
() => <DocumentSigningAuthAccount onOpenChange={onOpenChange} />,
|
||||||
)
|
)
|
||||||
.with({ documentAuthType: DocumentAuth.PASSKEY }, () => (
|
.with({ documentAuthType: DocumentAuth.PASSKEY }, () => (
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ export type DocumentSigningAuthContextValue = {
|
|||||||
derivedRecipientAccessAuth: TRecipientAccessAuthTypes[];
|
derivedRecipientAccessAuth: TRecipientAccessAuthTypes[];
|
||||||
derivedRecipientActionAuth: TRecipientActionAuthTypes[];
|
derivedRecipientActionAuth: TRecipientActionAuthTypes[];
|
||||||
isAuthRedirectRequired: boolean;
|
isAuthRedirectRequired: boolean;
|
||||||
isDirectTemplate?: boolean;
|
|
||||||
isCurrentlyAuthenticating: boolean;
|
isCurrentlyAuthenticating: boolean;
|
||||||
setIsCurrentlyAuthenticating: (_value: boolean) => void;
|
setIsCurrentlyAuthenticating: (_value: boolean) => void;
|
||||||
passkeyData: PasskeyData;
|
passkeyData: PasskeyData;
|
||||||
@@ -66,7 +65,6 @@ export const useRequiredDocumentSigningAuthContext = () => {
|
|||||||
export interface DocumentSigningAuthProviderProps {
|
export interface DocumentSigningAuthProviderProps {
|
||||||
documentAuthOptions: Envelope['authOptions'];
|
documentAuthOptions: Envelope['authOptions'];
|
||||||
recipient: SigningAuthRecipient;
|
recipient: SigningAuthRecipient;
|
||||||
isDirectTemplate?: boolean;
|
|
||||||
user?: SessionUser | null;
|
user?: SessionUser | null;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
@@ -74,7 +72,6 @@ export interface DocumentSigningAuthProviderProps {
|
|||||||
export const DocumentSigningAuthProvider = ({
|
export const DocumentSigningAuthProvider = ({
|
||||||
documentAuthOptions: initialDocumentAuthOptions,
|
documentAuthOptions: initialDocumentAuthOptions,
|
||||||
recipient: initialRecipient,
|
recipient: initialRecipient,
|
||||||
isDirectTemplate = false,
|
|
||||||
user,
|
user,
|
||||||
children,
|
children,
|
||||||
}: DocumentSigningAuthProviderProps) => {
|
}: DocumentSigningAuthProviderProps) => {
|
||||||
@@ -204,7 +201,6 @@ export const DocumentSigningAuthProvider = ({
|
|||||||
derivedRecipientAccessAuth,
|
derivedRecipientAccessAuth,
|
||||||
derivedRecipientActionAuth,
|
derivedRecipientActionAuth,
|
||||||
isAuthRedirectRequired,
|
isAuthRedirectRequired,
|
||||||
isDirectTemplate,
|
|
||||||
isCurrentlyAuthenticating,
|
isCurrentlyAuthenticating,
|
||||||
setIsCurrentlyAuthenticating,
|
setIsCurrentlyAuthenticating,
|
||||||
passkeyData,
|
passkeyData,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import { Card, CardContent } from '@documenso/ui/primitives/card';
|
|||||||
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
||||||
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
|
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
|
||||||
|
|
||||||
import { DocumentSigningAttachmentsPopover } from '~/components/general/document-signing/document-signing-attachments-popover';
|
|
||||||
import { DocumentSigningAutoSign } from '~/components/general/document-signing/document-signing-auto-sign';
|
import { DocumentSigningAutoSign } from '~/components/general/document-signing/document-signing-auto-sign';
|
||||||
import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field';
|
import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field';
|
||||||
import { DocumentSigningDateField } from '~/components/general/document-signing/document-signing-date-field';
|
import { DocumentSigningDateField } from '~/components/general/document-signing/document-signing-date-field';
|
||||||
@@ -232,14 +231,8 @@ export const DocumentSigningPageViewV1 = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-x-4">
|
|
||||||
<DocumentSigningAttachmentsPopover
|
|
||||||
envelopeId={document.envelopeId}
|
|
||||||
token={recipient.token}
|
|
||||||
/>
|
|
||||||
<DocumentSigningRejectDialog documentId={document.id} token={recipient.token} />
|
<DocumentSigningRejectDialog documentId={document.id} token={recipient.token} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative mt-4 flex w-full flex-col gap-x-6 gap-y-8 sm:mt-8 md:flex-row lg:gap-x-8 lg:gap-y-0">
|
<div className="relative mt-4 flex w-full flex-col gap-x-6 gap-y-8 sm:mt-8 md:flex-row lg:gap-x-8 lg:gap-y-0">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import { SignFieldNumberDialog } from '~/components/dialogs/sign-field-number-di
|
|||||||
import { SignFieldSignatureDialog } from '~/components/dialogs/sign-field-signature-dialog';
|
import { SignFieldSignatureDialog } from '~/components/dialogs/sign-field-signature-dialog';
|
||||||
import { SignFieldTextDialog } from '~/components/dialogs/sign-field-text-dialog';
|
import { SignFieldTextDialog } from '~/components/dialogs/sign-field-text-dialog';
|
||||||
|
|
||||||
import { DocumentSigningAttachmentsPopover } from '../document-signing/document-signing-attachments-popover';
|
|
||||||
import { EnvelopeItemSelector } from '../envelope-editor/envelope-file-selector';
|
import { EnvelopeItemSelector } from '../envelope-editor/envelope-file-selector';
|
||||||
import EnvelopeSignerForm from '../envelope-signing/envelope-signer-form';
|
import EnvelopeSignerForm from '../envelope-signing/envelope-signer-form';
|
||||||
import { EnvelopeSignerHeader } from '../envelope-signing/envelope-signer-header';
|
import { EnvelopeSignerHeader } from '../envelope-signing/envelope-signer-header';
|
||||||
@@ -32,13 +31,8 @@ const EnvelopeSignerPageRenderer = lazy(
|
|||||||
export const DocumentSigningPageViewV2 = () => {
|
export const DocumentSigningPageViewV2 = () => {
|
||||||
const { envelopeItems, currentEnvelopeItem, setCurrentEnvelopeItem } = useCurrentEnvelopeRender();
|
const { envelopeItems, currentEnvelopeItem, setCurrentEnvelopeItem } = useCurrentEnvelopeRender();
|
||||||
|
|
||||||
const {
|
const { envelope, recipientFields, recipientFieldsRemaining, showPendingFieldTooltip } =
|
||||||
envelope,
|
useRequiredEnvelopeSigningContext();
|
||||||
recipient,
|
|
||||||
recipientFields,
|
|
||||||
recipientFieldsRemaining,
|
|
||||||
showPendingFieldTooltip,
|
|
||||||
} = useRequiredEnvelopeSigningContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen bg-gray-50">
|
<div className="h-screen w-screen bg-gray-50">
|
||||||
@@ -89,10 +83,6 @@ export const DocumentSigningPageViewV2 = () => {
|
|||||||
<Trans>Actions</Trans>
|
<Trans>Actions</Trans>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<DocumentSigningAttachmentsPopover envelopeId={envelope.id} token={recipient.token} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Todo: Allow selecting which document to download and/or the original */}
|
{/* Todo: Allow selecting which document to download and/or the original */}
|
||||||
<Button variant="ghost" size="sm" className="w-full justify-start">
|
<Button variant="ghost" size="sm" className="w-full justify-start">
|
||||||
<DownloadCloudIcon className="mr-2 h-4 w-4" />
|
<DownloadCloudIcon className="mr-2 h-4 w-4" />
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
import { msg } from '@lingui/core/macro';
|
|
||||||
import { useLingui } from '@lingui/react';
|
|
||||||
import { Trans } from '@lingui/react/macro';
|
|
||||||
import { Paperclip, Plus, X } from 'lucide-react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { AppError } from '@documenso/lib/errors/app-error';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormMessage,
|
|
||||||
} from '@documenso/ui/primitives/form/form';
|
|
||||||
import { Input } from '@documenso/ui/primitives/input';
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover';
|
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
|
||||||
|
|
||||||
export type DocumentAttachmentsPopoverProps = {
|
|
||||||
envelopeId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ZAttachmentFormSchema = z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
url: z.string().url('Must be a valid URL'),
|
|
||||||
});
|
|
||||||
|
|
||||||
type TAttachmentFormSchema = z.infer<typeof ZAttachmentFormSchema>;
|
|
||||||
|
|
||||||
export const DocumentAttachmentsPopover = ({ envelopeId }: DocumentAttachmentsPopoverProps) => {
|
|
||||||
const { toast } = useToast();
|
|
||||||
const { _ } = useLingui();
|
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [isAdding, setIsAdding] = useState(false);
|
|
||||||
|
|
||||||
const utils = trpc.useUtils();
|
|
||||||
|
|
||||||
const { data: attachments } = trpc.envelope.attachment.find.useQuery({
|
|
||||||
envelopeId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutateAsync: createAttachment, isPending: isCreating } =
|
|
||||||
trpc.envelope.attachment.create.useMutation({
|
|
||||||
onSuccess: () => {
|
|
||||||
void utils.envelope.attachment.find.invalidate({ envelopeId });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutateAsync: deleteAttachment } = trpc.envelope.attachment.delete.useMutation({
|
|
||||||
onSuccess: () => {
|
|
||||||
void utils.envelope.attachment.find.invalidate({ envelopeId });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const form = useForm<TAttachmentFormSchema>({
|
|
||||||
resolver: zodResolver(ZAttachmentFormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
label: '',
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async (data: TAttachmentFormSchema) => {
|
|
||||||
try {
|
|
||||||
await createAttachment({
|
|
||||||
envelopeId,
|
|
||||||
data: {
|
|
||||||
label: data.label,
|
|
||||||
data: data.url,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
form.reset();
|
|
||||||
|
|
||||||
setIsAdding(false);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: _(msg`Success`),
|
|
||||||
description: _(msg`Attachment added successfully.`),
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const error = AppError.parseError(err);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: _(msg`Error`),
|
|
||||||
description: error.message,
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteAttachment = async (id: string) => {
|
|
||||||
try {
|
|
||||||
await deleteAttachment({ id });
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: _(msg`Success`),
|
|
||||||
description: _(msg`Attachment removed successfully.`),
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const error = AppError.parseError(err);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: _(msg`Error`),
|
|
||||||
description: error.message,
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button variant="outline" className="gap-2">
|
|
||||||
<Paperclip className="h-4 w-4" />
|
|
||||||
|
|
||||||
<span>
|
|
||||||
<Trans>Attachments</Trans>
|
|
||||||
{attachments && attachments.data.length > 0 && (
|
|
||||||
<span className="ml-1">({attachments.data.length})</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
|
|
||||||
<PopoverContent className="w-96" align="end">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<h4 className="font-medium">
|
|
||||||
<Trans>Attachments</Trans>
|
|
||||||
</h4>
|
|
||||||
<p className="text-muted-foreground mt-1 text-sm">
|
|
||||||
<Trans>Add links to relevant documents or resources.</Trans>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{attachments && attachments.data.length > 0 && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
{attachments?.data.map((attachment) => (
|
|
||||||
<div
|
|
||||||
key={attachment.id}
|
|
||||||
className="border-border flex items-center justify-between rounded-md border p-2"
|
|
||||||
>
|
|
||||||
<div className="min-w-0 flex-1">
|
|
||||||
<p className="truncate text-sm font-medium">{attachment.label}</p>
|
|
||||||
<a
|
|
||||||
href={attachment.data}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-muted-foreground hover:text-foreground truncate text-xs underline"
|
|
||||||
>
|
|
||||||
{attachment.data}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => void onDeleteAttachment(attachment.id)}
|
|
||||||
className="ml-2 h-8 w-8 p-0"
|
|
||||||
>
|
|
||||||
<X className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isAdding && (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className="w-full"
|
|
||||||
onClick={() => setIsAdding(true)}
|
|
||||||
>
|
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
|
||||||
<Trans>Add Attachment</Trans>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isAdding && (
|
|
||||||
<Form {...form}>
|
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="label"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder={_(msg`Label`)} {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="url"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormControl>
|
|
||||||
<Input type="url" placeholder={_(msg`URL`)} {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button type="submit" size="sm" className="flex-1" loading={isCreating}>
|
|
||||||
<Trans>Add</Trans>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className="flex-1"
|
|
||||||
onClick={() => {
|
|
||||||
setIsAdding(false);
|
|
||||||
form.reset();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Trans>Cancel</Trans>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -22,7 +22,6 @@ import { EnvelopeDistributeDialog } from '~/components/dialogs/envelope-distribu
|
|||||||
import { EnvelopeRedistributeDialog } from '~/components/dialogs/envelope-redistribute-dialog';
|
import { EnvelopeRedistributeDialog } from '~/components/dialogs/envelope-redistribute-dialog';
|
||||||
import { TemplateUseDialog } from '~/components/dialogs/template-use-dialog';
|
import { TemplateUseDialog } from '~/components/dialogs/template-use-dialog';
|
||||||
import { BrandingLogo } from '~/components/general/branding-logo';
|
import { BrandingLogo } from '~/components/general/branding-logo';
|
||||||
import { DocumentAttachmentsPopover } from '~/components/general/document/document-attachments-popover';
|
|
||||||
import { EnvelopeEditorSettingsDialog } from '~/components/general/envelope-editor/envelope-editor-settings-dialog';
|
import { EnvelopeEditorSettingsDialog } from '~/components/general/envelope-editor/envelope-editor-settings-dialog';
|
||||||
import { useCurrentTeam } from '~/providers/team';
|
import { useCurrentTeam } from '~/providers/team';
|
||||||
|
|
||||||
@@ -132,8 +131,6 @@ export default function EnvelopeEditorHeader() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<DocumentAttachmentsPopover envelopeId={envelope.id} />
|
|
||||||
|
|
||||||
<EnvelopeEditorSettingsDialog
|
<EnvelopeEditorSettingsDialog
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="outline" size="sm">
|
<Button variant="outline" size="sm">
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
|||||||
import { logDocumentAccess } from '@documenso/lib/utils/logger';
|
import { logDocumentAccess } from '@documenso/lib/utils/logger';
|
||||||
import { canAccessTeamDocument, formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { canAccessTeamDocument, formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
|
|
||||||
import { DocumentAttachmentsPopover } from '~/components/general/document/document-attachments-popover';
|
|
||||||
import { DocumentEditForm } from '~/components/general/document/document-edit-form';
|
import { DocumentEditForm } from '~/components/general/document/document-edit-form';
|
||||||
import { DocumentStatus } from '~/components/general/document/document-status';
|
import { DocumentStatus } from '~/components/general/document/document-status';
|
||||||
import { LegacyFieldWarningPopover } from '~/components/general/legacy-field-warning-popover';
|
import { LegacyFieldWarningPopover } from '~/components/general/legacy-field-warning-popover';
|
||||||
@@ -123,13 +122,11 @@ export default function DocumentEditPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-x-4">
|
|
||||||
<DocumentAttachmentsPopover envelopeId={document.envelopeId} />
|
|
||||||
|
|
||||||
{document.useLegacyFieldInsertion && (
|
{document.useLegacyFieldInsertion && (
|
||||||
|
<div>
|
||||||
<LegacyFieldWarningPopover type="document" documentId={document.id} />
|
<LegacyFieldWarningPopover type="document" documentId={document.id} />
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DocumentEditForm
|
<DocumentEditForm
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { getTemplateById } from '@documenso/lib/server-only/template/get-templat
|
|||||||
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
|
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||||
|
|
||||||
import { TemplateDirectLinkDialog } from '~/components/dialogs/template-direct-link-dialog';
|
import { TemplateDirectLinkDialog } from '~/components/dialogs/template-direct-link-dialog';
|
||||||
import { DocumentAttachmentsPopover } from '~/components/general/document/document-attachments-popover';
|
|
||||||
import { LegacyFieldWarningPopover } from '~/components/general/legacy-field-warning-popover';
|
import { LegacyFieldWarningPopover } from '~/components/general/legacy-field-warning-popover';
|
||||||
import { TemplateDirectLinkBadge } from '~/components/general/template/template-direct-link-badge';
|
import { TemplateDirectLinkBadge } from '~/components/general/template/template-direct-link-badge';
|
||||||
import { TemplateEditForm } from '~/components/general/template/template-edit-form';
|
import { TemplateEditForm } from '~/components/general/template/template-edit-form';
|
||||||
@@ -88,8 +87,6 @@ export default function TemplateEditPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 flex items-center gap-2 sm:mt-0 sm:self-end">
|
<div className="mt-2 flex items-center gap-2 sm:mt-0 sm:self-end">
|
||||||
<DocumentAttachmentsPopover envelopeId={template.envelopeId} />
|
|
||||||
|
|
||||||
<TemplateDirectLinkDialog
|
<TemplateDirectLinkDialog
|
||||||
templateId={template.id}
|
templateId={template.id}
|
||||||
directLink={template.directLink}
|
directLink={template.directLink}
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ export default function DirectTemplatePage() {
|
|||||||
<DocumentSigningAuthProvider
|
<DocumentSigningAuthProvider
|
||||||
documentAuthOptions={template.authOptions}
|
documentAuthOptions={template.authOptions}
|
||||||
recipient={directTemplateRecipient}
|
recipient={directTemplateRecipient}
|
||||||
isDirectTemplate={true}
|
|
||||||
user={user}
|
user={user}
|
||||||
>
|
>
|
||||||
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { Link, redirect } from 'react-router';
|
import { Link, redirect } from 'react-router';
|
||||||
|
|
||||||
@@ -11,7 +9,6 @@ import {
|
|||||||
OIDC_PROVIDER_LABEL,
|
OIDC_PROVIDER_LABEL,
|
||||||
} from '@documenso/lib/constants/auth';
|
} from '@documenso/lib/constants/auth';
|
||||||
import { env } from '@documenso/lib/utils/env';
|
import { env } from '@documenso/lib/utils/env';
|
||||||
import { isValidReturnTo, normalizeReturnTo } from '@documenso/lib/utils/is-valid-return-to';
|
|
||||||
|
|
||||||
import { SignInForm } from '~/components/forms/signin';
|
import { SignInForm } from '~/components/forms/signin';
|
||||||
import { appMetaTags } from '~/utils/meta';
|
import { appMetaTags } from '~/utils/meta';
|
||||||
@@ -31,12 +28,8 @@ export async function loader({ request }: Route.LoaderArgs) {
|
|||||||
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
|
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
|
||||||
const oidcProviderLabel = OIDC_PROVIDER_LABEL;
|
const oidcProviderLabel = OIDC_PROVIDER_LABEL;
|
||||||
|
|
||||||
let returnTo = new URL(request.url).searchParams.get('returnTo') ?? undefined;
|
|
||||||
|
|
||||||
returnTo = isValidReturnTo(returnTo) ? normalizeReturnTo(returnTo) : undefined;
|
|
||||||
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
throw redirect(returnTo || '/');
|
throw redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -44,28 +37,12 @@ export async function loader({ request }: Route.LoaderArgs) {
|
|||||||
isMicrosoftSSOEnabled,
|
isMicrosoftSSOEnabled,
|
||||||
isOIDCSSOEnabled,
|
isOIDCSSOEnabled,
|
||||||
oidcProviderLabel,
|
oidcProviderLabel,
|
||||||
returnTo,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SignIn({ loaderData }: Route.ComponentProps) {
|
export default function SignIn({ loaderData }: Route.ComponentProps) {
|
||||||
const {
|
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled, oidcProviderLabel } =
|
||||||
isGoogleSSOEnabled,
|
loaderData;
|
||||||
isMicrosoftSSOEnabled,
|
|
||||||
isOIDCSSOEnabled,
|
|
||||||
oidcProviderLabel,
|
|
||||||
returnTo,
|
|
||||||
} = loaderData;
|
|
||||||
|
|
||||||
const [isEmbeddedRedirect, setIsEmbeddedRedirect] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const hash = window.location.hash.slice(1);
|
|
||||||
|
|
||||||
const params = new URLSearchParams(hash);
|
|
||||||
|
|
||||||
setIsEmbeddedRedirect(params.get('embedded') === 'true');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen max-w-lg px-4">
|
<div className="w-screen max-w-lg px-4">
|
||||||
@@ -84,17 +61,13 @@ export default function SignIn({ loaderData }: Route.ComponentProps) {
|
|||||||
isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
|
isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
|
||||||
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
||||||
oidcProviderLabel={oidcProviderLabel}
|
oidcProviderLabel={oidcProviderLabel}
|
||||||
returnTo={returnTo}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isEmbeddedRedirect && env('NEXT_PUBLIC_DISABLE_SIGNUP') !== 'true' && (
|
{env('NEXT_PUBLIC_DISABLE_SIGNUP') !== 'true' && (
|
||||||
<p className="text-muted-foreground mt-6 text-center text-sm">
|
<p className="text-muted-foreground mt-6 text-center text-sm">
|
||||||
<Trans>
|
<Trans>
|
||||||
Don't have an account?{' '}
|
Don't have an account?{' '}
|
||||||
<Link
|
<Link to="/signup" className="text-documenso-700 duration-200 hover:opacity-70">
|
||||||
to={returnTo ? `/signup?returnTo=${encodeURIComponent(returnTo)}` : '/signup'}
|
|
||||||
className="text-documenso-700 duration-200 hover:opacity-70"
|
|
||||||
>
|
|
||||||
Sign up
|
Sign up
|
||||||
</Link>
|
</Link>
|
||||||
</Trans>
|
</Trans>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
IS_OIDC_SSO_ENABLED,
|
IS_OIDC_SSO_ENABLED,
|
||||||
} from '@documenso/lib/constants/auth';
|
} from '@documenso/lib/constants/auth';
|
||||||
import { env } from '@documenso/lib/utils/env';
|
import { env } from '@documenso/lib/utils/env';
|
||||||
import { isValidReturnTo, normalizeReturnTo } from '@documenso/lib/utils/is-valid-return-to';
|
|
||||||
|
|
||||||
import { SignUpForm } from '~/components/forms/signup';
|
import { SignUpForm } from '~/components/forms/signup';
|
||||||
import { appMetaTags } from '~/utils/meta';
|
import { appMetaTags } from '~/utils/meta';
|
||||||
@@ -17,7 +16,7 @@ export function meta() {
|
|||||||
return appMetaTags('Sign Up');
|
return appMetaTags('Sign Up');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loader({ request }: Route.LoaderArgs) {
|
export function loader() {
|
||||||
const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP');
|
const NEXT_PUBLIC_DISABLE_SIGNUP = env('NEXT_PUBLIC_DISABLE_SIGNUP');
|
||||||
|
|
||||||
// SSR env variables.
|
// SSR env variables.
|
||||||
@@ -29,20 +28,15 @@ export function loader({ request }: Route.LoaderArgs) {
|
|||||||
throw redirect('/signin');
|
throw redirect('/signin');
|
||||||
}
|
}
|
||||||
|
|
||||||
let returnTo = new URL(request.url).searchParams.get('returnTo') ?? undefined;
|
|
||||||
|
|
||||||
returnTo = isValidReturnTo(returnTo) ? normalizeReturnTo(returnTo) : undefined;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isGoogleSSOEnabled,
|
isGoogleSSOEnabled,
|
||||||
isMicrosoftSSOEnabled,
|
isMicrosoftSSOEnabled,
|
||||||
isOIDCSSOEnabled,
|
isOIDCSSOEnabled,
|
||||||
returnTo,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SignUp({ loaderData }: Route.ComponentProps) {
|
export default function SignUp({ loaderData }: Route.ComponentProps) {
|
||||||
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled, returnTo } = loaderData;
|
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled } = loaderData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SignUpForm
|
<SignUpForm
|
||||||
@@ -50,7 +44,6 @@ export default function SignUp({ loaderData }: Route.ComponentProps) {
|
|||||||
isGoogleSSOEnabled={isGoogleSSOEnabled}
|
isGoogleSSOEnabled={isGoogleSSOEnabled}
|
||||||
isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
|
isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
|
||||||
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
||||||
returnTo={returnTo}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Outlet, isRouteErrorResponse, useRouteError } from 'react-router';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
IS_GOOGLE_SSO_ENABLED,
|
IS_GOOGLE_SSO_ENABLED,
|
||||||
IS_MICROSOFT_SSO_ENABLED,
|
|
||||||
IS_OIDC_SSO_ENABLED,
|
IS_OIDC_SSO_ENABLED,
|
||||||
OIDC_PROVIDER_LABEL,
|
OIDC_PROVIDER_LABEL,
|
||||||
} from '@documenso/lib/constants/auth';
|
} from '@documenso/lib/constants/auth';
|
||||||
@@ -30,13 +29,11 @@ export function headers({ loaderHeaders }: Route.HeadersArgs) {
|
|||||||
export function loader() {
|
export function loader() {
|
||||||
// SSR env variables.
|
// SSR env variables.
|
||||||
const isGoogleSSOEnabled = IS_GOOGLE_SSO_ENABLED;
|
const isGoogleSSOEnabled = IS_GOOGLE_SSO_ENABLED;
|
||||||
const isMicrosoftSSOEnabled = IS_MICROSOFT_SSO_ENABLED;
|
|
||||||
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
|
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
|
||||||
const oidcProviderLabel = OIDC_PROVIDER_LABEL;
|
const oidcProviderLabel = OIDC_PROVIDER_LABEL;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isGoogleSSOEnabled,
|
isGoogleSSOEnabled,
|
||||||
isMicrosoftSSOEnabled,
|
|
||||||
isOIDCSSOEnabled,
|
isOIDCSSOEnabled,
|
||||||
oidcProviderLabel,
|
oidcProviderLabel,
|
||||||
};
|
};
|
||||||
@@ -47,8 +44,7 @@ export default function Layout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ErrorBoundary({ loaderData }: Route.ErrorBoundaryProps) {
|
export function ErrorBoundary({ loaderData }: Route.ErrorBoundaryProps) {
|
||||||
const { isGoogleSSOEnabled, isMicrosoftSSOEnabled, isOIDCSSOEnabled, oidcProviderLabel } =
|
const { isGoogleSSOEnabled, isOIDCSSOEnabled, oidcProviderLabel } = loaderData || {};
|
||||||
loaderData || {};
|
|
||||||
|
|
||||||
const error = useRouteError();
|
const error = useRouteError();
|
||||||
|
|
||||||
@@ -57,7 +53,6 @@ export function ErrorBoundary({ loaderData }: Route.ErrorBoundaryProps) {
|
|||||||
return (
|
return (
|
||||||
<EmbedAuthenticationRequired
|
<EmbedAuthenticationRequired
|
||||||
isGoogleSSOEnabled={isGoogleSSOEnabled}
|
isGoogleSSOEnabled={isGoogleSSOEnabled}
|
||||||
isMicrosoftSSOEnabled={isMicrosoftSSOEnabled}
|
|
||||||
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
||||||
oidcProviderLabel={oidcProviderLabel}
|
oidcProviderLabel={oidcProviderLabel}
|
||||||
email={error.data.email}
|
email={error.data.email}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
|||||||
throw data(
|
throw data(
|
||||||
{
|
{
|
||||||
type: 'embed-authentication-required',
|
type: 'embed-authentication-required',
|
||||||
|
email: user?.email,
|
||||||
returnTo: `/embed/direct/${token}`,
|
returnTo: `/embed/direct/${token}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,13 +117,11 @@ export default function EmbedDirectTemplatePage() {
|
|||||||
<DocumentSigningAuthProvider
|
<DocumentSigningAuthProvider
|
||||||
documentAuthOptions={template.authOptions}
|
documentAuthOptions={template.authOptions}
|
||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
isDirectTemplate={true}
|
|
||||||
user={user}
|
user={user}
|
||||||
>
|
>
|
||||||
<DocumentSigningRecipientProvider recipient={recipient}>
|
<DocumentSigningRecipientProvider recipient={recipient}>
|
||||||
<EmbedDirectTemplateClientPage
|
<EmbedDirectTemplateClientPage
|
||||||
token={token}
|
token={token}
|
||||||
envelopeId={template.envelopeId}
|
|
||||||
updatedAt={template.updatedAt}
|
updatedAt={template.updatedAt}
|
||||||
documentData={template.templateDocumentData}
|
documentData={template.templateDocumentData}
|
||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ export default function EmbedSignDocumentPage() {
|
|||||||
<EmbedSignDocumentClientPage
|
<EmbedSignDocumentClientPage
|
||||||
token={token}
|
token={token}
|
||||||
documentId={document.id}
|
documentId={document.id}
|
||||||
envelopeId={document.envelopeId}
|
|
||||||
documentData={document.documentData}
|
documentData={document.documentData}
|
||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
|
|||||||
@@ -103,5 +103,5 @@
|
|||||||
"vite-plugin-babel-macros": "^1.0.6",
|
"vite-plugin-babel-macros": "^1.0.6",
|
||||||
"vite-tsconfig-paths": "^5.1.4"
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
},
|
},
|
||||||
"version": "1.13.2"
|
"version": "1.13.0"
|
||||||
}
|
}
|
||||||
|
|||||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.13.2",
|
"version": "1.13.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.13.2",
|
"version": "1.13.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
"inngest-cli": "^0.29.1",
|
"inngest-cli": "^0.29.1",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
"mupdf": "^1.0.0",
|
"mupdf": "^1.0.0",
|
||||||
|
"pdf2json": "^4.0.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.2",
|
||||||
"zod": "3.24.1"
|
"zod": "3.24.1"
|
||||||
@@ -89,7 +90,7 @@
|
|||||||
},
|
},
|
||||||
"apps/remix": {
|
"apps/remix": {
|
||||||
"name": "@documenso/remix",
|
"name": "@documenso/remix",
|
||||||
"version": "1.13.2",
|
"version": "1.13.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cantoo/pdf-lib": "^2.3.2",
|
"@cantoo/pdf-lib": "^2.3.2",
|
||||||
"@documenso/api": "*",
|
"@documenso/api": "*",
|
||||||
@@ -27198,6 +27199,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/pdf2json": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdf2json/-/pdf2json-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-WkezNsLK8sGpuFC7+PPP0DsXROwdoOxmXPBTtUWWkCwCi/Vi97MRC52Ly6FWIJjOKIywpm/L2oaUgSrmtU+7ZQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"pdf2json": "bin/pdf2json.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pdfjs-dist": {
|
"node_modules/pdfjs-dist": {
|
||||||
"version": "3.11.174",
|
"version": "3.11.174",
|
||||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz",
|
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.13.2",
|
"version": "1.13.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"dev": "turbo run dev --filter=@documenso/remix",
|
"dev": "turbo run dev --filter=@documenso/remix",
|
||||||
@@ -74,6 +74,7 @@
|
|||||||
"inngest-cli": "^0.29.1",
|
"inngest-cli": "^0.29.1",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
"mupdf": "^1.0.0",
|
"mupdf": "^1.0.0",
|
||||||
|
"pdf2json": "^4.0.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.2",
|
||||||
"zod": "3.24.1"
|
"zod": "3.24.1"
|
||||||
|
|||||||
@@ -427,7 +427,6 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
|||||||
globalAccessAuth: body.authOptions?.globalAccessAuth,
|
globalAccessAuth: body.authOptions?.globalAccessAuth,
|
||||||
globalActionAuth: body.authOptions?.globalActionAuth,
|
globalActionAuth: body.authOptions?.globalActionAuth,
|
||||||
},
|
},
|
||||||
attachments: body.attachments,
|
|
||||||
meta: {
|
meta: {
|
||||||
subject: body.meta.subject,
|
subject: body.meta.subject,
|
||||||
message: body.meta.message,
|
message: body.meta.message,
|
||||||
@@ -498,7 +497,6 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
|||||||
publicDescription,
|
publicDescription,
|
||||||
type,
|
type,
|
||||||
meta,
|
meta,
|
||||||
attachments,
|
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -570,7 +568,6 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
|||||||
publicDescription,
|
publicDescription,
|
||||||
},
|
},
|
||||||
meta,
|
meta,
|
||||||
attachments,
|
|
||||||
requestMetadata: metadata,
|
requestMetadata: metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -795,7 +792,6 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
|||||||
...body.meta,
|
...body.meta,
|
||||||
title: body.title,
|
title: body.title,
|
||||||
},
|
},
|
||||||
attachments: body.attachments,
|
|
||||||
requestMetadata: metadata,
|
requestMetadata: metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
ZRecipientActionAuthTypesSchema,
|
ZRecipientActionAuthTypesSchema,
|
||||||
} from '@documenso/lib/types/document-auth';
|
} from '@documenso/lib/types/document-auth';
|
||||||
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
import { ZFieldMetaPrefillFieldsSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
import { ZFieldMetaPrefillFieldsSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||||
|
|
||||||
extendZodWithOpenApi(z);
|
extendZodWithOpenApi(z);
|
||||||
@@ -198,15 +197,6 @@ export const ZCreateDocumentMutationSchema = z.object({
|
|||||||
description: 'The globalActionAuth property is only available for Enterprise accounts.',
|
description: 'The globalActionAuth property is only available for Enterprise accounts.',
|
||||||
}),
|
}),
|
||||||
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
||||||
attachments: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
|
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
|
||||||
@@ -272,15 +262,6 @@ export const ZCreateDocumentFromTemplateMutationSchema = z.object({
|
|||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
||||||
attachments: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
|
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { deleteCookie } from 'hono/cookie';
|
|||||||
|
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { onCreateUserHook } from '@documenso/lib/server-only/user/create-user';
|
import { onCreateUserHook } from '@documenso/lib/server-only/user/create-user';
|
||||||
import { isValidReturnTo, normalizeReturnTo } from '@documenso/lib/utils/is-valid-return-to';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
import type { OAuthClientOptions } from '../../config';
|
import type { OAuthClientOptions } from '../../config';
|
||||||
@@ -178,12 +177,6 @@ export const validateOauth = async (options: HandleOAuthCallbackUrlOptions) => {
|
|||||||
redirectPath = '/';
|
redirectPath = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidReturnTo(redirectPath)) {
|
|
||||||
redirectPath = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
redirectPath = normalizeReturnTo(redirectPath) || '/';
|
|
||||||
|
|
||||||
const tokens = await oAuthClient.validateAuthorizationCode(
|
const tokens = await oAuthClient.validateAuthorizationCode(
|
||||||
token_endpoint,
|
token_endpoint,
|
||||||
code,
|
code,
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import { DocumentStatus } from '@prisma/client';
|
|
||||||
|
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
|
|
||||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
|
||||||
|
|
||||||
export type CreateAttachmentOptions = {
|
|
||||||
envelopeId: string;
|
|
||||||
teamId: number;
|
|
||||||
userId: number;
|
|
||||||
data: {
|
|
||||||
label: string;
|
|
||||||
data: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createAttachment = async ({
|
|
||||||
envelopeId,
|
|
||||||
teamId,
|
|
||||||
userId,
|
|
||||||
data,
|
|
||||||
}: CreateAttachmentOptions) => {
|
|
||||||
const envelope = await prisma.envelope.findFirst({
|
|
||||||
where: {
|
|
||||||
id: envelopeId,
|
|
||||||
team: buildTeamWhereQuery({ teamId, userId }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!envelope) {
|
|
||||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
||||||
message: 'Envelope not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (envelope.status === DocumentStatus.COMPLETED || envelope.status === DocumentStatus.REJECTED) {
|
|
||||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
|
||||||
message: 'Attachments can not be modified after the document has been completed or rejected',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await prisma.envelopeAttachment.create({
|
|
||||||
data: {
|
|
||||||
envelopeId,
|
|
||||||
type: 'link',
|
|
||||||
...data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { DocumentStatus } from '@prisma/client';
|
|
||||||
|
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
|
|
||||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
|
||||||
|
|
||||||
export type DeleteAttachmentOptions = {
|
|
||||||
id: string;
|
|
||||||
userId: number;
|
|
||||||
teamId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteAttachment = async ({ id, userId, teamId }: DeleteAttachmentOptions) => {
|
|
||||||
const attachment = await prisma.envelopeAttachment.findFirst({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
envelope: {
|
|
||||||
team: buildTeamWhereQuery({ teamId, userId }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
envelope: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!attachment) {
|
|
||||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
||||||
message: 'Attachment not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
attachment.envelope.status === DocumentStatus.COMPLETED ||
|
|
||||||
attachment.envelope.status === DocumentStatus.REJECTED
|
|
||||||
) {
|
|
||||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
|
||||||
message: 'Attachments can not be modified after the document has been completed or rejected',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.envelopeAttachment.delete({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
|
|
||||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
|
||||||
|
|
||||||
export type FindAttachmentsByEnvelopeIdOptions = {
|
|
||||||
envelopeId: string;
|
|
||||||
userId: number;
|
|
||||||
teamId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findAttachmentsByEnvelopeId = async ({
|
|
||||||
envelopeId,
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
}: FindAttachmentsByEnvelopeIdOptions) => {
|
|
||||||
const envelope = await prisma.envelope.findFirst({
|
|
||||||
where: {
|
|
||||||
id: envelopeId,
|
|
||||||
team: buildTeamWhereQuery({ teamId, userId }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!envelope) {
|
|
||||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
||||||
message: 'Envelope not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await prisma.envelopeAttachment.findMany({
|
|
||||||
where: {
|
|
||||||
envelopeId,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'asc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
|
|
||||||
export type FindAttachmentsByTokenOptions = {
|
|
||||||
envelopeId: string;
|
|
||||||
token: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findAttachmentsByToken = async ({
|
|
||||||
envelopeId,
|
|
||||||
token,
|
|
||||||
}: FindAttachmentsByTokenOptions) => {
|
|
||||||
const envelope = await prisma.envelope.findFirst({
|
|
||||||
where: {
|
|
||||||
id: envelopeId,
|
|
||||||
recipients: {
|
|
||||||
some: {
|
|
||||||
token,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!envelope) {
|
|
||||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
||||||
message: 'Envelope not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await prisma.envelopeAttachment.findMany({
|
|
||||||
where: {
|
|
||||||
envelopeId,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'asc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FindAttachmentsByTeamOptions = {
|
|
||||||
envelopeId: string;
|
|
||||||
teamId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findAttachmentsByTeam = async ({
|
|
||||||
envelopeId,
|
|
||||||
teamId,
|
|
||||||
}: FindAttachmentsByTeamOptions) => {
|
|
||||||
const envelope = await prisma.envelope.findFirst({
|
|
||||||
where: {
|
|
||||||
id: envelopeId,
|
|
||||||
teamId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!envelope) {
|
|
||||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
||||||
message: 'Envelope not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await prisma.envelopeAttachment.findMany({
|
|
||||||
where: {
|
|
||||||
envelopeId,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'asc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { DocumentStatus } from '@prisma/client';
|
|
||||||
|
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
|
|
||||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
|
||||||
|
|
||||||
export type UpdateAttachmentOptions = {
|
|
||||||
id: string;
|
|
||||||
userId: number;
|
|
||||||
teamId: number;
|
|
||||||
data: { label?: string; data?: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateAttachment = async ({ id, teamId, userId, data }: UpdateAttachmentOptions) => {
|
|
||||||
const attachment = await prisma.envelopeAttachment.findFirst({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
envelope: {
|
|
||||||
team: buildTeamWhereQuery({ teamId, userId }),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
envelope: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!attachment) {
|
|
||||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
||||||
message: 'Attachment not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
attachment.envelope.status === DocumentStatus.COMPLETED ||
|
|
||||||
attachment.envelope.status === DocumentStatus.REJECTED
|
|
||||||
) {
|
|
||||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
|
||||||
message: 'Attachments can not be modified after the document has been completed or rejected',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await prisma.envelopeAttachment.update({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -20,7 +20,6 @@ import type { TCreateEnvelopeRequest } from '@documenso/trpc/server/envelope-rou
|
|||||||
|
|
||||||
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
|
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
|
||||||
import type { TDocumentFormValues } from '../../types/document-form-values';
|
import type { TDocumentFormValues } from '../../types/document-form-values';
|
||||||
import type { TEnvelopeAttachmentType } from '../../types/envelope-attachment';
|
|
||||||
import {
|
import {
|
||||||
ZWebhookDocumentSchema,
|
ZWebhookDocumentSchema,
|
||||||
mapEnvelopeToWebhookDocumentPayload,
|
mapEnvelopeToWebhookDocumentPayload,
|
||||||
@@ -59,11 +58,6 @@ export type CreateEnvelopeOptions = {
|
|||||||
recipients?: TCreateEnvelopeRequest['recipients'];
|
recipients?: TCreateEnvelopeRequest['recipients'];
|
||||||
folderId?: string;
|
folderId?: string;
|
||||||
};
|
};
|
||||||
attachments?: Array<{
|
|
||||||
label: string;
|
|
||||||
data: string;
|
|
||||||
type?: TEnvelopeAttachmentType;
|
|
||||||
}>;
|
|
||||||
meta?: Partial<Omit<DocumentMeta, 'id'>>;
|
meta?: Partial<Omit<DocumentMeta, 'id'>>;
|
||||||
requestMetadata: ApiRequestMetadata;
|
requestMetadata: ApiRequestMetadata;
|
||||||
};
|
};
|
||||||
@@ -73,7 +67,6 @@ export const createEnvelope = async ({
|
|||||||
teamId,
|
teamId,
|
||||||
normalizePdf,
|
normalizePdf,
|
||||||
data,
|
data,
|
||||||
attachments,
|
|
||||||
meta,
|
meta,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
internalVersion,
|
internalVersion,
|
||||||
@@ -253,15 +246,6 @@ export const createEnvelope = async ({
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
envelopeAttachments: {
|
|
||||||
createMany: {
|
|
||||||
data: (attachments || []).map((attachment) => ({
|
|
||||||
label: attachment.label,
|
|
||||||
data: attachment.data,
|
|
||||||
type: attachment.type ?? 'link',
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
authOptions,
|
authOptions,
|
||||||
@@ -354,7 +338,6 @@ export const createEnvelope = async ({
|
|||||||
fields: true,
|
fields: true,
|
||||||
folder: true,
|
folder: true,
|
||||||
envelopeItems: true,
|
envelopeItems: true,
|
||||||
envelopeAttachments: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
202
packages/lib/server-only/pdf/auto-place-fields.ts
Normal file
202
packages/lib/server-only/pdf/auto-place-fields.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
import { PDFDocument, rgb } from '@cantoo/pdf-lib';
|
||||||
|
import PDFParser from 'pdf2json';
|
||||||
|
|
||||||
|
import { getPageSize } from './get-page-size';
|
||||||
|
|
||||||
|
type TextPosition = {
|
||||||
|
text: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
w: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CharIndexMapping = {
|
||||||
|
textPosIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PlaceholderInfo = {
|
||||||
|
placeholder: string;
|
||||||
|
fieldType: string;
|
||||||
|
recipient: string;
|
||||||
|
isRequired: string;
|
||||||
|
page: number;
|
||||||
|
// PDF2JSON coordinates (in page units - these are relative to page dimensions)
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
// Page dimensions from PDF2JSON (in page units)
|
||||||
|
pageWidth: number;
|
||||||
|
pageHeight: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Questions for later:
|
||||||
|
- Does it handle multi-page PDFs?
|
||||||
|
- What happens with incorrect placeholders? E.g. those containing non-accepted properties.
|
||||||
|
- The placeholder data is dynamic. How to handle this parsing? Perhaps we need to do it similar to the fieldMeta parsing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const extractPlaceholdersFromPDF = async (pdf: Buffer): Promise<PlaceholderInfo[]> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const parser = new PDFParser(null, true);
|
||||||
|
|
||||||
|
parser.on('pdfParser_dataError', (errData) => {
|
||||||
|
reject(errData);
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.on('pdfParser_dataReady', (pdfData) => {
|
||||||
|
const placeholders: PlaceholderInfo[] = [];
|
||||||
|
|
||||||
|
pdfData.Pages.forEach((page, pageIndex) => {
|
||||||
|
/*
|
||||||
|
pdf2json returns the PDF page content as an array of characters.
|
||||||
|
We need to concatenate the characters to get the full text.
|
||||||
|
We also need to get the position of the text so we can place the placeholders in the correct position.
|
||||||
|
|
||||||
|
Page dimensions from PDF2JSON are in "page units" (relative coordinates)
|
||||||
|
*/
|
||||||
|
const pageWidth = page.Width;
|
||||||
|
const pageHeight = page.Height;
|
||||||
|
|
||||||
|
let pageText = '';
|
||||||
|
const textPositions: TextPosition[] = [];
|
||||||
|
const charIndexToTextPos: CharIndexMapping[] = [];
|
||||||
|
|
||||||
|
page.Texts.forEach((text) => {
|
||||||
|
/*
|
||||||
|
R is an array that contains objects with each character.
|
||||||
|
The decodedText contains only the character, without any other information.
|
||||||
|
|
||||||
|
textPositions stores each character and its position on the page.
|
||||||
|
*/
|
||||||
|
const decodedText = text.R.map((run) => decodeURIComponent(run.T)).join('');
|
||||||
|
|
||||||
|
for (let i = 0; i < decodedText.length; i++) {
|
||||||
|
charIndexToTextPos.push({
|
||||||
|
textPosIndex: textPositions.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pageText += decodedText;
|
||||||
|
|
||||||
|
textPositions.push({
|
||||||
|
text: decodedText,
|
||||||
|
x: text.x,
|
||||||
|
y: text.y,
|
||||||
|
w: text.w || 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const placeholderMatches = pageText.matchAll(/{{([^}]+)}}/g);
|
||||||
|
|
||||||
|
for (const match of placeholderMatches) {
|
||||||
|
const placeholder = match[0];
|
||||||
|
const placeholderData = match[1].split(',').map((part) => part.trim());
|
||||||
|
|
||||||
|
const [fieldType, recipient, isRequired] = placeholderData;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find the position of where the placeholder starts in the text
|
||||||
|
|
||||||
|
Then find the position of where the placeholder ends in the text by adding the length of the placeholder to the index of the placeholder.
|
||||||
|
*/
|
||||||
|
const matchIndex = match.index;
|
||||||
|
const placeholderLength = placeholder.length;
|
||||||
|
const placeholderEndIndex = matchIndex + placeholderLength;
|
||||||
|
|
||||||
|
const startCharInfo = charIndexToTextPos[matchIndex];
|
||||||
|
const endCharInfo = charIndexToTextPos[placeholderEndIndex - 1];
|
||||||
|
|
||||||
|
if (!startCharInfo || !endCharInfo) {
|
||||||
|
console.error('Could not find text position for placeholder', placeholder);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTextPos = textPositions[startCharInfo.textPosIndex];
|
||||||
|
const endTextPos = textPositions[endCharInfo.textPosIndex];
|
||||||
|
|
||||||
|
/*
|
||||||
|
PDF2JSON coordinates - these are in "page units" (relative coordinates)
|
||||||
|
Calculate width as the distance from start to end, plus a portion of the last character's width
|
||||||
|
Use 10% of the last character width to avoid extending too far beyond the placeholder
|
||||||
|
*/
|
||||||
|
const x = startTextPos.x;
|
||||||
|
const y = startTextPos.y;
|
||||||
|
const width = endTextPos.x + endTextPos.w * 0.1 - startTextPos.x;
|
||||||
|
|
||||||
|
placeholders.push({
|
||||||
|
placeholder,
|
||||||
|
fieldType,
|
||||||
|
recipient,
|
||||||
|
isRequired,
|
||||||
|
page: pageIndex + 1,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height: 1,
|
||||||
|
pageWidth,
|
||||||
|
pageHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(placeholders);
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.parseBuffer(pdf);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const replacePlaceholdersInPDF = async (pdf: Buffer): Promise<Buffer> => {
|
||||||
|
const placeholders = await extractPlaceholdersFromPDF(pdf);
|
||||||
|
|
||||||
|
const pdfDoc = await PDFDocument.load(new Uint8Array(pdf));
|
||||||
|
const pages = pdfDoc.getPages();
|
||||||
|
|
||||||
|
for (const placeholder of placeholders) {
|
||||||
|
const pageIndex = placeholder.page - 1;
|
||||||
|
const page = pages[pageIndex];
|
||||||
|
|
||||||
|
const { width: pdfLibPageWidth, height: pdfLibPageHeight } = getPageSize(page);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert PDF2JSON coordinates to pdf-lib coordinates:
|
||||||
|
|
||||||
|
PDF2JSON uses relative "page units":
|
||||||
|
- x, y, width, height are in page units
|
||||||
|
- Page dimensions (Width, Height) are also in page units
|
||||||
|
|
||||||
|
pdf-lib uses absolute points (1 point = 1/72 inch):
|
||||||
|
- Need to convert from page units to points
|
||||||
|
- Y-axis in pdf-lib is bottom-up (origin at bottom-left)
|
||||||
|
- Y-axis in PDF2JSON is top-down (origin at top-left)
|
||||||
|
|
||||||
|
Conversion formulas:
|
||||||
|
- x_points = (x / pageWidth) * pdfLibPageWidth
|
||||||
|
- y_points = pdfLibPageHeight - ((y / pageHeight) * pdfLibPageHeight)
|
||||||
|
- width_points = (width / pageWidth) * pdfLibPageWidth
|
||||||
|
- height_points = (height / pageHeight) * pdfLibPageHeight
|
||||||
|
*/
|
||||||
|
|
||||||
|
const xPoints = (placeholder.x / placeholder.pageWidth) * pdfLibPageWidth;
|
||||||
|
const yPoints = pdfLibPageHeight - (placeholder.y / placeholder.pageHeight) * pdfLibPageHeight;
|
||||||
|
const widthPoints = (placeholder.width / placeholder.pageWidth) * pdfLibPageWidth;
|
||||||
|
const heightPoints = (placeholder.height / placeholder.pageHeight) * pdfLibPageHeight;
|
||||||
|
|
||||||
|
page.drawRectangle({
|
||||||
|
x: xPoints,
|
||||||
|
y: yPoints - heightPoints, // Adjust for height since y is at baseline
|
||||||
|
width: widthPoints,
|
||||||
|
height: heightPoints,
|
||||||
|
color: rgb(1, 1, 1),
|
||||||
|
borderColor: rgb(1, 1, 1),
|
||||||
|
borderWidth: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiedPdfBytes = await pdfDoc.save();
|
||||||
|
|
||||||
|
return Buffer.from(modifiedPdfBytes);
|
||||||
|
};
|
||||||
@@ -640,23 +640,6 @@ export const createDocumentFromDirectTemplate = async ({
|
|||||||
data: auditLogsToCreate,
|
data: auditLogsToCreate,
|
||||||
});
|
});
|
||||||
|
|
||||||
const templateAttachments = await tx.envelopeAttachment.findMany({
|
|
||||||
where: {
|
|
||||||
envelopeId: directTemplateEnvelope.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (templateAttachments.length > 0) {
|
|
||||||
await tx.envelopeAttachment.createMany({
|
|
||||||
data: templateAttachments.map((attachment) => ({
|
|
||||||
envelopeId: createdEnvelope.id,
|
|
||||||
type: attachment.type,
|
|
||||||
label: attachment.label,
|
|
||||||
data: attachment.data,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send email to template owner.
|
// Send email to template owner.
|
||||||
const emailTemplate = createElement(DocumentCreatedFromDirectTemplateEmailTemplate, {
|
const emailTemplate = createElement(DocumentCreatedFromDirectTemplateEmailTemplate, {
|
||||||
recipientName: directRecipientEmail,
|
recipientName: directRecipientEmail,
|
||||||
|
|||||||
@@ -91,12 +91,6 @@ export type CreateDocumentFromTemplateOptions = {
|
|||||||
envelopeItemId?: string;
|
envelopeItemId?: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
attachments?: Array<{
|
|
||||||
label: string;
|
|
||||||
data: string;
|
|
||||||
type?: 'link';
|
|
||||||
}>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Values that will override the predefined values in the template.
|
* Values that will override the predefined values in the template.
|
||||||
*/
|
*/
|
||||||
@@ -301,7 +295,6 @@ export const createDocumentFromTemplate = async ({
|
|||||||
requestMetadata,
|
requestMetadata,
|
||||||
folderId,
|
folderId,
|
||||||
prefillFields,
|
prefillFields,
|
||||||
attachments,
|
|
||||||
}: CreateDocumentFromTemplateOptions) => {
|
}: CreateDocumentFromTemplateOptions) => {
|
||||||
const { envelopeWhereInput } = await getEnvelopeWhereInput({
|
const { envelopeWhereInput } = await getEnvelopeWhereInput({
|
||||||
id,
|
id,
|
||||||
@@ -674,33 +667,6 @@ export const createDocumentFromTemplate = async ({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const templateAttachments = await tx.envelopeAttachment.findMany({
|
|
||||||
where: {
|
|
||||||
envelopeId: template.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const attachmentsToCreate = [
|
|
||||||
...templateAttachments.map((attachment) => ({
|
|
||||||
envelopeId: envelope.id,
|
|
||||||
type: attachment.type,
|
|
||||||
label: attachment.label,
|
|
||||||
data: attachment.data,
|
|
||||||
})),
|
|
||||||
...(attachments || []).map((attachment) => ({
|
|
||||||
envelopeId: envelope.id,
|
|
||||||
type: attachment.type || 'link',
|
|
||||||
label: attachment.label,
|
|
||||||
data: attachment.data,
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (attachmentsToCreate.length > 0) {
|
|
||||||
await tx.envelopeAttachment.createMany({
|
|
||||||
data: attachmentsToCreate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const createdEnvelope = await tx.envelope.findFirst({
|
const createdEnvelope = await tx.envelope.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: envelope.id,
|
id: envelope.id,
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZEnvelopeAttachmentTypeSchema = z.enum(['link']);
|
|
||||||
|
|
||||||
export type TEnvelopeAttachmentType = z.infer<typeof ZEnvelopeAttachmentTypeSchema>;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
|
||||||
|
|
||||||
export const isValidReturnTo = (returnTo?: string) => {
|
|
||||||
if (!returnTo) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Decode if it's URL encoded
|
|
||||||
const decodedReturnTo = decodeURIComponent(returnTo);
|
|
||||||
const returnToUrl = new URL(decodedReturnTo, NEXT_PUBLIC_WEBAPP_URL());
|
|
||||||
|
|
||||||
if (returnToUrl.origin !== NEXT_PUBLIC_WEBAPP_URL()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const normalizeReturnTo = (returnTo?: string) => {
|
|
||||||
if (!returnTo) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Decode if it's URL encoded
|
|
||||||
const decodedReturnTo = decodeURIComponent(returnTo);
|
|
||||||
const returnToUrl = new URL(decodedReturnTo, NEXT_PUBLIC_WEBAPP_URL());
|
|
||||||
|
|
||||||
return `${returnToUrl.pathname}${returnToUrl.search}${returnToUrl.hash}`;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "EnvelopeAttachment" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"type" TEXT NOT NULL,
|
|
||||||
"label" TEXT NOT NULL,
|
|
||||||
"data" TEXT NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
"envelopeId" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "EnvelopeAttachment_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "EnvelopeAttachment" ADD CONSTRAINT "EnvelopeAttachment_envelopeId_fkey" FOREIGN KEY ("envelopeId") REFERENCES "Envelope"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -422,8 +422,6 @@ model Envelope {
|
|||||||
|
|
||||||
documentMetaId String @unique
|
documentMetaId String @unique
|
||||||
documentMeta DocumentMeta @relation(fields: [documentMetaId], references: [id])
|
documentMeta DocumentMeta @relation(fields: [documentMetaId], references: [id])
|
||||||
|
|
||||||
envelopeAttachments EnvelopeAttachment[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model EnvelopeItem {
|
model EnvelopeItem {
|
||||||
@@ -510,22 +508,6 @@ model DocumentMeta {
|
|||||||
envelope Envelope?
|
envelope Envelope?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @zod.import(["import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';"])
|
|
||||||
model EnvelopeAttachment {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
|
|
||||||
type String /// [EnvelopeAttachmentType] @zod.custom.use(ZEnvelopeAttachmentTypeSchema)
|
|
||||||
label String
|
|
||||||
|
|
||||||
data String
|
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
envelopeId String
|
|
||||||
envelope Envelope @relation(fields: [envelopeId], references: [id], onDelete: Cascade)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ReadStatus {
|
enum ReadStatus {
|
||||||
NOT_OPENED
|
NOT_OPENED
|
||||||
OPENED
|
OPENED
|
||||||
|
|||||||
3
packages/prisma/types/types.d.ts
vendored
3
packages/prisma/types/types.d.ts
vendored
@@ -5,7 +5,6 @@ import type {
|
|||||||
} from '@documenso/lib/types/document-auth';
|
} from '@documenso/lib/types/document-auth';
|
||||||
import type { TDocumentEmailSettings } from '@documenso/lib/types/document-email';
|
import type { TDocumentEmailSettings } from '@documenso/lib/types/document-email';
|
||||||
import type { TDocumentFormValues } from '@documenso/lib/types/document-form-values';
|
import type { TDocumentFormValues } from '@documenso/lib/types/document-form-values';
|
||||||
import type { TEnvelopeAttachmentType } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
import type { TFieldMetaNotOptionalSchema } from '@documenso/lib/types/field-meta';
|
import type { TFieldMetaNotOptionalSchema } from '@documenso/lib/types/field-meta';
|
||||||
import type { TClaimFlags } from '@documenso/lib/types/subscription';
|
import type { TClaimFlags } from '@documenso/lib/types/subscription';
|
||||||
|
|
||||||
@@ -24,8 +23,6 @@ declare global {
|
|||||||
type RecipientAuthOptions = TRecipientAuthOptions;
|
type RecipientAuthOptions = TRecipientAuthOptions;
|
||||||
|
|
||||||
type FieldMeta = TFieldMetaNotOptionalSchema;
|
type FieldMeta = TFieldMetaNotOptionalSchema;
|
||||||
|
|
||||||
type EnvelopeAttachmentType = TEnvelopeAttachmentType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import { EnvelopeType } from '@prisma/client';
|
|
||||||
|
|
||||||
import { createAttachment } from '@documenso/lib/server-only/envelope-attachment/create-attachment';
|
|
||||||
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZCreateAttachmentRequestSchema,
|
|
||||||
ZCreateAttachmentResponseSchema,
|
|
||||||
} from './create-attachment.types';
|
|
||||||
|
|
||||||
export const createAttachmentRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/document/attachment/create',
|
|
||||||
summary: 'Create attachment',
|
|
||||||
description: 'Create a new attachment for a document',
|
|
||||||
tags: ['Document'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZCreateAttachmentRequestSchema)
|
|
||||||
.output(ZCreateAttachmentResponseSchema)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
const { documentId, data } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { documentId, label: data.label },
|
|
||||||
});
|
|
||||||
|
|
||||||
const envelope = await getEnvelopeById({
|
|
||||||
id: {
|
|
||||||
type: 'documentId',
|
|
||||||
id: documentId,
|
|
||||||
},
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
type: EnvelopeType.DOCUMENT,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createAttachment({
|
|
||||||
envelopeId: envelope.id,
|
|
||||||
teamId,
|
|
||||||
userId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZCreateAttachmentRequestSchema = z.object({
|
|
||||||
documentId: z.number(),
|
|
||||||
data: z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZCreateAttachmentResponseSchema = z.void();
|
|
||||||
|
|
||||||
export type TCreateAttachmentRequest = z.infer<typeof ZCreateAttachmentRequestSchema>;
|
|
||||||
export type TCreateAttachmentResponse = z.infer<typeof ZCreateAttachmentResponseSchema>;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { deleteAttachment } from '@documenso/lib/server-only/envelope-attachment/delete-attachment';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZDeleteAttachmentRequestSchema,
|
|
||||||
ZDeleteAttachmentResponseSchema,
|
|
||||||
} from './delete-attachment.types';
|
|
||||||
|
|
||||||
export const deleteAttachmentRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/document/attachment/delete',
|
|
||||||
summary: 'Delete attachment',
|
|
||||||
description: 'Delete an attachment from a document',
|
|
||||||
tags: ['Document'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZDeleteAttachmentRequestSchema)
|
|
||||||
.output(ZDeleteAttachmentResponseSchema)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
const { id } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { id },
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAttachment({
|
|
||||||
id,
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZDeleteAttachmentRequestSchema = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZDeleteAttachmentResponseSchema = z.void();
|
|
||||||
|
|
||||||
export type TDeleteAttachmentRequest = z.infer<typeof ZDeleteAttachmentRequestSchema>;
|
|
||||||
export type TDeleteAttachmentResponse = z.infer<typeof ZDeleteAttachmentResponseSchema>;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { EnvelopeType } from '@prisma/client';
|
|
||||||
|
|
||||||
import { findAttachmentsByEnvelopeId } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-envelope-id';
|
|
||||||
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZFindAttachmentsRequestSchema,
|
|
||||||
ZFindAttachmentsResponseSchema,
|
|
||||||
} from './find-attachments.types';
|
|
||||||
|
|
||||||
export const findAttachmentsRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'GET',
|
|
||||||
path: '/document/attachment',
|
|
||||||
summary: 'Find attachments',
|
|
||||||
description: 'Find all attachments for a document',
|
|
||||||
tags: ['Document'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZFindAttachmentsRequestSchema)
|
|
||||||
.output(ZFindAttachmentsResponseSchema)
|
|
||||||
.query(async ({ input, ctx }) => {
|
|
||||||
const { documentId } = input;
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { documentId },
|
|
||||||
});
|
|
||||||
|
|
||||||
const envelope = await getEnvelopeById({
|
|
||||||
id: {
|
|
||||||
type: 'documentId',
|
|
||||||
id: documentId,
|
|
||||||
},
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
type: EnvelopeType.DOCUMENT,
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await findAttachmentsByEnvelopeId({
|
|
||||||
envelopeId: envelope.id,
|
|
||||||
teamId,
|
|
||||||
userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
|
|
||||||
export const ZFindAttachmentsRequestSchema = z.object({
|
|
||||||
documentId: z.number(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZFindAttachmentsResponseSchema = z.object({
|
|
||||||
data: z.array(
|
|
||||||
z.object({
|
|
||||||
id: z.string(),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema,
|
|
||||||
label: z.string(),
|
|
||||||
data: z.string(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TFindAttachmentsRequest = z.infer<typeof ZFindAttachmentsRequestSchema>;
|
|
||||||
export type TFindAttachmentsResponse = z.infer<typeof ZFindAttachmentsResponseSchema>;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { updateAttachment } from '@documenso/lib/server-only/envelope-attachment/update-attachment';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZUpdateAttachmentRequestSchema,
|
|
||||||
ZUpdateAttachmentResponseSchema,
|
|
||||||
} from './update-attachment.types';
|
|
||||||
|
|
||||||
export const updateAttachmentRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/document/attachment/update',
|
|
||||||
summary: 'Update attachment',
|
|
||||||
description: 'Update an existing attachment',
|
|
||||||
tags: ['Document'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZUpdateAttachmentRequestSchema)
|
|
||||||
.output(ZUpdateAttachmentResponseSchema)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
const { id, data } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { id },
|
|
||||||
});
|
|
||||||
|
|
||||||
await updateAttachment({
|
|
||||||
id,
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZUpdateAttachmentRequestSchema = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
data: z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZUpdateAttachmentResponseSchema = z.void();
|
|
||||||
|
|
||||||
export type TUpdateAttachmentRequest = z.infer<typeof ZUpdateAttachmentRequestSchema>;
|
|
||||||
export type TUpdateAttachmentResponse = z.infer<typeof ZUpdateAttachmentResponseSchema>;
|
|
||||||
@@ -37,7 +37,6 @@ export const createDocumentTemporaryRoute = authenticatedProcedure
|
|||||||
recipients,
|
recipients,
|
||||||
meta,
|
meta,
|
||||||
folderId,
|
folderId,
|
||||||
attachments,
|
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
const { remaining } = await getServerLimits({ userId: user.id, teamId });
|
const { remaining } = await getServerLimits({ userId: user.id, teamId });
|
||||||
@@ -87,7 +86,6 @@ export const createDocumentTemporaryRoute = authenticatedProcedure
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
attachments,
|
|
||||||
meta: {
|
meta: {
|
||||||
...meta,
|
...meta,
|
||||||
emailSettings: meta?.emailSettings ?? undefined,
|
emailSettings: meta?.emailSettings ?? undefined,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
} from '@documenso/lib/types/document-auth';
|
} from '@documenso/lib/types/document-auth';
|
||||||
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
||||||
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
|
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
import {
|
import {
|
||||||
ZFieldHeightSchema,
|
ZFieldHeightSchema,
|
||||||
ZFieldPageNumberSchema,
|
ZFieldPageNumberSchema,
|
||||||
@@ -69,15 +68,6 @@ export const ZCreateDocumentTemporaryRequestSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
.optional(),
|
|
||||||
attachments: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
.optional(),
|
||||||
meta: ZDocumentMetaCreateSchema.optional(),
|
meta: ZDocumentMetaCreateSchema.optional(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const createDocumentRoute = authenticatedProcedure
|
|||||||
.output(ZCreateDocumentResponseSchema)
|
.output(ZCreateDocumentResponseSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const { user, teamId } = ctx;
|
const { user, teamId } = ctx;
|
||||||
const { title, documentDataId, timezone, folderId, attachments } = input;
|
const { title, documentDataId, timezone, folderId } = input;
|
||||||
|
|
||||||
ctx.logger.info({
|
ctx.logger.info({
|
||||||
input: {
|
input: {
|
||||||
@@ -48,7 +48,6 @@ export const createDocumentRoute = authenticatedProcedure
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
attachments,
|
|
||||||
normalizePdf: true,
|
normalizePdf: true,
|
||||||
requestMetadata: ctx.metadata,
|
requestMetadata: ctx.metadata,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { ZDocumentMetaTimezoneSchema } from '@documenso/lib/types/document-meta';
|
import { ZDocumentMetaTimezoneSchema } from '@documenso/lib/types/document-meta';
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
|
|
||||||
import { ZDocumentTitleSchema } from './schema';
|
import { ZDocumentTitleSchema } from './schema';
|
||||||
|
|
||||||
@@ -20,15 +19,6 @@ export const ZCreateDocumentRequestSchema = z.object({
|
|||||||
documentDataId: z.string().min(1),
|
documentDataId: z.string().min(1),
|
||||||
timezone: ZDocumentMetaTimezoneSchema.optional(),
|
timezone: ZDocumentMetaTimezoneSchema.optional(),
|
||||||
folderId: z.string().describe('The ID of the folder to create the document in').optional(),
|
folderId: z.string().describe('The ID of the folder to create the document in').optional(),
|
||||||
attachments: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ZCreateDocumentResponseSchema = z.object({
|
export const ZCreateDocumentResponseSchema = z.object({
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { router } from '../trpc';
|
import { router } from '../trpc';
|
||||||
import { accessAuthRequest2FAEmailRoute } from './access-auth-request-2fa-email';
|
import { accessAuthRequest2FAEmailRoute } from './access-auth-request-2fa-email';
|
||||||
import { createAttachmentRoute } from './attachment/create-attachment';
|
|
||||||
import { deleteAttachmentRoute } from './attachment/delete-attachment';
|
|
||||||
import { findAttachmentsRoute } from './attachment/find-attachments';
|
|
||||||
import { updateAttachmentRoute } from './attachment/update-attachment';
|
|
||||||
import { createDocumentRoute } from './create-document';
|
import { createDocumentRoute } from './create-document';
|
||||||
import { createDocumentTemporaryRoute } from './create-document-temporary';
|
import { createDocumentTemporaryRoute } from './create-document-temporary';
|
||||||
import { deleteDocumentRoute } from './delete-document';
|
import { deleteDocumentRoute } from './delete-document';
|
||||||
@@ -57,10 +53,4 @@ export const documentRouter = router({
|
|||||||
find: findInboxRoute,
|
find: findInboxRoute,
|
||||||
getCount: getInboxCountRoute,
|
getCount: getInboxCountRoute,
|
||||||
}),
|
}),
|
||||||
attachment: {
|
|
||||||
create: createAttachmentRoute,
|
|
||||||
update: updateAttachmentRoute,
|
|
||||||
delete: deleteAttachmentRoute,
|
|
||||||
find: findAttachmentsRoute,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { createAttachment } from '@documenso/lib/server-only/envelope-attachment/create-attachment';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZCreateAttachmentRequestSchema,
|
|
||||||
ZCreateAttachmentResponseSchema,
|
|
||||||
} from './create-attachment.types';
|
|
||||||
|
|
||||||
export const createAttachmentRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/envelope/attachment/create',
|
|
||||||
summary: 'Create attachment',
|
|
||||||
description: 'Create a new attachment for an envelope',
|
|
||||||
tags: ['Envelope'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZCreateAttachmentRequestSchema)
|
|
||||||
.output(ZCreateAttachmentResponseSchema)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
const { envelopeId, data } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { envelopeId, label: data.label },
|
|
||||||
});
|
|
||||||
|
|
||||||
await createAttachment({
|
|
||||||
envelopeId,
|
|
||||||
teamId,
|
|
||||||
userId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZCreateAttachmentRequestSchema = z.object({
|
|
||||||
envelopeId: z.string(),
|
|
||||||
data: z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZCreateAttachmentResponseSchema = z.void();
|
|
||||||
|
|
||||||
export type TCreateAttachmentRequest = z.infer<typeof ZCreateAttachmentRequestSchema>;
|
|
||||||
export type TCreateAttachmentResponse = z.infer<typeof ZCreateAttachmentResponseSchema>;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { deleteAttachment } from '@documenso/lib/server-only/envelope-attachment/delete-attachment';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZDeleteAttachmentRequestSchema,
|
|
||||||
ZDeleteAttachmentResponseSchema,
|
|
||||||
} from './delete-attachment.types';
|
|
||||||
|
|
||||||
export const deleteAttachmentRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/envelope/attachment/delete',
|
|
||||||
summary: 'Delete attachment',
|
|
||||||
description: 'Delete an attachment from an envelope',
|
|
||||||
tags: ['Envelope'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZDeleteAttachmentRequestSchema)
|
|
||||||
.output(ZDeleteAttachmentResponseSchema)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
const { id } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { id },
|
|
||||||
});
|
|
||||||
|
|
||||||
await deleteAttachment({
|
|
||||||
id,
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZDeleteAttachmentRequestSchema = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZDeleteAttachmentResponseSchema = z.void();
|
|
||||||
|
|
||||||
export type TDeleteAttachmentRequest = z.infer<typeof ZDeleteAttachmentRequestSchema>;
|
|
||||||
export type TDeleteAttachmentResponse = z.infer<typeof ZDeleteAttachmentResponseSchema>;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
||||||
import { findAttachmentsByEnvelopeId } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-envelope-id';
|
|
||||||
import { findAttachmentsByToken } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-token';
|
|
||||||
|
|
||||||
import { procedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZFindAttachmentsRequestSchema,
|
|
||||||
ZFindAttachmentsResponseSchema,
|
|
||||||
} from './find-attachments.types';
|
|
||||||
|
|
||||||
export const findAttachmentsRoute = procedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'GET',
|
|
||||||
path: '/envelope/attachment',
|
|
||||||
summary: 'Find attachments',
|
|
||||||
description: 'Find all attachments for an envelope',
|
|
||||||
tags: ['Envelope'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZFindAttachmentsRequestSchema)
|
|
||||||
.output(ZFindAttachmentsResponseSchema)
|
|
||||||
.query(async ({ input, ctx }) => {
|
|
||||||
const { envelopeId, token } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { envelopeId },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
const data = await findAttachmentsByToken({ envelopeId, token });
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId || !teamId) {
|
|
||||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
|
||||||
message: 'You must be authenticated to access this resource',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await findAttachmentsByEnvelopeId({ envelopeId, teamId, userId });
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
|
|
||||||
export const ZFindAttachmentsRequestSchema = z.object({
|
|
||||||
envelopeId: z.string(),
|
|
||||||
token: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZFindAttachmentsResponseSchema = z.object({
|
|
||||||
data: z.array(
|
|
||||||
z.object({
|
|
||||||
id: z.string(),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema,
|
|
||||||
label: z.string(),
|
|
||||||
data: z.string(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TFindAttachmentsRequest = z.infer<typeof ZFindAttachmentsRequestSchema>;
|
|
||||||
export type TFindAttachmentsResponse = z.infer<typeof ZFindAttachmentsResponseSchema>;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { updateAttachment } from '@documenso/lib/server-only/envelope-attachment/update-attachment';
|
|
||||||
|
|
||||||
import { authenticatedProcedure } from '../../trpc';
|
|
||||||
import {
|
|
||||||
ZUpdateAttachmentRequestSchema,
|
|
||||||
ZUpdateAttachmentResponseSchema,
|
|
||||||
} from './update-attachment.types';
|
|
||||||
|
|
||||||
export const updateAttachmentRoute = authenticatedProcedure
|
|
||||||
.meta({
|
|
||||||
openapi: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/envelope/attachment/update',
|
|
||||||
summary: 'Update attachment',
|
|
||||||
description: 'Update an existing attachment',
|
|
||||||
tags: ['Envelope'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.input(ZUpdateAttachmentRequestSchema)
|
|
||||||
.output(ZUpdateAttachmentResponseSchema)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { teamId } = ctx;
|
|
||||||
const userId = ctx.user.id;
|
|
||||||
|
|
||||||
const { id, data } = input;
|
|
||||||
|
|
||||||
ctx.logger.info({
|
|
||||||
input: { id },
|
|
||||||
});
|
|
||||||
|
|
||||||
await updateAttachment({
|
|
||||||
id,
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
export const ZUpdateAttachmentRequestSchema = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
data: z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZUpdateAttachmentResponseSchema = z.void();
|
|
||||||
|
|
||||||
export type TUpdateAttachmentRequest = z.infer<typeof ZUpdateAttachmentRequestSchema>;
|
|
||||||
export type TUpdateAttachmentResponse = z.infer<typeof ZUpdateAttachmentResponseSchema>;
|
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
} from './create-envelope.types';
|
} from './create-envelope.types';
|
||||||
|
|
||||||
export const createEnvelopeRoute = authenticatedProcedure
|
export const createEnvelopeRoute = authenticatedProcedure
|
||||||
.input(ZCreateEnvelopeRequestSchema)
|
.input(ZCreateEnvelopeRequestSchema) // Note: Before releasing this to public, update the response schema to be correct.
|
||||||
.output(ZCreateEnvelopeResponseSchema)
|
.output(ZCreateEnvelopeResponseSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const { user, teamId } = ctx;
|
const { user, teamId } = ctx;
|
||||||
@@ -24,7 +24,6 @@ export const createEnvelopeRoute = authenticatedProcedure
|
|||||||
folderId,
|
folderId,
|
||||||
items,
|
items,
|
||||||
meta,
|
meta,
|
||||||
attachments,
|
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
ctx.logger.info({
|
ctx.logger.info({
|
||||||
@@ -58,7 +57,6 @@ export const createEnvelopeRoute = authenticatedProcedure
|
|||||||
folderId,
|
folderId,
|
||||||
envelopeItems: items,
|
envelopeItems: items,
|
||||||
},
|
},
|
||||||
attachments,
|
|
||||||
meta,
|
meta,
|
||||||
normalizePdf: true,
|
normalizePdf: true,
|
||||||
requestMetadata: ctx.metadata,
|
requestMetadata: ctx.metadata,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
} from '@documenso/lib/types/document-auth';
|
} from '@documenso/lib/types/document-auth';
|
||||||
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
||||||
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
|
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
import {
|
import {
|
||||||
ZFieldHeightSchema,
|
ZFieldHeightSchema,
|
||||||
ZFieldPageNumberSchema,
|
ZFieldPageNumberSchema,
|
||||||
@@ -77,15 +76,6 @@ export const ZCreateEnvelopeRequestSchema = z.object({
|
|||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
meta: ZDocumentMetaCreateSchema.optional(),
|
meta: ZDocumentMetaCreateSchema.optional(),
|
||||||
attachments: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ZCreateEnvelopeResponseSchema = z.object({
|
export const ZCreateEnvelopeResponseSchema = z.object({
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import { router } from '../trpc';
|
import { router } from '../trpc';
|
||||||
import { createAttachmentRoute } from './attachment/create-attachment';
|
|
||||||
import { deleteAttachmentRoute } from './attachment/delete-attachment';
|
|
||||||
import { findAttachmentsRoute } from './attachment/find-attachments';
|
|
||||||
import { updateAttachmentRoute } from './attachment/update-attachment';
|
|
||||||
import { createEnvelopeRoute } from './create-envelope';
|
import { createEnvelopeRoute } from './create-envelope';
|
||||||
import { createEnvelopeItemsRoute } from './create-envelope-items';
|
import { createEnvelopeItemsRoute } from './create-envelope-items';
|
||||||
import { deleteEnvelopeRoute } from './delete-envelope';
|
import { deleteEnvelopeRoute } from './delete-envelope';
|
||||||
@@ -39,10 +35,4 @@ export const envelopeRouter = router({
|
|||||||
set: setEnvelopeFieldsRoute,
|
set: setEnvelopeFieldsRoute,
|
||||||
sign: signEnvelopeFieldRoute,
|
sign: signEnvelopeFieldRoute,
|
||||||
},
|
},
|
||||||
attachment: {
|
|
||||||
find: findAttachmentsRoute,
|
|
||||||
create: createAttachmentRoute,
|
|
||||||
update: updateAttachmentRoute,
|
|
||||||
delete: deleteAttachmentRoute,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,11 +52,6 @@ export const getOrganisation = async ({
|
|||||||
organisationGlobalSettings: true,
|
organisationGlobalSettings: true,
|
||||||
subscription: true,
|
subscription: true,
|
||||||
organisationClaim: true,
|
organisationClaim: true,
|
||||||
members: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
teams: {
|
teams: {
|
||||||
where: {
|
where: {
|
||||||
teamGroups: {
|
teamGroups: {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { z } from 'zod';
|
|||||||
import { ZOrganisationSchema } from '@documenso/lib/types/organisation';
|
import { ZOrganisationSchema } from '@documenso/lib/types/organisation';
|
||||||
import OrganisationClaimSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationClaimSchema';
|
import OrganisationClaimSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationClaimSchema';
|
||||||
import OrganisationGlobalSettingsSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationGlobalSettingsSchema';
|
import OrganisationGlobalSettingsSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationGlobalSettingsSchema';
|
||||||
import OrganisationMemberSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationMemberSchema';
|
|
||||||
import SubscriptionSchema from '@documenso/prisma/generated/zod/modelSchema/SubscriptionSchema';
|
import SubscriptionSchema from '@documenso/prisma/generated/zod/modelSchema/SubscriptionSchema';
|
||||||
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
|
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
|
||||||
|
|
||||||
@@ -25,11 +24,6 @@ export const ZGetOrganisationResponseSchema = ZOrganisationSchema.extend({
|
|||||||
organisationGlobalSettings: OrganisationGlobalSettingsSchema,
|
organisationGlobalSettings: OrganisationGlobalSettingsSchema,
|
||||||
organisationClaim: OrganisationClaimSchema,
|
organisationClaim: OrganisationClaimSchema,
|
||||||
subscription: SubscriptionSchema.nullable(),
|
subscription: SubscriptionSchema.nullable(),
|
||||||
members: z.array(
|
|
||||||
OrganisationMemberSchema.pick({
|
|
||||||
id: true,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
teams: z.array(
|
teams: z.array(
|
||||||
TeamSchema.pick({
|
TeamSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
|
|||||||
@@ -235,7 +235,6 @@ export const templateRouter = router({
|
|||||||
publicDescription,
|
publicDescription,
|
||||||
type,
|
type,
|
||||||
meta,
|
meta,
|
||||||
attachments,
|
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
const fileName = title.endsWith('.pdf') ? title : `${title}.pdf`;
|
const fileName = title.endsWith('.pdf') ? title : `${title}.pdf`;
|
||||||
@@ -269,7 +268,6 @@ export const templateRouter = router({
|
|||||||
publicDescription,
|
publicDescription,
|
||||||
},
|
},
|
||||||
meta,
|
meta,
|
||||||
attachments,
|
|
||||||
requestMetadata: ctx.metadata,
|
requestMetadata: ctx.metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
ZDocumentMetaTypedSignatureEnabledSchema,
|
ZDocumentMetaTypedSignatureEnabledSchema,
|
||||||
ZDocumentMetaUploadSignatureEnabledSchema,
|
ZDocumentMetaUploadSignatureEnabledSchema,
|
||||||
} from '@documenso/lib/types/document-meta';
|
} from '@documenso/lib/types/document-meta';
|
||||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
|
||||||
import { ZFieldMetaPrefillFieldsSchema } from '@documenso/lib/types/field-meta';
|
import { ZFieldMetaPrefillFieldsSchema } from '@documenso/lib/types/field-meta';
|
||||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||||
import {
|
import {
|
||||||
@@ -198,15 +197,6 @@ export const ZCreateTemplateV2RequestSchema = z.object({
|
|||||||
publicDescription: ZTemplatePublicDescriptionSchema.optional(),
|
publicDescription: ZTemplatePublicDescriptionSchema.optional(),
|
||||||
type: z.nativeEnum(TemplateType).optional(),
|
type: z.nativeEnum(TemplateType).optional(),
|
||||||
meta: ZTemplateMetaUpsertSchema.optional(),
|
meta: ZTemplateMetaUpsertSchema.optional(),
|
||||||
attachments: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
label: z.string().min(1, 'Label is required'),
|
|
||||||
data: z.string().url('Must be a valid URL'),
|
|
||||||
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user