chore: upgrade to latest next.js version (#553)
* chore: upgrade next.js * fix: canvas not found error * chore: upgrade package for marketing * feat: add isServer conditional * fix: inverse isServer condition * fix: normalize packages * fix: upgrade ee package * fix: depdency nightmares * fix: failing seed script
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -31,6 +31,7 @@ yarn-error.log*
|
|||||||
|
|
||||||
# turbo
|
# turbo
|
||||||
.turbo
|
.turbo
|
||||||
|
.turbo-cookie
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
@ -22,6 +22,14 @@ const config = {
|
|||||||
transform: 'lucide-react/dist/esm/icons/{{ kebabCase member }}',
|
transform: 'lucide-react/dist/esm/icons/{{ kebabCase member }}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
webpack: (config, { isServer }) => {
|
||||||
|
// fixes: Module not found: Can’t resolve ‘../build/Release/canvas.node’
|
||||||
|
if (isServer) {
|
||||||
|
config.resolve.alias.canvas = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"framer-motion": "^10.12.8",
|
"framer-motion": "^10.12.8",
|
||||||
"lucide-react": "^0.277.0",
|
"lucide-react": "^0.277.0",
|
||||||
"micro": "^10.0.1",
|
"micro": "^10.0.1",
|
||||||
"next": "13.4.19",
|
"next": "13.5.4",
|
||||||
"next-auth": "4.22.3",
|
"next-auth": "4.22.3",
|
||||||
"next-contentlayer": "^0.3.4",
|
"next-contentlayer": "^0.3.4",
|
||||||
"next-plausible": "^3.10.1",
|
"next-plausible": "^3.10.1",
|
||||||
@ -34,7 +34,7 @@
|
|||||||
"react-icons": "^4.11.0",
|
"react-icons": "^4.11.0",
|
||||||
"recharts": "^2.7.2",
|
"recharts": "^2.7.2",
|
||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.2.2",
|
||||||
"zod": "^3.21.4"
|
"zod": "^3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
247
apps/marketing/src/app/(marketing)/singleplayer/client.tsx
Normal file
247
apps/marketing/src/app/(marketing)/singleplayer/client.tsx
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||||
|
import { base64 } from '@documenso/lib/universal/base64';
|
||||||
|
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
||||||
|
import { Field, Prisma, Recipient } from '@documenso/prisma/client';
|
||||||
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
|
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
||||||
|
import { AddFieldsFormPartial } from '@documenso/ui/primitives/document-flow/add-fields';
|
||||||
|
import { TAddFieldsFormSchema } from '@documenso/ui/primitives/document-flow/add-fields.types';
|
||||||
|
import { AddSignatureFormPartial } from '@documenso/ui/primitives/document-flow/add-signature';
|
||||||
|
import { TAddSignatureFormSchema } from '@documenso/ui/primitives/document-flow/add-signature.types';
|
||||||
|
import {
|
||||||
|
DocumentFlowFormContainer,
|
||||||
|
DocumentFlowFormContainerHeader,
|
||||||
|
} from '@documenso/ui/primitives/document-flow/document-flow-root';
|
||||||
|
import { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/types';
|
||||||
|
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import { createSinglePlayerDocument } from '~/components/(marketing)/single-player-mode/create-single-player-document.action';
|
||||||
|
|
||||||
|
type SinglePlayerModeStep = 'fields' | 'sign';
|
||||||
|
|
||||||
|
// !: This entire file is a hack to get around failed prerendering of
|
||||||
|
// !: the Single Player Mode page. This regression was introduced during
|
||||||
|
// !: the upgrade of Next.js to v13.5.x.
|
||||||
|
export const SinglePlayerClient = () => {
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [uploadedFile, setUploadedFile] = useState<{ file: File; fileBase64: string } | null>();
|
||||||
|
|
||||||
|
const [step, setStep] = useState<SinglePlayerModeStep>('fields');
|
||||||
|
const [fields, setFields] = useState<Field[]>([]);
|
||||||
|
|
||||||
|
const documentFlow: Record<SinglePlayerModeStep, DocumentFlowStep> = {
|
||||||
|
fields: {
|
||||||
|
title: 'Add document',
|
||||||
|
description: 'Upload a document and add fields.',
|
||||||
|
stepIndex: 1,
|
||||||
|
onBackStep: uploadedFile
|
||||||
|
? () => {
|
||||||
|
setUploadedFile(null);
|
||||||
|
setFields([]);
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
onNextStep: () => setStep('sign'),
|
||||||
|
},
|
||||||
|
sign: {
|
||||||
|
title: 'Sign',
|
||||||
|
description: 'Enter your details.',
|
||||||
|
stepIndex: 2,
|
||||||
|
onBackStep: () => setStep('fields'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentDocumentFlow = documentFlow[step];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
analytics.startSessionRecording('marketing_session_recording_spm');
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
analytics.stopSessionRecording();
|
||||||
|
};
|
||||||
|
}, [analytics]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the selected fields into the local state.
|
||||||
|
*/
|
||||||
|
const onFieldsSubmit = (data: TAddFieldsFormSchema) => {
|
||||||
|
if (!uploadedFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFields(
|
||||||
|
data.fields.map((field, i) => ({
|
||||||
|
id: i,
|
||||||
|
documentId: -1,
|
||||||
|
recipientId: -1,
|
||||||
|
type: field.type,
|
||||||
|
page: field.pageNumber,
|
||||||
|
positionX: new Prisma.Decimal(field.pageX),
|
||||||
|
positionY: new Prisma.Decimal(field.pageY),
|
||||||
|
width: new Prisma.Decimal(field.pageWidth),
|
||||||
|
height: new Prisma.Decimal(field.pageHeight),
|
||||||
|
customText: '',
|
||||||
|
inserted: false,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
analytics.capture('Marketing: SPM - Fields added');
|
||||||
|
|
||||||
|
documentFlow.fields.onNextStep?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload, create, sign and send the document.
|
||||||
|
*/
|
||||||
|
const onSignSubmit = async (data: TAddSignatureFormSchema) => {
|
||||||
|
if (!uploadedFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const putFileData = await putFile(uploadedFile.file);
|
||||||
|
|
||||||
|
const documentToken = await createSinglePlayerDocument({
|
||||||
|
documentData: {
|
||||||
|
type: putFileData.type,
|
||||||
|
data: putFileData.data,
|
||||||
|
},
|
||||||
|
documentName: uploadedFile.file.name,
|
||||||
|
signer: data,
|
||||||
|
fields: fields.map((field) => ({
|
||||||
|
page: field.page,
|
||||||
|
type: field.type,
|
||||||
|
positionX: field.positionX.toNumber(),
|
||||||
|
positionY: field.positionY.toNumber(),
|
||||||
|
width: field.width.toNumber(),
|
||||||
|
height: field.height.toNumber(),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
analytics.capture('Marketing: SPM - Document signed', {
|
||||||
|
signer: data.email,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push(`/singleplayer/${documentToken}/success`);
|
||||||
|
} catch {
|
||||||
|
toast({
|
||||||
|
title: 'Something went wrong',
|
||||||
|
description: 'Please try again later.',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const placeholderRecipient: Recipient = {
|
||||||
|
id: -1,
|
||||||
|
documentId: -1,
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
token: '',
|
||||||
|
expired: null,
|
||||||
|
signedAt: null,
|
||||||
|
readStatus: 'OPENED',
|
||||||
|
signingStatus: 'NOT_SIGNED',
|
||||||
|
sendStatus: 'NOT_SENT',
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFileDrop = async (file: File) => {
|
||||||
|
try {
|
||||||
|
const arrayBuffer = await file.arrayBuffer();
|
||||||
|
const base64String = base64.encode(new Uint8Array(arrayBuffer));
|
||||||
|
|
||||||
|
setUploadedFile({
|
||||||
|
file,
|
||||||
|
fileBase64: `data:application/pdf;base64,${base64String}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
analytics.capture('Marketing: SPM - Document uploaded');
|
||||||
|
} catch {
|
||||||
|
toast({
|
||||||
|
title: 'Something went wrong',
|
||||||
|
description: 'Please try again later.',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-6 sm:mt-12">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-3xl font-bold lg:text-5xl">Single Player Mode</h1>
|
||||||
|
|
||||||
|
<p className="text-foreground mx-auto mt-4 max-w-[50ch] text-lg leading-normal">
|
||||||
|
View our{' '}
|
||||||
|
<Link
|
||||||
|
href={'/pricing'}
|
||||||
|
target="_blank"
|
||||||
|
className="hover:text-foreground/80 font-semibold transition-colors"
|
||||||
|
>
|
||||||
|
community plan
|
||||||
|
</Link>{' '}
|
||||||
|
for exclusive features, including the ability to collaborate with multiple signers.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 grid w-full grid-cols-12 gap-8">
|
||||||
|
<div className="col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7">
|
||||||
|
{uploadedFile ? (
|
||||||
|
<Card gradient>
|
||||||
|
<CardContent className="p-2">
|
||||||
|
<LazyPDFViewer document={uploadedFile.fileBase64} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
<DocumentDropzone className="h-[80vh] max-h-[60rem]" onDrop={onFileDrop} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-span-12 lg:col-span-6 xl:col-span-5">
|
||||||
|
<DocumentFlowFormContainer className="top-24" onSubmit={(e) => e.preventDefault()}>
|
||||||
|
<DocumentFlowFormContainerHeader
|
||||||
|
title={currentDocumentFlow.title}
|
||||||
|
description={currentDocumentFlow.description}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Add fields to PDF page. */}
|
||||||
|
{step === 'fields' && (
|
||||||
|
<fieldset disabled={!uploadedFile} className="flex h-full flex-col">
|
||||||
|
<AddFieldsFormPartial
|
||||||
|
documentFlow={documentFlow.fields}
|
||||||
|
hideRecipients={true}
|
||||||
|
recipients={uploadedFile ? [placeholderRecipient] : []}
|
||||||
|
numberOfSteps={Object.keys(documentFlow).length}
|
||||||
|
fields={fields}
|
||||||
|
onSubmit={onFieldsSubmit}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Enter user details and signature. */}
|
||||||
|
{step === 'sign' && (
|
||||||
|
<AddSignatureFormPartial
|
||||||
|
documentFlow={documentFlow.sign}
|
||||||
|
numberOfSteps={Object.keys(documentFlow).length}
|
||||||
|
fields={fields}
|
||||||
|
onSubmit={onSignSubmit}
|
||||||
|
requireName={Boolean(fields.find((field) => field.type === 'NAME'))}
|
||||||
|
requireSignature={Boolean(fields.find((field) => field.type === 'SIGNATURE'))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DocumentFlowFormContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,244 +1,10 @@
|
|||||||
'use client';
|
import { SinglePlayerClient } from './client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
export const revalidate = 0;
|
||||||
|
|
||||||
import Link from 'next/link';
|
// !: This entire file is a hack to get around failed prerendering of
|
||||||
import { useRouter } from 'next/navigation';
|
// !: the Single Player Mode page. This regression was introduced during
|
||||||
|
// !: the upgrade of Next.js to v13.5.x.
|
||||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
export default function SingleplayerPage() {
|
||||||
import { base64 } from '@documenso/lib/universal/base64';
|
return <SinglePlayerClient />;
|
||||||
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
|
||||||
import { Field, Prisma, Recipient } from '@documenso/prisma/client';
|
|
||||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
|
||||||
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
|
||||||
import { AddFieldsFormPartial } from '@documenso/ui/primitives/document-flow/add-fields';
|
|
||||||
import { TAddFieldsFormSchema } from '@documenso/ui/primitives/document-flow/add-fields.types';
|
|
||||||
import { AddSignatureFormPartial } from '@documenso/ui/primitives/document-flow/add-signature';
|
|
||||||
import { TAddSignatureFormSchema } from '@documenso/ui/primitives/document-flow/add-signature.types';
|
|
||||||
import {
|
|
||||||
DocumentFlowFormContainer,
|
|
||||||
DocumentFlowFormContainerHeader,
|
|
||||||
} from '@documenso/ui/primitives/document-flow/document-flow-root';
|
|
||||||
import { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/types';
|
|
||||||
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
|
||||||
|
|
||||||
import { createSinglePlayerDocument } from '~/components/(marketing)/single-player-mode/create-single-player-document.action';
|
|
||||||
|
|
||||||
type SinglePlayerModeStep = 'fields' | 'sign';
|
|
||||||
|
|
||||||
export default function SinglePlayerModePage() {
|
|
||||||
const analytics = useAnalytics();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const [uploadedFile, setUploadedFile] = useState<{ file: File; fileBase64: string } | null>();
|
|
||||||
|
|
||||||
const [step, setStep] = useState<SinglePlayerModeStep>('fields');
|
|
||||||
const [fields, setFields] = useState<Field[]>([]);
|
|
||||||
|
|
||||||
const documentFlow: Record<SinglePlayerModeStep, DocumentFlowStep> = {
|
|
||||||
fields: {
|
|
||||||
title: 'Add document',
|
|
||||||
description: 'Upload a document and add fields.',
|
|
||||||
stepIndex: 1,
|
|
||||||
onBackStep: uploadedFile
|
|
||||||
? () => {
|
|
||||||
setUploadedFile(null);
|
|
||||||
setFields([]);
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
onNextStep: () => setStep('sign'),
|
|
||||||
},
|
|
||||||
sign: {
|
|
||||||
title: 'Sign',
|
|
||||||
description: 'Enter your details.',
|
|
||||||
stepIndex: 2,
|
|
||||||
onBackStep: () => setStep('fields'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentDocumentFlow = documentFlow[step];
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
analytics.startSessionRecording('marketing_session_recording_spm');
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
analytics.stopSessionRecording();
|
|
||||||
};
|
|
||||||
}, [analytics]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert the selected fields into the local state.
|
|
||||||
*/
|
|
||||||
const onFieldsSubmit = (data: TAddFieldsFormSchema) => {
|
|
||||||
if (!uploadedFile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setFields(
|
|
||||||
data.fields.map((field, i) => ({
|
|
||||||
id: i,
|
|
||||||
documentId: -1,
|
|
||||||
recipientId: -1,
|
|
||||||
type: field.type,
|
|
||||||
page: field.pageNumber,
|
|
||||||
positionX: new Prisma.Decimal(field.pageX),
|
|
||||||
positionY: new Prisma.Decimal(field.pageY),
|
|
||||||
width: new Prisma.Decimal(field.pageWidth),
|
|
||||||
height: new Prisma.Decimal(field.pageHeight),
|
|
||||||
customText: '',
|
|
||||||
inserted: false,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
analytics.capture('Marketing: SPM - Fields added');
|
|
||||||
|
|
||||||
documentFlow.fields.onNextStep?.();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload, create, sign and send the document.
|
|
||||||
*/
|
|
||||||
const onSignSubmit = async (data: TAddSignatureFormSchema) => {
|
|
||||||
if (!uploadedFile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const putFileData = await putFile(uploadedFile.file);
|
|
||||||
|
|
||||||
const documentToken = await createSinglePlayerDocument({
|
|
||||||
documentData: {
|
|
||||||
type: putFileData.type,
|
|
||||||
data: putFileData.data,
|
|
||||||
},
|
|
||||||
documentName: uploadedFile.file.name,
|
|
||||||
signer: data,
|
|
||||||
fields: fields.map((field) => ({
|
|
||||||
page: field.page,
|
|
||||||
type: field.type,
|
|
||||||
positionX: field.positionX.toNumber(),
|
|
||||||
positionY: field.positionY.toNumber(),
|
|
||||||
width: field.width.toNumber(),
|
|
||||||
height: field.height.toNumber(),
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
analytics.capture('Marketing: SPM - Document signed', {
|
|
||||||
signer: data.email,
|
|
||||||
});
|
|
||||||
|
|
||||||
router.push(`/singleplayer/${documentToken}/success`);
|
|
||||||
} catch {
|
|
||||||
toast({
|
|
||||||
title: 'Something went wrong',
|
|
||||||
description: 'Please try again later.',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const placeholderRecipient: Recipient = {
|
|
||||||
id: -1,
|
|
||||||
documentId: -1,
|
|
||||||
email: '',
|
|
||||||
name: '',
|
|
||||||
token: '',
|
|
||||||
expired: null,
|
|
||||||
signedAt: null,
|
|
||||||
readStatus: 'OPENED',
|
|
||||||
signingStatus: 'NOT_SIGNED',
|
|
||||||
sendStatus: 'NOT_SENT',
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFileDrop = async (file: File) => {
|
|
||||||
try {
|
|
||||||
const arrayBuffer = await file.arrayBuffer();
|
|
||||||
const base64String = base64.encode(new Uint8Array(arrayBuffer));
|
|
||||||
|
|
||||||
setUploadedFile({
|
|
||||||
file,
|
|
||||||
fileBase64: `data:application/pdf;base64,${base64String}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
analytics.capture('Marketing: SPM - Document uploaded');
|
|
||||||
} catch {
|
|
||||||
toast({
|
|
||||||
title: 'Something went wrong',
|
|
||||||
description: 'Please try again later.',
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mt-6 sm:mt-12">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-3xl font-bold lg:text-5xl">Single Player Mode</h1>
|
|
||||||
|
|
||||||
<p className="text-foreground mx-auto mt-4 max-w-[50ch] text-lg leading-normal">
|
|
||||||
View our{' '}
|
|
||||||
<Link
|
|
||||||
href={'/pricing'}
|
|
||||||
target="_blank"
|
|
||||||
className="hover:text-foreground/80 font-semibold transition-colors"
|
|
||||||
>
|
|
||||||
community plan
|
|
||||||
</Link>{' '}
|
|
||||||
for exclusive features, including the ability to collaborate with multiple signers.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-12 grid w-full grid-cols-12 gap-8">
|
|
||||||
<div className="col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7">
|
|
||||||
{uploadedFile ? (
|
|
||||||
<Card gradient>
|
|
||||||
<CardContent className="p-2">
|
|
||||||
<LazyPDFViewer document={uploadedFile.fileBase64} />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
) : (
|
|
||||||
<DocumentDropzone className="h-[80vh] max-h-[60rem]" onDrop={onFileDrop} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-12 lg:col-span-6 xl:col-span-5">
|
|
||||||
<DocumentFlowFormContainer className="top-24" onSubmit={(e) => e.preventDefault()}>
|
|
||||||
<DocumentFlowFormContainerHeader
|
|
||||||
title={currentDocumentFlow.title}
|
|
||||||
description={currentDocumentFlow.description}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Add fields to PDF page. */}
|
|
||||||
{step === 'fields' && (
|
|
||||||
<fieldset disabled={!uploadedFile} className="flex h-full flex-col">
|
|
||||||
<AddFieldsFormPartial
|
|
||||||
documentFlow={documentFlow.fields}
|
|
||||||
hideRecipients={true}
|
|
||||||
recipients={uploadedFile ? [placeholderRecipient] : []}
|
|
||||||
numberOfSteps={Object.keys(documentFlow).length}
|
|
||||||
fields={fields}
|
|
||||||
onSubmit={onFieldsSubmit}
|
|
||||||
/>
|
|
||||||
</fieldset>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Enter user details and signature. */}
|
|
||||||
{step === 'sign' && (
|
|
||||||
<AddSignatureFormPartial
|
|
||||||
documentFlow={documentFlow.sign}
|
|
||||||
numberOfSteps={Object.keys(documentFlow).length}
|
|
||||||
fields={fields}
|
|
||||||
onSubmit={onSignSubmit}
|
|
||||||
requireName={Boolean(fields.find((field) => field.type === 'NAME'))}
|
|
||||||
requireSignature={Boolean(fields.find((field) => field.type === 'SIGNATURE'))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DocumentFlowFormContainer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,14 @@ const config = {
|
|||||||
transform: 'lucide-react/dist/esm/icons/{{ kebabCase member }}',
|
transform: 'lucide-react/dist/esm/icons/{{ kebabCase member }}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
webpack: (config, { isServer }) => {
|
||||||
|
// fixes: Module not found: Can’t resolve ‘../build/Release/canvas.node’
|
||||||
|
if (isServer) {
|
||||||
|
config.resolve.alias.canvas = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
"lucide-react": "^0.277.0",
|
"lucide-react": "^0.277.0",
|
||||||
"luxon": "^3.4.0",
|
"luxon": "^3.4.0",
|
||||||
"micro": "^10.0.1",
|
"micro": "^10.0.1",
|
||||||
"nanoid": "^4.0.2",
|
"next": "13.5.4",
|
||||||
"next": "13.4.19",
|
|
||||||
"next-auth": "4.22.3",
|
"next-auth": "4.22.3",
|
||||||
"next-plausible": "^3.10.1",
|
"next-plausible": "^3.10.1",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
@ -41,7 +40,7 @@
|
|||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.1",
|
||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.2.2",
|
||||||
"zod": "^3.21.4"
|
"zod": "^3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -14,14 +14,14 @@ import { DataTable } from '@documenso/ui/primitives/data-table';
|
|||||||
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
|
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
|
||||||
import { Input } from '@documenso/ui/primitives/input';
|
import { Input } from '@documenso/ui/primitives/input';
|
||||||
|
|
||||||
interface User {
|
type UserData = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
Subscription?: SubscriptionLite | null;
|
Subscription?: SubscriptionLite | null;
|
||||||
Document: DocumentLite[];
|
Document: DocumentLite[];
|
||||||
}
|
};
|
||||||
|
|
||||||
type SubscriptionLite = Pick<
|
type SubscriptionLite = Pick<
|
||||||
Subscription,
|
Subscription,
|
||||||
@ -31,7 +31,7 @@ type SubscriptionLite = Pick<
|
|||||||
type DocumentLite = Pick<Document, 'id'>;
|
type DocumentLite = Pick<Document, 'id'>;
|
||||||
|
|
||||||
type UsersDataTableProps = {
|
type UsersDataTableProps = {
|
||||||
users: User[];
|
users: UserData[];
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
perPage: number;
|
perPage: number;
|
||||||
page: number;
|
page: number;
|
||||||
|
7499
package-lock.json
generated
7499
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,12 @@
|
|||||||
"dx": "npm i && npm run dx:up && npm run prisma:migrate-dev",
|
"dx": "npm i && npm run dx:up && npm run prisma:migrate-dev",
|
||||||
"dx:up": "docker compose -f docker/compose-services.yml up -d",
|
"dx:up": "docker compose -f docker/compose-services.yml up -d",
|
||||||
"dx:down": "docker compose -f docker/compose-services.yml down",
|
"dx:down": "docker compose -f docker/compose-services.yml down",
|
||||||
"ci": "turbo run build test:e2e"
|
"ci": "turbo run build test:e2e",
|
||||||
|
"prisma:generate": "npm run with:env -- npm run prisma:generate -w @documenso/prisma",
|
||||||
|
"prisma:migrate-dev": "npm run with:env -- npm run prisma:migrate-dev -w @documenso/prisma",
|
||||||
|
"prisma:migrate-deploy": "npm run with:env -- npm run prisma:migrate-deploy -w @documenso/prisma",
|
||||||
|
"with:env": "dotenv -e .env -e .env.local --",
|
||||||
|
"reset:hard": "npm run clean && npm i && npm run prisma:generate"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": ">=8.6.0",
|
"npm": ">=8.6.0",
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"@documenso/prisma": "*",
|
"@documenso/prisma": "*",
|
||||||
"luxon": "^3.4.0",
|
"luxon": "^3.4.0",
|
||||||
"micro": "^10.0.1",
|
"micro": "^10.0.1",
|
||||||
"next": "13.4.19",
|
"next": "13.5.4",
|
||||||
"next-auth": "4.22.3",
|
"next-auth": "4.22.3",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
|
@ -1 +1 @@
|
|||||||
export { render, renderAsync } from '@react-email/components';
|
export { render } from '@react-email/components';
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
"clean": "rimraf node_modules"
|
"clean": "rimraf node_modules"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
"@typescript-eslint/eslint-plugin": "6.8.0",
|
||||||
"@typescript-eslint/parser": "^5.59.2",
|
"@typescript-eslint/parser": "6.8.0",
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.40.0",
|
||||||
"eslint-config-next": "13.4.19",
|
"eslint-config-next": "13.4.19",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
@ -16,6 +16,6 @@
|
|||||||
"eslint-plugin-package-json": "^0.1.4",
|
"eslint-plugin-package-json": "^0.1.4",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"luxon": "^3.4.0",
|
"luxon": "^3.4.0",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^4.0.2",
|
||||||
"next": "13.4.19",
|
"next": "13.5.4",
|
||||||
"next-auth": "4.22.3",
|
"next-auth": "4.22.3",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
@ -3,8 +3,6 @@ import { match } from 'ts-pattern';
|
|||||||
|
|
||||||
import { DocumentDataType } from '@documenso/prisma/client';
|
import { DocumentDataType } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { getPresignGetUrl } from './server-actions';
|
|
||||||
|
|
||||||
export type GetFileOptions = {
|
export type GetFileOptions = {
|
||||||
type: DocumentDataType;
|
type: DocumentDataType;
|
||||||
data: string;
|
data: string;
|
||||||
@ -33,6 +31,8 @@ const getFileFromBytes64 = (data: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getFileFromS3 = async (key: string) => {
|
const getFileFromS3 = async (key: string) => {
|
||||||
|
const { getPresignGetUrl } = await import('./server-actions');
|
||||||
|
|
||||||
const { url } = await getPresignGetUrl(key);
|
const { url } = await getPresignGetUrl(key);
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -4,7 +4,6 @@ import { match } from 'ts-pattern';
|
|||||||
import { DocumentDataType } from '@documenso/prisma/client';
|
import { DocumentDataType } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { createDocumentData } from '../../server-only/document-data/create-document-data';
|
import { createDocumentData } from '../../server-only/document-data/create-document-data';
|
||||||
import { getPresignPostUrl } from './server-actions';
|
|
||||||
|
|
||||||
type File = {
|
type File = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -34,6 +33,8 @@ const putFileInDatabase = async (file: File) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const putFileInS3 = async (file: File) => {
|
const putFileInS3 = async (file: File) => {
|
||||||
|
const { getPresignPostUrl } = await import('./server-actions');
|
||||||
|
|
||||||
const { url, key } = await getPresignPostUrl(file.name, file.type);
|
const { url, key } = await getPresignPostUrl(file.name, file.type);
|
||||||
|
|
||||||
const body = await file.arrayBuffer();
|
const body = await file.arrayBuffer();
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
PutObjectCommand,
|
PutObjectCommand,
|
||||||
S3Client,
|
S3Client,
|
||||||
} from '@aws-sdk/client-s3';
|
} from '@aws-sdk/client-s3';
|
||||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
||||||
import slugify from '@sindresorhus/slugify';
|
import slugify from '@sindresorhus/slugify';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
@ -17,6 +16,8 @@ import { alphaid } from '../id';
|
|||||||
export const getPresignPostUrl = async (fileName: string, contentType: string) => {
|
export const getPresignPostUrl = async (fileName: string, contentType: string) => {
|
||||||
const client = getS3Client();
|
const client = getS3Client();
|
||||||
|
|
||||||
|
const { getSignedUrl } = await import('@aws-sdk/s3-request-presigner');
|
||||||
|
|
||||||
const { user } = await getServerComponentSession();
|
const { user } = await getServerComponentSession();
|
||||||
|
|
||||||
// Get the basename and extension for the file
|
// Get the basename and extension for the file
|
||||||
@ -44,6 +45,8 @@ export const getPresignPostUrl = async (fileName: string, contentType: string) =
|
|||||||
export const getAbsolutePresignPostUrl = async (key: string) => {
|
export const getAbsolutePresignPostUrl = async (key: string) => {
|
||||||
const client = getS3Client();
|
const client = getS3Client();
|
||||||
|
|
||||||
|
const { getSignedUrl } = await import('@aws-sdk/s3-request-presigner');
|
||||||
|
|
||||||
const putObjectCommand = new PutObjectCommand({
|
const putObjectCommand = new PutObjectCommand({
|
||||||
Bucket: process.env.NEXT_PRIVATE_UPLOAD_BUCKET,
|
Bucket: process.env.NEXT_PRIVATE_UPLOAD_BUCKET,
|
||||||
Key: key,
|
Key: key,
|
||||||
@ -59,6 +62,8 @@ export const getAbsolutePresignPostUrl = async (key: string) => {
|
|||||||
export const getPresignGetUrl = async (key: string) => {
|
export const getPresignGetUrl = async (key: string) => {
|
||||||
const client = getS3Client();
|
const client = getS3Client();
|
||||||
|
|
||||||
|
const { getSignedUrl } = await import('@aws-sdk/s3-request-presigner');
|
||||||
|
|
||||||
const getObjectCommand = new GetObjectCommand({
|
const getObjectCommand = new GetObjectCommand({
|
||||||
Bucket: process.env.NEXT_PRIVATE_UPLOAD_BUCKET,
|
Bucket: process.env.NEXT_PRIVATE_UPLOAD_BUCKET,
|
||||||
Key: key,
|
Key: key,
|
||||||
|
@ -3,8 +3,6 @@ import { match } from 'ts-pattern';
|
|||||||
|
|
||||||
import { DocumentDataType } from '@documenso/prisma/client';
|
import { DocumentDataType } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { getAbsolutePresignPostUrl } from './server-actions';
|
|
||||||
|
|
||||||
export type UpdateFileOptions = {
|
export type UpdateFileOptions = {
|
||||||
type: DocumentDataType;
|
type: DocumentDataType;
|
||||||
oldData: string;
|
oldData: string;
|
||||||
@ -40,6 +38,8 @@ const updateFileWithBytes64 = (data: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateFileWithS3 = async (key: string, data: string) => {
|
const updateFileWithS3 = async (key: string, data: string) => {
|
||||||
|
const { getAbsolutePresignPostUrl } = await import('./server-actions');
|
||||||
|
|
||||||
const { url } = await getAbsolutePresignPostUrl(key);
|
const { url } = await getAbsolutePresignPostUrl(key);
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -14,16 +14,16 @@
|
|||||||
"prisma:seed": "prisma db seed"
|
"prisma:seed": "prisma db seed"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"seed": "ts-node --transpileOnly --skipProject ./seed-database.ts"
|
"seed": "ts-node --transpileOnly --project ./tsconfig.seed.json ./seed-database.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "5.3.1",
|
"@prisma/client": "5.4.2",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"dotenv-cli": "^7.3.0",
|
"dotenv-cli": "^7.3.0",
|
||||||
"prisma": "5.3.1"
|
"prisma": "5.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
packages/prisma/tsconfig.seed.json
Normal file
6
packages/prisma/tsconfig.seed.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "NodeNext"
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@
|
|||||||
"@types/react": "18.2.18",
|
"@types/react": "18.2.18",
|
||||||
"@types/react-dom": "18.2.7",
|
"@types/react-dom": "18.2.7",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "5.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/lib": "*",
|
"@documenso/lib": "*",
|
||||||
@ -60,11 +60,11 @@
|
|||||||
"framer-motion": "^10.12.8",
|
"framer-motion": "^10.12.8",
|
||||||
"lucide-react": "^0.277.0",
|
"lucide-react": "^0.277.0",
|
||||||
"luxon": "^3.4.2",
|
"luxon": "^3.4.2",
|
||||||
"next": "13.4.19",
|
"next": "13.5.4",
|
||||||
"pdfjs-dist": "3.6.172",
|
"pdfjs-dist": "3.11.174",
|
||||||
"react-day-picker": "^8.7.1",
|
"react-day-picker": "^8.7.1",
|
||||||
"react-hook-form": "^7.45.4",
|
"react-hook-form": "^7.45.4",
|
||||||
"react-pdf": "^7.3.3",
|
"react-pdf": "^7.5.0",
|
||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.1",
|
||||||
"tailwind-merge": "^1.12.0",
|
"tailwind-merge": "^1.12.0",
|
||||||
"tailwindcss-animate": "^1.0.5"
|
"tailwindcss-animate": "^1.0.5"
|
||||||
|
@ -11,12 +11,8 @@ const AlertDialog = AlertDialogPrimitive.Root;
|
|||||||
|
|
||||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
||||||
|
|
||||||
const AlertDialogPortal = ({
|
const AlertDialogPortal = ({ children, ...props }: AlertDialogPrimitive.AlertDialogPortalProps) => (
|
||||||
className,
|
<AlertDialogPrimitive.Portal {...props}>
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: AlertDialogPrimitive.AlertDialogPortalProps) => (
|
|
||||||
<AlertDialogPrimitive.Portal className={cn(className)} {...props}>
|
|
||||||
<div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
|
<div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,12 +12,11 @@ const Dialog = DialogPrimitive.Root;
|
|||||||
const DialogTrigger = DialogPrimitive.Trigger;
|
const DialogTrigger = DialogPrimitive.Trigger;
|
||||||
|
|
||||||
const DialogPortal = ({
|
const DialogPortal = ({
|
||||||
className,
|
|
||||||
children,
|
children,
|
||||||
position = 'start',
|
position = 'start',
|
||||||
...props
|
...props
|
||||||
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' | 'center' }) => (
|
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' | 'center' }) => (
|
||||||
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
<DialogPrimitive.Portal {...props}>
|
||||||
<div
|
<div
|
||||||
className={cn('fixed inset-0 z-50 flex justify-center sm:items-center', {
|
className={cn('fixed inset-0 z-50 flex justify-center sm:items-center', {
|
||||||
'items-start': position === 'start',
|
'items-start': position === 'start',
|
||||||
|
@ -28,8 +28,8 @@ interface SheetPortalProps
|
|||||||
extends SheetPrimitive.DialogPortalProps,
|
extends SheetPrimitive.DialogPortalProps,
|
||||||
VariantProps<typeof portalVariants> {}
|
VariantProps<typeof portalVariants> {}
|
||||||
|
|
||||||
const SheetPortal = ({ position, className, children, ...props }: SheetPortalProps) => (
|
const SheetPortal = ({ position, children, ...props }: SheetPortalProps) => (
|
||||||
<SheetPrimitive.Portal className={cn(className)} {...props}>
|
<SheetPrimitive.Portal {...props}>
|
||||||
<div className={portalVariants({ position })}>{children}</div>
|
<div className={portalVariants({ position })}>{children}</div>
|
||||||
</SheetPrimitive.Portal>
|
</SheetPrimitive.Portal>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user