mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 01:01:49 +10:00
Merge branch 'main' into DOC-185-buttons-alignment
This commit is contained in:
12
.env.example
12
.env.example
@ -1,7 +1,7 @@
|
||||
# Database
|
||||
# You use the provided remote test database, courtesy of the documenso team: postgres://documenso_test_user:GnmLG14u12sd9zHsd4vVWwP40WneFJMo@dpg-cf2hljh4reb5o45oqpq0-a.oregon-postgres.render.com/documenso_test_e2i3
|
||||
# It is however recommend, that you set up a local Postgres SQL instance
|
||||
# ⚠ WARNING: The test database can be resetted or taken offline at any point
|
||||
# Option 1: You can use the provided remote test database, courtesy of the documenso team: postgres://documenso_test_user:GnmLG14u12sd9zHsd4vVWwP40WneFJMo@dpg-cf2hljh4reb5o45oqpq0-a.oregon-postgres.render.com/documenso_test_e2i3
|
||||
# Option 2: Set up a local Postgres SQL instance (RECOMMENDED)
|
||||
# ⚠ WARNING: The test database can be resetted or taken offline at any point.
|
||||
# ⚠ WARNING: Please be aware that nothing written to the test databae is private.
|
||||
DATABASE_URL=''
|
||||
|
||||
@ -18,4 +18,8 @@ NEXTAUTH_URL='http://localhost:3000'
|
||||
# You can also configure you own SMTP server using Nodemailer in sendMailts. (currently not possible via config)
|
||||
SENDGRID_API_KEY=''
|
||||
# Sender for signing requests and completion mails.
|
||||
MAIL_FROM=''
|
||||
MAIL_FROM=''
|
||||
|
||||
#FEATURE FLAGS
|
||||
# Allow users to register via the /signup page. Otherwise they will be redirect to the home page.
|
||||
ALLOW_SIGNUP=true
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "apps/website/documenso/website"]
|
||||
path = apps/website/documenso/website
|
||||
url = http://github.com/eltimuro/website.git
|
||||
url = http://github.com/documenso/website.git
|
||||
@ -1,31 +1,37 @@
|
||||
|
||||
# Contributing to Documenso
|
||||
|
||||
If you plan to contribute to Documenso, please take a moment to feel awesome ✨ People like you are what open source is about ♥. Any contributions, no matter how big or small, are highly appreciated.
|
||||
|
||||
## Before getting started
|
||||
|
||||
- Before jumping into a PR be sure to search [existing PRs](https://github.com/documenso/documenso/pulls) or [issues](https://github.com/documenso/documenso/issues) for an open or closed item that relates to your submission.
|
||||
- Select and issue from [here](https://github.com/documenso/documenso/issues) or create a new one
|
||||
- Consider the results from the discussion in the issue
|
||||
|
||||
## Developing
|
||||
The development branch is <code>development</code>. All pull request should be made against this branch. If you need help getting started, [join us on Slack](https://join.slack.com/t/documenso/shared_invite/zt-1qwxxsvli-nDyojjt~wakhgBGl9JRl2w).
|
||||
|
||||
The development branch is <code>main</code>. All pull request should be made against this branch. If you need help getting started, [join us on Slack](https://join.slack.com/t/documenso/shared_invite/zt-1qwxxsvli-nDyojjt~wakhgBGl9JRl2w).
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your
|
||||
own GitHub account and then
|
||||
[clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
|
||||
2. Create a new branch:
|
||||
|
||||
- Create a new branch (include the issue id and somthing readable):
|
||||
|
||||
```sh
|
||||
git checkout -b doc-999-my-feature-or-fix
|
||||
```
|
||||
```sh
|
||||
git checkout -b doc-999-my-feature-or-fix
|
||||
```
|
||||
|
||||
3. See the [Developer Setup](https://github.com/documenso/documenso/blob/main/README.md#developer-setup) for more setup details.
|
||||
## Building
|
||||
|
||||
## Building
|
||||
|
||||
> **Note**
|
||||
> Please be sure that you can make a full production build before pushing code or creating PRs.
|
||||
|
||||
You can build the project with:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
> **Note**
|
||||
> Please be sure that you can make a full production build before pushing code or creating PRs.
|
||||
@ -25,9 +25,6 @@ export default function FieldTypeSelector(props: any) {
|
||||
onChange={(e: any) => {
|
||||
setSelectedFieldType(e);
|
||||
}}
|
||||
onMouseDown={(e: any) => {
|
||||
if (e.button === 0) props.setAdding(true);
|
||||
}}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{fieldTypes.map((fieldType) => (
|
||||
|
||||
@ -17,8 +17,9 @@ export default function PDFEditor(props: any) {
|
||||
const [fields, setFields] = useState<any[]>(props.document.Field);
|
||||
const [selectedRecipient, setSelectedRecipient]: any = useState();
|
||||
const [selectedFieldType, setSelectedFieldType] = useState();
|
||||
const noRecipients = props?.document.Recipient.length === 0;
|
||||
const [adding, setAdding] = useState(false);
|
||||
const noRecipients =
|
||||
props?.document.Recipient.length === 0 ||
|
||||
props?.document.Recipient.every((e: any) => !e.email);
|
||||
|
||||
function onPositionChangedHandler(position: any, id: any) {
|
||||
if (!position) return;
|
||||
@ -60,11 +61,6 @@ export default function PDFEditor(props: any) {
|
||||
onMouseUp={(e: any, page: number) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log(adding);
|
||||
if (adding) {
|
||||
addField(e, page);
|
||||
setAdding(false);
|
||||
}
|
||||
}}
|
||||
onMouseDown={(e: any, page: number) => {
|
||||
if (e.button === 0) addField(e, page);
|
||||
@ -80,7 +76,6 @@ export default function PDFEditor(props: any) {
|
||||
/>
|
||||
<hr className="m-3 border-slate-300"></hr>
|
||||
<FieldTypeSelector
|
||||
setAdding={setAdding}
|
||||
selectedRecipient={selectedRecipient}
|
||||
onChange={setSelectedFieldType}
|
||||
/>
|
||||
@ -101,7 +96,7 @@ export default function PDFEditor(props: any) {
|
||||
);
|
||||
|
||||
createOrUpdateField(props?.document, signatureField).then((res) => {
|
||||
setFields(fields.concat(res));
|
||||
setFields((prevState) => [...prevState, res]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
CheckBadgeIcon,
|
||||
InformationCircleIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import toast from "react-hot-toast";
|
||||
import { FieldType } from "@prisma/client";
|
||||
import {
|
||||
createOrUpdateField,
|
||||
@ -70,7 +69,7 @@ export default function PDFSigner(props: any) {
|
||||
);
|
||||
const signedField = { ...dialogField };
|
||||
signedField.signature = signature;
|
||||
setFields(fields.concat(signedField));
|
||||
setFields((prevState) => [...prevState, signedField]);
|
||||
setOpen(false);
|
||||
setDialogField(null);
|
||||
}
|
||||
@ -174,8 +173,12 @@ export default function PDFSigner(props: any) {
|
||||
FieldType.FREE_SIGNATURE
|
||||
);
|
||||
|
||||
createOrUpdateField(props.document, freeSignatureField).then((res) => {
|
||||
setFields(fields.concat(res));
|
||||
createOrUpdateField(
|
||||
props.document,
|
||||
freeSignatureField,
|
||||
recipient.token
|
||||
).then((res) => {
|
||||
setFields((prevState) => [...prevState, res]);
|
||||
setDialogField(res);
|
||||
setOpen(true);
|
||||
});
|
||||
|
||||
@ -3,6 +3,7 @@ import { Document, Page } from "react-pdf/dist/esm/entry.webpack5";
|
||||
import EditableField from "./editable-field";
|
||||
import SignableField from "./signable-field";
|
||||
import short from "short-uuid";
|
||||
import { FieldType } from "@prisma/client";
|
||||
|
||||
export default function PDFViewer(props) {
|
||||
const [numPages, setNumPages] = useState(null);
|
||||
@ -71,21 +72,25 @@ export default function PDFViewer(props) {
|
||||
onRenderError={() => setLoading(false)}
|
||||
></Page>
|
||||
{props?.fields
|
||||
.filter((item) => item.page === index)
|
||||
.map((item) =>
|
||||
.filter((field) => field.page === index)
|
||||
.map((field) =>
|
||||
props.readonly ? (
|
||||
<SignableField
|
||||
onClick={props.onClick}
|
||||
key={item.id}
|
||||
field={item}
|
||||
key={field.id}
|
||||
field={field}
|
||||
className="absolute"
|
||||
onDelete={onDeleteHandler}
|
||||
></SignableField>
|
||||
) : (
|
||||
<EditableField
|
||||
hidden={item.Signature || item.inserted}
|
||||
key={item.id}
|
||||
field={item}
|
||||
hidden={
|
||||
field.Signature ||
|
||||
field.inserted ||
|
||||
field.type === FieldType.FREE_SIGNATURE
|
||||
}
|
||||
key={field.id}
|
||||
field={field}
|
||||
className="absolute"
|
||||
onPositionChanged={onPositionChangedHandler}
|
||||
onDelete={onDeleteHandler}
|
||||
|
||||
@ -2,6 +2,7 @@ import React, { useState } from "react";
|
||||
import Draggable from "react-draggable";
|
||||
import { IconButton } from "@documenso/ui";
|
||||
import { XCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { classNames } from "@documenso/lib";
|
||||
const stc = require("string-to-color");
|
||||
|
||||
type FieldPropsType = {
|
||||
@ -43,13 +44,19 @@ export default function SignableField(props: FieldPropsType) {
|
||||
if (!field?.signature) props.onClick(props.field);
|
||||
}}
|
||||
ref={nodeRef}
|
||||
className="cursor-pointer opacity-80 m-auto w-48 h-16 flex-row-reverse text-lg font-bold text-center absolute top-0 left-0 select-none hover:brightness-50"
|
||||
className={classNames(
|
||||
"opacity-80 m-auto w-48 h-16 flex-row-reverse text-lg font-bold text-center absolute top-0 left-0 select-none",
|
||||
field.type === "SIGNATURE"
|
||||
? "cursor-pointer hover:brightness-50"
|
||||
: "cursor-not-allowed"
|
||||
)}
|
||||
style={{
|
||||
background: stc(props.field.Recipient.email),
|
||||
}}
|
||||
>
|
||||
<div hidden={field?.signature} className="font-medium my-4">
|
||||
{field.type === "SIGNATURE" ? "SIGN HERE" : ""}
|
||||
{field.type === "DATE" ? <small>Date (filled on sign)</small> : ""}
|
||||
</div>
|
||||
<div
|
||||
hidden={!field?.signature}
|
||||
|
||||
@ -17,12 +17,11 @@ interface LoginValues {
|
||||
csrfToken: string;
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
export default function Login(props: any) {
|
||||
const router = useRouter();
|
||||
const methods = useForm<LoginValues>();
|
||||
const { register, formState } = methods;
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
let callbackUrl =
|
||||
typeof router.query?.callbackUrl === "string"
|
||||
? router.query.callbackUrl
|
||||
@ -117,7 +116,6 @@ export default function Login() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-sm">
|
||||
<a href="#" className="font-medium text-neon hover:text-neon">
|
||||
@ -125,7 +123,6 @@ export default function Login() {
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
type="submit"
|
||||
@ -152,15 +149,27 @@ export default function Login() {
|
||||
<div className="relative flex justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Are you new here?{" "}
|
||||
<Link
|
||||
href="/signup"
|
||||
className="font-medium text-neon hover:text-neon"
|
||||
>
|
||||
Create a new Account
|
||||
</Link>
|
||||
</p>
|
||||
{props.allowSignup ? (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Are you new here?{" "}
|
||||
<Link
|
||||
href="/signup"
|
||||
className="font-medium text-neon hover:text-neon"
|
||||
>
|
||||
Create a new Account
|
||||
</Link>
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Like Documenso{" "}
|
||||
<Link
|
||||
href="https://documenso.com"
|
||||
className="font-medium text-neon hover:text-neon"
|
||||
>
|
||||
Hosted Documenso will be availible soon™
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
</FormProvider>
|
||||
</div>
|
||||
|
||||
@ -115,9 +115,13 @@ export default function Setttings() {
|
||||
</aside>
|
||||
|
||||
<form
|
||||
className="divide-y divide-gray-200 lg:col-span-9"
|
||||
className="divide-y divide-gray-200 lg:col-span-9 min-h-[251px]"
|
||||
action="#"
|
||||
method="POST"
|
||||
hidden={
|
||||
subNavigation.filter((e) => e.current)[0]?.name !==
|
||||
subNavigation[0].name
|
||||
}
|
||||
>
|
||||
{/* Profile section */}
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
@ -170,6 +174,26 @@ export default function Setttings() {
|
||||
<Button onClick={() => updateUser(user)}>Save</Button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
hidden={
|
||||
subNavigation.filter((e) => e.current)[0]?.name !==
|
||||
subNavigation[1].name
|
||||
}
|
||||
className="divide-y divide-gray-200 lg:col-span-9 min-h-[251px]"
|
||||
>
|
||||
{/* Passwords section */}
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
<div>
|
||||
<h2 className="text-lg font-medium leading-6 text-gray-900">
|
||||
Password
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Forgot your passwort? Email <b>hi@documenso.com</b> to reset
|
||||
it.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"formidable": "^3.2.5",
|
||||
"install": "^0.13.0",
|
||||
"next": "13.0.3",
|
||||
"next-auth": "^4.18.3",
|
||||
"next-auth": ">=4.20.1",
|
||||
"next-transpile-modules": "^10.0.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-signpdf": "^1.5.0",
|
||||
|
||||
@ -18,10 +18,10 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
let user = null;
|
||||
|
||||
let recipient = null;
|
||||
if (recipientToken) {
|
||||
// Request from signing page without login
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
token: recipientToken?.toString(),
|
||||
},
|
||||
@ -37,7 +37,14 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
if (!user) return res.status(401).end();
|
||||
|
||||
const document: PrismaDocument = await getDocument(+documentId, req, res);
|
||||
let document: PrismaDocument | null = null;
|
||||
if (recipientToken) {
|
||||
document = await prisma.document.findFirst({
|
||||
where: { id: recipient?.Document?.id },
|
||||
});
|
||||
} else {
|
||||
document = await getDocument(+documentId, req, res);
|
||||
}
|
||||
|
||||
if (!document)
|
||||
res.status(404).end(`No document with id ${documentId} found.`);
|
||||
@ -45,16 +52,18 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const signaturesCount = await prisma.signature.count({
|
||||
where: {
|
||||
Field: {
|
||||
documentId: document.id,
|
||||
documentId: document?.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let signedDocumentAsBase64 = document.document;
|
||||
let signedDocumentAsBase64 = document?.document || "";
|
||||
|
||||
// No need to add a signature, if no one signed yet.
|
||||
if (signaturesCount > 0) {
|
||||
signedDocumentAsBase64 = await addDigitalSignature(document.document);
|
||||
signedDocumentAsBase64 = await addDigitalSignature(
|
||||
document?.document || ""
|
||||
);
|
||||
}
|
||||
|
||||
const buffer: Buffer = Buffer.from(signedDocumentAsBase64, "base64");
|
||||
@ -62,7 +71,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.setHeader("Content-Length", buffer.length);
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename=${document.title}`
|
||||
`attachment; filename=${document?.title}`
|
||||
);
|
||||
|
||||
return res.status(200).send(buffer);
|
||||
|
||||
@ -36,8 +36,10 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getUserFromToken(req, res);
|
||||
const { id: documentId } = req.query;
|
||||
const { token: recipientToken } = req.query;
|
||||
let user = null;
|
||||
if (!recipientToken) user = await getUserFromToken(req, res);
|
||||
if (!user && !recipientToken) return res.status(401).end();
|
||||
const body: {
|
||||
id: number;
|
||||
type: FieldType;
|
||||
@ -48,18 +50,30 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
customText: string;
|
||||
} = req.body;
|
||||
|
||||
if (!user) return;
|
||||
|
||||
const { id: documentId } = req.query;
|
||||
if (!documentId) {
|
||||
res.status(400).send("Missing parameter documentId.");
|
||||
return;
|
||||
return res.status(400).send("Missing parameter documentId.");
|
||||
}
|
||||
|
||||
const document: PrismaDocument = await getDocument(+documentId, req, res);
|
||||
if (recipientToken) {
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: { token: recipientToken?.toString() },
|
||||
});
|
||||
|
||||
// todo entity ownerships checks
|
||||
if (document.userId !== user.id) {
|
||||
return res.status(401).send("User does not have access to this document.");
|
||||
if (!recipient || recipient?.documentId !== +documentId)
|
||||
return res
|
||||
.status(401)
|
||||
.send("Recipient does not have access to this document.");
|
||||
}
|
||||
|
||||
if (user) {
|
||||
const document: PrismaDocument = await getDocument(+documentId, req, res);
|
||||
// todo entity ownerships checks
|
||||
if (document.userId !== user.id) {
|
||||
return res
|
||||
.status(401)
|
||||
.send("User does not have access to this document.");
|
||||
}
|
||||
}
|
||||
|
||||
const field = await prisma.field.upsert({
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { SigningStatus, DocumentStatus } from "@prisma/client";
|
||||
@ -12,7 +8,6 @@ import { insertImageInPDF, insertTextInPDF } from "@documenso/pdf";
|
||||
import { sendSigningDoneMail } from "@documenso/lib/mail";
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const existingUser = await getUserFromToken(req, res);
|
||||
const { token: recipientToken } = req.query;
|
||||
const { signatures: signaturesFromBody }: { signatures: any[] } = req.body;
|
||||
|
||||
@ -29,11 +24,19 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
return res.status(401).send("Recipient not found.");
|
||||
}
|
||||
|
||||
const document: PrismaDocument = await getDocument(
|
||||
recipient.documentId,
|
||||
req,
|
||||
res
|
||||
);
|
||||
const document: PrismaDocument = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: recipient.documentId,
|
||||
},
|
||||
include: {
|
||||
Recipient: {
|
||||
orderBy: {
|
||||
id: "asc",
|
||||
},
|
||||
},
|
||||
Field: { include: { Recipient: true, Signature: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!document) res.status(404).end(`No document found.`);
|
||||
|
||||
@ -70,6 +73,8 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
},
|
||||
});
|
||||
|
||||
// Don't check for inserted, because currently no "sign again" scenarios exist and
|
||||
// this is probably the expected behaviour in unclean states.
|
||||
const nonSignatureFields = await prisma.field.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import { Document as PrismaDocument, DocumentStatus } from "@prisma/client";
|
||||
import { Breadcrumb, Button, IconButton } from "@documenso/ui";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import {
|
||||
@ -96,14 +96,6 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 mt-4 md:mt-0 md:ml-4">
|
||||
<Button
|
||||
icon={PencilSquareIcon}
|
||||
color="secondary"
|
||||
className="mr-2"
|
||||
href={breadcrumbItems[1].href}
|
||||
>
|
||||
Edit Document
|
||||
</Button>
|
||||
<Button
|
||||
icon={ArrowDownTrayIcon}
|
||||
color="secondary"
|
||||
@ -112,6 +104,19 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
<Button
|
||||
icon={PencilSquareIcon}
|
||||
disabled={props.document.status === DocumentStatus.COMPLETED}
|
||||
color={
|
||||
props.document.status === DocumentStatus.COMPLETED
|
||||
? "primary"
|
||||
: "secondary"
|
||||
}
|
||||
className="mr-2"
|
||||
href={breadcrumbItems[1].href}
|
||||
>
|
||||
Edit Document
|
||||
</Button>
|
||||
<Button
|
||||
className="min-w-[125px]"
|
||||
color="primary"
|
||||
|
||||
@ -107,7 +107,6 @@ export async function getServerSideProps(context: any) {
|
||||
where: {
|
||||
documentId: recipient.Document.id,
|
||||
recipientId: recipient.id,
|
||||
type: { in: [FieldType.SIGNATURE] },
|
||||
Signature: { is: null },
|
||||
},
|
||||
include: {
|
||||
|
||||
@ -6,7 +6,7 @@ import { Button, IconButton } from "@documenso/ui";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
const SignPage: NextPageWithLayout = (props: any) => {
|
||||
const Signed: NextPageWithLayout = (props: any) => {
|
||||
const router = useRouter();
|
||||
const allRecipientsSigned = props.document.Recipient?.every(
|
||||
(r: any) => r.signingStatus === "SIGNED"
|
||||
@ -47,7 +47,12 @@ const SignPage: NextPageWithLayout = (props: any) => {
|
||||
onClick={(event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
router.push("/api/documents/" + props.document.id);
|
||||
router.push(
|
||||
"/api/documents/" +
|
||||
props.document.id +
|
||||
"?token=" +
|
||||
props.recipient.token
|
||||
);
|
||||
}}
|
||||
>
|
||||
Download "{props.document.title}"
|
||||
@ -103,8 +108,9 @@ export async function getServerSideProps(context: any) {
|
||||
props: {
|
||||
document: JSON.parse(JSON.stringify(recipient.Document)),
|
||||
fields: JSON.parse(JSON.stringify(fields)),
|
||||
recipient: JSON.parse(JSON.stringify(recipient)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default SignPage;
|
||||
export default Signed;
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
import Head from "next/head";
|
||||
import Login from "../components/login";
|
||||
|
||||
export default function LoginPage() {
|
||||
export default function LoginPage(props: any) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Login | Documenso</title>
|
||||
</Head>
|
||||
<Login></Login>
|
||||
<Login allowSignup={props.ALLOW_SIGNUP}></Login>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
const ALLOW_SIGNUP = process.env.ALLOW_SIGNUP === "true";
|
||||
|
||||
return {
|
||||
props: {
|
||||
ALLOW_SIGNUP: ALLOW_SIGNUP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -14,6 +14,14 @@ export default function SignupPage(props: { source: string }) {
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
if (process.env.ALLOW_SIGNUP !== "true")
|
||||
return {
|
||||
redirect: {
|
||||
destination: "/login",
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
|
||||
const signupSource: string = context.query["source"];
|
||||
return {
|
||||
props: {
|
||||
|
||||
1908
package-lock.json
generated
1908
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,7 @@
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/react-dom": "18.0.9",
|
||||
"@types/react-signature-canvas": "^1.0.2",
|
||||
"avatar-from-initials": "^1.0.3",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"dotenv": "^16.0.3",
|
||||
@ -31,7 +32,7 @@
|
||||
"eslint-config-next": "13.0.3",
|
||||
"install": "^0.13.0",
|
||||
"next": "13.0.3",
|
||||
"next-auth": "^4.18.3",
|
||||
"next-auth": ">=4.20.1",
|
||||
"next-transpile-modules": "^10.0.0",
|
||||
"npm": "^9.1.3",
|
||||
"pdf-lib": "^1.17.1",
|
||||
@ -41,9 +42,5 @@
|
||||
"react-hot-toast": "^2.4.0",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"typescript": "4.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-signature-canvas": "^1.0.2",
|
||||
"file-loader": "^6.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@ import toast from "react-hot-toast";
|
||||
|
||||
export const createOrUpdateField = async (
|
||||
document: any,
|
||||
field: any
|
||||
field: any,
|
||||
recipientToken: string = ""
|
||||
): Promise<any> => {
|
||||
try {
|
||||
const created = await toast.promise(
|
||||
fetch("/api/documents/" + document.id + "/fields", {
|
||||
fetch("/api/documents/" + document.id + "/fields?token=" + recipientToken, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@ -12,7 +12,7 @@ export async function getUserFromToken(
|
||||
const tokenEmail = token?.email?.toString();
|
||||
|
||||
if (!token) {
|
||||
res.status(401).send("No session token found for request.");
|
||||
if (res.status) res.status(401).send("No session token found for request.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user