diff --git a/apps/marketing/src/app/(marketing)/singleplayer/[token]/success/page.tsx b/apps/marketing/src/app/(marketing)/singleplayer/[token]/success/page.tsx
index 6e02a470f..fbf020c38 100644
--- a/apps/marketing/src/app/(marketing)/singleplayer/[token]/success/page.tsx
+++ b/apps/marketing/src/app/(marketing)/singleplayer/[token]/success/page.tsx
@@ -1,6 +1,7 @@
import { notFound } from 'next/navigation';
import { getDocumentAndRecipientByToken } from '@documenso/lib/server-only/document/get-document-by-token';
+import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures';
import { DocumentStatus } from '@documenso/prisma/client';
import { SinglePlayerModeSuccess } from '~/components/(marketing)/single-player-mode/single-player-mode-success';
@@ -26,5 +27,7 @@ export default async function SinglePlayerModeSuccessPage({
return notFound();
}
- return ;
+ const signatures = await getRecipientSignatures({ recipientId: document.Recipient.id });
+
+ return ;
}
diff --git a/apps/marketing/src/app/(marketing)/singleplayer/client.tsx b/apps/marketing/src/app/(marketing)/singleplayer/client.tsx
index 747b77a15..7b500d295 100644
--- a/apps/marketing/src/app/(marketing)/singleplayer/client.tsx
+++ b/apps/marketing/src/app/(marketing)/singleplayer/client.tsx
@@ -159,11 +159,11 @@ export const SinglePlayerClient = () => {
const onFileDrop = async (file: File) => {
try {
const arrayBuffer = await file.arrayBuffer();
- const base64String = base64.encode(new Uint8Array(arrayBuffer));
+ const fileBase64 = base64.encode(new Uint8Array(arrayBuffer));
setUploadedFile({
file,
- fileBase64: `data:application/pdf;base64,${base64String}`,
+ fileBase64,
});
analytics.capture('Marketing: SPM - Document uploaded');
@@ -182,7 +182,15 @@ export const SinglePlayerClient = () => {
Single Player Mode
- View our{' '}
+ Create a{' '}
+
+ free account
+ {' '}
+ or view our{' '}
& { duration?: number }) {
+}: React.ComponentPropsWithoutRef & { duration?: number }) => {
const isMounted = useIsMounted();
const { width, height } = useWindowSize();
@@ -43,4 +43,4 @@ export default function ConfettiScreen({
/>,
document.body,
);
-}
+};
diff --git a/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx b/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx
index 49a9efcae..f92c64c84 100644
--- a/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx
+++ b/apps/marketing/src/components/(marketing)/single-player-mode/single-player-mode-success.tsx
@@ -5,8 +5,7 @@ import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
-import { base64 } from '@documenso/lib/universal/base64';
-import { getFile } from '@documenso/lib/universal/upload/get-file';
+import { DocumentStatus, Signature } from '@documenso/prisma/client';
import { DocumentWithRecipient } from '@documenso/prisma/types/document-with-recipient';
import DocumentDialog from '@documenso/ui/components/document/document-dialog';
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
@@ -14,53 +13,28 @@ import { DocumentShareButton } from '@documenso/ui/components/document/document-
import { SigningCard3D } from '@documenso/ui/components/signing-card';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
-import { useToast } from '@documenso/ui/primitives/use-toast';
import signingCelebration from '~/assets/signing-celebration.png';
-import ConfettiScreen from '~/components/(marketing)/confetti-screen';
-
-import { DocumentStatus } from '.prisma/client';
+import { ConfettiScreen } from '~/components/(marketing)/confetti-screen';
interface SinglePlayerModeSuccessProps {
className?: string;
document: DocumentWithRecipient;
+ signatures: Signature[];
}
-export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerModeSuccessProps) => {
+export const SinglePlayerModeSuccess = ({
+ className,
+ document,
+ signatures,
+}: SinglePlayerModeSuccessProps) => {
const { getFlag } = useFeatureFlags();
const isConfettiEnabled = getFlag('marketing_spm_confetti');
const [showDocumentDialog, setShowDocumentDialog] = useState(false);
- const [isFetchingDocumentFile, setIsFetchingDocumentFile] = useState(false);
- const [documentFile, setDocumentFile] = useState(null);
- const { toast } = useToast();
-
- const onShowDocumentClick = async () => {
- if (isFetchingDocumentFile) {
- return;
- }
-
- setIsFetchingDocumentFile(true);
-
- try {
- const data = await getFile(document.documentData);
-
- setDocumentFile(base64.encode(data));
-
- setShowDocumentDialog(true);
- } catch {
- toast({
- title: 'Something went wrong.',
- description: 'We were unable to retrieve the document at this time. Please try again.',
- variant: 'destructive',
- duration: 7500,
- });
- }
-
- setIsFetchingDocumentFile(false);
- };
+ const { documentData } = document;
useEffect(() => {
window.scrollTo({ top: 0 });
@@ -80,6 +54,7 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
@@ -99,11 +74,7 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
disabled={document.status !== DocumentStatus.COMPLETED}
/>
-
diff --git a/apps/web/src/app/(signing)/sign/[token]/complete/page.tsx b/apps/web/src/app/(signing)/sign/[token]/complete/page.tsx
index 5402d12cb..d5852ecdd 100644
--- a/apps/web/src/app/(signing)/sign/[token]/complete/page.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/complete/page.tsx
@@ -7,6 +7,7 @@ import { match } from 'ts-pattern';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
+import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures';
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
@@ -46,6 +47,8 @@ export default async function CompletedSigningPage({
return notFound();
}
+ const signatures = await getRecipientSignatures({ recipientId: recipient.id });
+
const recipientName =
recipient.name ||
fields.find((field) => field.type === FieldType.NAME)?.customText ||
@@ -54,7 +57,11 @@ export default async function CompletedSigningPage({
return (
{/* Card with recipient */}
-
+
{match(document.status)
@@ -72,7 +79,8 @@ export default async function CompletedSigningPage({
))}
- You have signed "{document.title}"
+ You have signed
+ "{document.title}"
{match(document.status)
diff --git a/package-lock.json b/package-lock.json
index 45ddd83a2..648e1ca5f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19913,7 +19913,8 @@
"react-pdf": "7.3.3",
"react-rnd": "^10.4.1",
"tailwind-merge": "^1.12.0",
- "tailwindcss-animate": "^1.0.5"
+ "tailwindcss-animate": "^1.0.5",
+ "ts-pattern": "^5.0.5"
},
"devDependencies": {
"@documenso/tailwind-config": "*",
diff --git a/packages/lib/server-only/recipient/get-recipient-signatures.ts b/packages/lib/server-only/recipient/get-recipient-signatures.ts
new file mode 100644
index 000000000..9b37e84b5
--- /dev/null
+++ b/packages/lib/server-only/recipient/get-recipient-signatures.ts
@@ -0,0 +1,15 @@
+import { prisma } from '@documenso/prisma';
+
+export type GetRecipientSignaturesOptions = {
+ recipientId: number;
+};
+
+export const getRecipientSignatures = async ({ recipientId }: GetRecipientSignaturesOptions) => {
+ return await prisma.signature.findMany({
+ where: {
+ Field: {
+ recipientId,
+ },
+ },
+ });
+};
diff --git a/packages/ui/components/document/document-dialog.tsx b/packages/ui/components/document/document-dialog.tsx
index e90147806..6099fecff 100644
--- a/packages/ui/components/document/document-dialog.tsx
+++ b/packages/ui/components/document/document-dialog.tsx
@@ -5,20 +5,20 @@ import { useState } from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { X } from 'lucide-react';
-import { DocumentDataType } from '@documenso/prisma/client';
+import { DocumentData } from '@documenso/prisma/client';
import { cn } from '../../lib/utils';
import { Dialog, DialogOverlay, DialogPortal } from '../../primitives/dialog';
import { LazyPDFViewerNoLoader } from '../../primitives/lazy-pdf-viewer';
export type DocumentDialogProps = {
- document: string;
+ documentData: DocumentData;
} & Omit
;
/**
* A dialog which renders the provided document.
*/
-export default function DocumentDialog({ document, ...props }: DocumentDialogProps) {
+export default function DocumentDialog({ documentData, ...props }: DocumentDialogProps) {
const [documentLoaded, setDocumentLoaded] = useState(false);
const onDocumentLoad = () => {
@@ -41,12 +41,7 @@ export default function DocumentDialog({ document, ...props }: DocumentDialogPro
>
e.stopPropagation()}
onDocumentLoad={onDocumentLoad}
/>
diff --git a/packages/ui/components/signing-card.tsx b/packages/ui/components/signing-card.tsx
index 3c65ba9dc..ab057c4e5 100644
--- a/packages/ui/components/signing-card.tsx
+++ b/packages/ui/components/signing-card.tsx
@@ -5,23 +5,31 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import Image, { StaticImageData } from 'next/image';
import { animate, motion, useMotionTemplate, useMotionValue, useTransform } from 'framer-motion';
+import { P, match } from 'ts-pattern';
+import { Signature } from '@documenso/prisma/client';
import { cn } from '@documenso/ui/lib/utils';
import { Card, CardContent } from '@documenso/ui/primitives/card';
export type SigningCardProps = {
className?: string;
name: string;
+ signature?: Signature;
signingCelebrationImage?: StaticImageData;
};
/**
* 2D signing card.
*/
-export const SigningCard = ({ className, name, signingCelebrationImage }: SigningCardProps) => {
+export const SigningCard = ({
+ className,
+ name,
+ signature,
+ signingCelebrationImage,
+}: SigningCardProps) => {
return (
-
+
{signingCelebrationImage && (
@@ -33,7 +41,12 @@ export const SigningCard = ({ className, name, signingCelebrationImage }: Signin
/**
* 3D signing card that follows the mouse movement within a certain range.
*/
-export const SigningCard3D = ({ className, name, signingCelebrationImage }: SigningCardProps) => {
+export const SigningCard3D = ({
+ className,
+ name,
+ signature,
+ signingCelebrationImage,
+}: SigningCardProps) => {
// Should use % based dimensions by calculating the window height/width.
const boundary = 400;
@@ -130,7 +143,7 @@ export const SigningCard3D = ({ className, name, signingCelebrationImage }: Sign
rotateY,
}}
>
-
+
{signingCelebrationImage && (
@@ -142,10 +155,11 @@ export const SigningCard3D = ({ className, name, signingCelebrationImage }: Sign
type SigningCardContentProps = {
name: string;
+ signature?: Signature;
className?: string;
};
-const SigningCardContent = ({ className, name }: SigningCardContentProps) => {
+const SigningCardContent = ({ className, name, signature }: SigningCardContentProps) => {
return (
{
container: 'main',
}}
>
-
- {name}
-
+ {match(signature)
+ .with({ signatureImageAsBase64: P.string }, (signature) => (
+
+ ))
+ .with({ typedSignature: P.string }, (signature) => (
+
+ {signature.typedSignature}
+
+ ))
+ .otherwise(() => (
+
+ {name}
+
+ ))}
);
diff --git a/packages/ui/package.json b/packages/ui/package.json
index c92cbbff2..c317f7e48 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -67,6 +67,7 @@
"react-pdf": "7.3.3",
"react-rnd": "^10.4.1",
"tailwind-merge": "^1.12.0",
- "tailwindcss-animate": "^1.0.5"
+ "tailwindcss-animate": "^1.0.5",
+ "ts-pattern": "^5.0.5"
}
}