mirror of
https://github.com/documenso/documenso.git
synced 2025-11-19 19:21:39 +10:00
@ -22,7 +22,9 @@ export default function RecipientLayout({ matches }: Route.ComponentProps) {
|
||||
|
||||
// Hide the header for signing routes.
|
||||
const hideHeader = matches.some(
|
||||
(match) => match?.id === 'routes/_recipient+/sign.$token+/_index',
|
||||
(match) =>
|
||||
match?.id === 'routes/_recipient+/sign.$token+/_index' ||
|
||||
match?.id === 'routes/_recipient+/d.$token+/_index',
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -4,20 +4,29 @@ import { redirect } from 'react-router';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import { EnvelopeRenderProvider } from '@documenso/lib/client-only/providers/envelope-render-provider';
|
||||
import { useOptionalSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeForDirectTemplateSigning } from '@documenso/lib/server-only/envelope/get-envelope-for-direct-template-signing';
|
||||
import { getEnvelopeRequiredAccessData } from '@documenso/lib/server-only/envelope/get-envelope-required-access-data';
|
||||
import { getTemplateByDirectLinkToken } from '@documenso/lib/server-only/template/get-template-by-direct-link-token';
|
||||
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
||||
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { Header as AuthenticatedHeader } from '~/components/general/app-header';
|
||||
import { DirectTemplatePageView } from '~/components/general/direct-template/direct-template-page';
|
||||
import { DirectTemplateAuthPageView } from '~/components/general/direct-template/direct-template-signing-auth-page';
|
||||
import { DocumentSigningAuthPageView } from '~/components/general/document-signing/document-signing-auth-page';
|
||||
import { DocumentSigningAuthProvider } from '~/components/general/document-signing/document-signing-auth-provider';
|
||||
import { DocumentSigningPageViewV2 } from '~/components/general/document-signing/document-signing-page-view-v2';
|
||||
import { DocumentSigningProvider } from '~/components/general/document-signing/document-signing-provider';
|
||||
import { EnvelopeSigningProvider } from '~/components/general/document-signing/envelope-signing-provider';
|
||||
import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
|
||||
|
||||
import type { Route } from './+types/_index';
|
||||
|
||||
export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
const handleV1Loader = async ({ params, request }: Route.LoaderArgs) => {
|
||||
const session = await getOptionalSession(request);
|
||||
|
||||
const { token } = params;
|
||||
@ -55,27 +64,111 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
);
|
||||
|
||||
if (!isAccessAuthValid) {
|
||||
return superLoaderJson({
|
||||
return {
|
||||
isAccessAuthValid: false as const,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return superLoaderJson({
|
||||
return {
|
||||
isAccessAuthValid: true,
|
||||
template: {
|
||||
...template,
|
||||
folder: null,
|
||||
},
|
||||
directTemplateRecipient,
|
||||
} as const;
|
||||
};
|
||||
|
||||
const handleV2Loader = async ({ params, request }: Route.LoaderArgs) => {
|
||||
const session = await getOptionalSession(request);
|
||||
|
||||
const { token } = params;
|
||||
|
||||
if (!token) {
|
||||
throw redirect('/');
|
||||
}
|
||||
|
||||
return await getEnvelopeForDirectTemplateSigning({
|
||||
token,
|
||||
userId: session?.user?.id,
|
||||
})
|
||||
.then((envelopeForSigning) => {
|
||||
return {
|
||||
isDocumentAccessValid: true,
|
||||
envelopeForSigning,
|
||||
} as const;
|
||||
})
|
||||
.catch(async (e) => {
|
||||
const error = AppError.parseError(e);
|
||||
|
||||
if (error.code === AppErrorCode.UNAUTHORIZED) {
|
||||
const requiredAccessData = await getEnvelopeRequiredAccessData({ token });
|
||||
|
||||
return {
|
||||
isDocumentAccessValid: false,
|
||||
...requiredAccessData,
|
||||
} as const;
|
||||
}
|
||||
|
||||
throw new Response('Not Found', { status: 404 });
|
||||
});
|
||||
};
|
||||
|
||||
export async function loader(loaderArgs: Route.LoaderArgs) {
|
||||
const { token } = loaderArgs.params;
|
||||
|
||||
if (!token) {
|
||||
throw redirect('/');
|
||||
}
|
||||
|
||||
const directEnvelope = await prisma.envelope.findFirst({
|
||||
where: {
|
||||
directLink: {
|
||||
enabled: true,
|
||||
token,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
internalVersion: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!directEnvelope) {
|
||||
throw new Response('Not Found', { status: 404 });
|
||||
}
|
||||
|
||||
if (directEnvelope.internalVersion === 2) {
|
||||
const payloadV2 = await handleV2Loader(loaderArgs);
|
||||
|
||||
return superLoaderJson({
|
||||
version: 2,
|
||||
payload: payloadV2,
|
||||
} as const);
|
||||
}
|
||||
|
||||
const payloadV1 = await handleV1Loader(loaderArgs);
|
||||
|
||||
return superLoaderJson({
|
||||
version: 1,
|
||||
payload: payloadV1,
|
||||
} as const);
|
||||
}
|
||||
|
||||
export default function DirectTemplatePage() {
|
||||
const { sessionData } = useOptionalSession();
|
||||
const user = sessionData?.user;
|
||||
|
||||
const data = useSuperLoaderData<typeof loader>();
|
||||
|
||||
if (data.version === 2) {
|
||||
return <DirectSigningPageV2 data={data.payload} />;
|
||||
}
|
||||
|
||||
return <DirectSigningPageV1 data={data.payload} />;
|
||||
}
|
||||
|
||||
const DirectSigningPageV1 = ({ data }: { data: Awaited<ReturnType<typeof handleV1Loader>> }) => {
|
||||
const { sessionData } = useOptionalSession();
|
||||
|
||||
const user = sessionData?.user;
|
||||
|
||||
// Should not be possible for directLink to be null.
|
||||
if (!data.isAccessAuthValid) {
|
||||
return <DirectTemplateAuthPageView />;
|
||||
@ -97,28 +190,68 @@ export default function DirectTemplatePage() {
|
||||
recipient={directTemplateRecipient}
|
||||
user={user}
|
||||
>
|
||||
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||
<h1
|
||||
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||
title={template.title}
|
||||
>
|
||||
{template.title}
|
||||
</h1>
|
||||
<>
|
||||
{sessionData?.user && <AuthenticatedHeader />}
|
||||
|
||||
<div className="text-muted-foreground mb-8 mt-2.5 flex items-center gap-x-2">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
<p className="text-muted-foreground/80">
|
||||
<Plural value={template.recipients.length} one="# recipient" other="# recipients" />
|
||||
</p>
|
||||
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||
<h1
|
||||
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||
title={template.title}
|
||||
>
|
||||
{template.title}
|
||||
</h1>
|
||||
|
||||
<div className="text-muted-foreground mb-8 mt-2.5 flex items-center gap-x-2">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
<p className="text-muted-foreground/80">
|
||||
<Plural value={template.recipients.length} one="# recipient" other="# recipients" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<DirectTemplatePageView
|
||||
directTemplateRecipient={directTemplateRecipient}
|
||||
directTemplateToken={template.directLink.token}
|
||||
template={template}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DirectTemplatePageView
|
||||
directTemplateRecipient={directTemplateRecipient}
|
||||
directTemplateToken={template.directLink.token}
|
||||
template={template}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</DocumentSigningAuthProvider>
|
||||
</DocumentSigningProvider>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const DirectSigningPageV2 = ({ data }: { data: Awaited<ReturnType<typeof handleV2Loader>> }) => {
|
||||
const { sessionData } = useOptionalSession();
|
||||
|
||||
const user = sessionData?.user;
|
||||
|
||||
if (!data.isDocumentAccessValid) {
|
||||
return (
|
||||
<DocumentSigningAuthPageView
|
||||
email={data.recipientEmail}
|
||||
emailHasAccount={!!data.recipientHasAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { envelope, recipient } = data.envelopeForSigning;
|
||||
|
||||
return (
|
||||
<EnvelopeSigningProvider
|
||||
envelopeData={data.envelopeForSigning}
|
||||
email={''} // Doing this allows us to let users change the email if they want to.
|
||||
fullName={user?.name}
|
||||
signature={user?.signature}
|
||||
>
|
||||
<DocumentSigningAuthProvider
|
||||
documentAuthOptions={envelope.authOptions}
|
||||
recipient={recipient}
|
||||
user={user}
|
||||
>
|
||||
<EnvelopeRenderProvider envelope={envelope}>
|
||||
<DocumentSigningPageViewV2 />
|
||||
</EnvelopeRenderProvider>
|
||||
</DocumentSigningAuthProvider>
|
||||
</EnvelopeSigningProvider>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user