mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
Apply prettier config to all files
This commit is contained in:
@ -1,31 +1,27 @@
|
||||
import { ArrowSmallLeftIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
import { Button } from "@documenso/ui";
|
||||
import Logo from "../components/logo";
|
||||
import { ArrowSmallLeftIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
export default function Custom404() {
|
||||
return (
|
||||
<>
|
||||
<main className="relative min-h-full bg-gray-100 isolate">
|
||||
<main className="relative isolate min-h-full bg-gray-100">
|
||||
<div className="absolute top-10 left-10">
|
||||
<Logo className="w-10 md:w-20" />
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-48 mx-auto text-center max-w-7xl sm:py-40 lg:px-8">
|
||||
<p className="text-base font-semibold leading-8 text-brown">404</p>
|
||||
<h1 className="mt-4 text-3xl font-bold tracking-tight text-brown sm:text-5xl">
|
||||
Page not found
|
||||
</h1>
|
||||
<div className="mx-auto max-w-7xl px-6 py-48 text-center sm:py-40 lg:px-8">
|
||||
<p className="text-brown text-base font-semibold leading-8">404</p>
|
||||
<h1 className="text-brown mt-4 text-3xl font-bold tracking-tight sm:text-5xl">Page not found</h1>
|
||||
<p className="mt-4 text-base text-gray-700 sm:mt-6">
|
||||
Sorry, we couldn’t find the page you’re looking for.
|
||||
</p>
|
||||
<div className="flex justify-center mt-10">
|
||||
<div className="mt-10 flex justify-center">
|
||||
<Button
|
||||
color="secondary"
|
||||
href="/"
|
||||
icon={ArrowSmallLeftIcon}
|
||||
className="text-base font-semibold leading-7 text-brown"
|
||||
>
|
||||
className="text-brown text-base font-semibold leading-7">
|
||||
Back to home
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -1,27 +1,23 @@
|
||||
import Logo from "../components/logo";
|
||||
import { Button } from "@documenso/ui";
|
||||
import Logo from "../components/logo";
|
||||
import { ArrowSmallLeftIcon } from "@heroicons/react/20/solid";
|
||||
import { EllipsisVerticalIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
export default function Custom500() {
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex flex-col items-center justify-center min-h-full text-white bg-black">
|
||||
<div className="relative flex min-h-full flex-col items-center justify-center bg-black text-white">
|
||||
<div className="absolute top-10 left-10">
|
||||
<Logo dark className="w-10 md:w-20" />
|
||||
</div>
|
||||
|
||||
<div className="px-4 py-10 mt-20 max-w-7xl">
|
||||
<div className="mt-20 max-w-7xl px-4 py-10">
|
||||
<p className="inline-flex items-center text-3xl font-bold sm:text-5xl">
|
||||
500
|
||||
<span className="relative px-3 font-thin sm:text-6xl -top-1.5">
|
||||
|
|
||||
</span>{" "}
|
||||
<span className="text-base font-semibold align-middle sm:text-2xl">
|
||||
Something went wrong.
|
||||
</span>
|
||||
<span className="relative -top-1.5 px-3 font-thin sm:text-6xl">|</span>{" "}
|
||||
<span className="align-middle text-base font-semibold sm:text-2xl">Something went wrong.</span>
|
||||
</p>
|
||||
<div className="flex justify-center mt-10">
|
||||
<div className="mt-10 flex justify-center">
|
||||
<Button color="secondary" href="/" icon={ArrowSmallLeftIcon}>
|
||||
Back to home
|
||||
</Button>
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import "../styles/tailwind.css";
|
||||
import { ReactElement, ReactNode } from "react";
|
||||
import { NextPage } from "next";
|
||||
import type { AppProps } from "next/app";
|
||||
import "../../../node_modules/placeholder-loading/src/scss/placeholder-loading.scss";
|
||||
import "../../../node_modules/react-resizable/css/styles.css";
|
||||
import "react-tooltip/dist/react-tooltip.css";
|
||||
import { ReactElement, ReactNode } from "react";
|
||||
import type { AppProps } from "next/app";
|
||||
import { NextPage } from "next";
|
||||
import "../styles/tailwind.css";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
export { coloredConsole } from "@documenso/lib";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import "react-tooltip/dist/react-tooltip.css";
|
||||
|
||||
export { coloredConsole } from "@documenso/lib";
|
||||
|
||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (page: ReactElement) => ReactNode;
|
||||
@ -17,10 +18,7 @@ type AppPropsWithLayout = AppProps & {
|
||||
Component: NextPageWithLayout;
|
||||
};
|
||||
|
||||
export default function App({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}: AppPropsWithLayout) {
|
||||
export default function App({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) {
|
||||
const getLayout = Component.getLayout || ((page: any) => page);
|
||||
return (
|
||||
<SessionProvider session={session}>
|
||||
|
||||
@ -5,10 +5,7 @@ export default function Document(props) {
|
||||
let pageProps = props.__NEXT_DATA__?.props?.pageProps;
|
||||
|
||||
return (
|
||||
<Html
|
||||
className="h-full bg-gray-100 scroll-smooth font-normal antialiased"
|
||||
lang="en"
|
||||
>
|
||||
<Html className="h-full scroll-smooth bg-gray-100 font-normal antialiased" lang="en">
|
||||
<Head>
|
||||
<meta name="color-scheme"></meta>
|
||||
</Head>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import NextAuth, { Session } from "next-auth";
|
||||
import GitHubProvider from "next-auth/providers/github";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import { ErrorCode } from "@documenso/lib/auth";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { verifyPassword } from "@documenso/lib/auth";
|
||||
import prisma from "@documenso/prisma";
|
||||
import NextAuth, { Session } from "next-auth";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import GitHubProvider from "next-auth/providers/github";
|
||||
|
||||
export default NextAuth({
|
||||
secret: process.env.AUTH_SECRET,
|
||||
@ -27,8 +27,7 @@ export default NextAuth({
|
||||
password: {
|
||||
label: "Password",
|
||||
type: "password",
|
||||
placeholder:
|
||||
"Select a password. Here is some inspiration: https://xkcd.com/936/",
|
||||
placeholder: "Select a password. Here is some inspiration: https://xkcd.com/936/",
|
||||
},
|
||||
},
|
||||
async authorize(credentials: any) {
|
||||
@ -57,10 +56,7 @@ export default NextAuth({
|
||||
throw new Error(ErrorCode.UserMissingPassword);
|
||||
}
|
||||
|
||||
const isCorrectPassword = await verifyPassword(
|
||||
credentials.password,
|
||||
user.password
|
||||
);
|
||||
const isCorrectPassword = await verifyPassword(credentials.password, user.password);
|
||||
|
||||
if (!isCorrectPassword) {
|
||||
throw new Error(ErrorCode.IncorrectPassword);
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { IdentityProvider } from "@prisma/client";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import prisma from "@documenso/prisma";
|
||||
import { hashPassword } from "@documenso/lib/auth";
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { IdentityProvider } from "@prisma/client";
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { email, password, source } = req.body;
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { addDigitalSignature } from "@documenso/signing/addDigitalSignature";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
|
||||
async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { id: documentId } = req.query;
|
||||
@ -46,8 +42,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
document = await getDocument(+documentId, req, res);
|
||||
}
|
||||
|
||||
if (!document)
|
||||
res.status(404).end(`No document with id ${documentId} found.`);
|
||||
if (!document) res.status(404).end(`No document with id ${documentId} found.`);
|
||||
|
||||
const signaturesCount = await prisma.signature.count({
|
||||
where: {
|
||||
@ -61,18 +56,13 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
// 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");
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader("Content-Length", buffer.length);
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename=${document?.title}`
|
||||
);
|
||||
res.setHeader("Content-Disposition", `attachment; filename=${document?.title}`);
|
||||
|
||||
return res.status(200).send(buffer);
|
||||
}
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import short from "short-uuid";
|
||||
import { Document as PrismaDocument, FieldType } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { FieldType, Document as PrismaDocument } from "@prisma/client";
|
||||
import short from "short-uuid";
|
||||
|
||||
async function deleteHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getUserFromToken(req, res);
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { Document as PrismaDocument, FieldType } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { FieldType, Document as PrismaDocument } from "@prisma/client";
|
||||
|
||||
async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getUserFromToken(req, res);
|
||||
@ -61,18 +57,14 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
if (!recipient || recipient?.documentId !== +documentId)
|
||||
return res
|
||||
.status(401)
|
||||
.send("Recipient does not have access to this document.");
|
||||
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.");
|
||||
return res.status(401).send("User does not have access to this document.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import short from "short-uuid";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import short from "short-uuid";
|
||||
|
||||
async function deleteHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getUserFromToken(req, res);
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import short from "short-uuid";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import short from "short-uuid";
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getUserFromToken(req, res);
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { sendSigningRequest } from "@documenso/lib/mail";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { Document as PrismaDocument, SendStatus } from "@prisma/client";
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
@ -23,8 +19,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
const document: PrismaDocument = await getDocument(+documentId, req, res);
|
||||
|
||||
if (!document)
|
||||
res.status(404).end(`No document with id ${documentId} found.`);
|
||||
if (!document) res.status(404).end(`No document with id ${documentId} found.`);
|
||||
|
||||
let recipientCondition: any = {
|
||||
documentId: +documentId,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { SigningStatus, DocumentStatus } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { Document as PrismaDocument, FieldType } from "@prisma/client";
|
||||
import { insertImageInPDF, insertTextInPDF } from "@documenso/pdf";
|
||||
import { sendSigningDoneMail } from "@documenso/lib/mail";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import { insertImageInPDF, insertTextInPDF } from "@documenso/pdf";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { DocumentStatus, SigningStatus } from "@prisma/client";
|
||||
import { FieldType, Document as PrismaDocument } from "@prisma/client";
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { token: recipientToken } = req.query;
|
||||
@ -115,10 +115,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
},
|
||||
data: {
|
||||
document: documentWithInserts,
|
||||
status:
|
||||
unsignedRecipients.length > 0
|
||||
? DocumentStatus.PENDING
|
||||
: DocumentStatus.COMPLETED,
|
||||
status: unsignedRecipients.length > 0 ? DocumentStatus.PENDING : DocumentStatus.COMPLETED,
|
||||
},
|
||||
});
|
||||
|
||||
@ -129,8 +126,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
document.document = documentWithInserts;
|
||||
if (documentOwner)
|
||||
await sendSigningDoneMail(recipient, document, documentOwner);
|
||||
if (documentOwner) await sendSigningDoneMail(recipient, document, documentOwner);
|
||||
}
|
||||
|
||||
return res.status(200).end();
|
||||
@ -139,9 +135,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (signedField?.Signature?.signatureImageAsBase64) {
|
||||
documentWithInserts = await insertImageInPDF(
|
||||
documentWithInserts,
|
||||
signedField.Signature
|
||||
? signedField.Signature?.signatureImageAsBase64
|
||||
: "",
|
||||
signedField.Signature ? signedField.Signature?.signatureImageAsBase64 : "",
|
||||
signedField.positionX,
|
||||
signedField.positionY,
|
||||
signedField.page
|
||||
@ -169,12 +163,8 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
create: {
|
||||
recipientId: recipient.id,
|
||||
fieldId: signature.fieldId,
|
||||
signatureImageAsBase64: signature.signatureImage
|
||||
? signature.signatureImage
|
||||
: null,
|
||||
typedSignature: signature.typedSignature
|
||||
? signature.typedSignature
|
||||
: null,
|
||||
signatureImageAsBase64: signature.signatureImage ? signature.signatureImage : null,
|
||||
typedSignature: signature.typedSignature ? signature.typedSignature : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import formidable from "formidable";
|
||||
import { getDocumentsForUserFromToken } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import formidable from "formidable";
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { defaultHandler, defaultResponder } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { addDigitalSignature } from "@documenso/signing/addDigitalSignature";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
|
||||
// todo remove before launch
|
||||
|
||||
@ -17,10 +13,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const signedDocument = await addDigitalSignature(document.document);
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader("Content-Length", signedDocument.length);
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename=${document.title}`
|
||||
);
|
||||
res.setHeader("Content-Disposition", `attachment; filename=${document.title}`);
|
||||
|
||||
return res.status(200).send(signedDocument);
|
||||
}
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method, body } = req;
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
import {
|
||||
defaultHandler,
|
||||
defaultResponder,
|
||||
getUserFromToken,
|
||||
} from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
|
||||
import prisma from "@documenso/prisma";
|
||||
|
||||
async function getHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getUserFromToken(req, res);
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import Head from "next/head";
|
||||
import { ReactElement } from "react";
|
||||
import Layout from "../components/layout";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { uploadDocument } from "@documenso/features";
|
||||
import { getDocumentsForUserFromToken } from "@documenso/lib/query";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import Layout from "../components/layout";
|
||||
import type { NextPageWithLayout } from "./_app";
|
||||
import {
|
||||
CheckBadgeIcon,
|
||||
@ -9,15 +12,7 @@ import {
|
||||
ExclamationTriangleIcon,
|
||||
UsersIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { uploadDocument } from "@documenso/features";
|
||||
import {
|
||||
DocumentStatus,
|
||||
SendStatus,
|
||||
SigningStatus,
|
||||
Document as PrismaDocument,
|
||||
} from "@prisma/client";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import { getDocumentsForUserFromToken } from "@documenso/lib/query";
|
||||
import { DocumentStatus, Document as PrismaDocument, SendStatus, SigningStatus } from "@prisma/client";
|
||||
import { truncate } from "fs";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
|
||||
@ -55,19 +50,16 @@ const DashboardPage: NextPageWithLayout = (props: any) => {
|
||||
|
||||
<div className="py-10 max-sm:px-4">
|
||||
<header>
|
||||
<h1 className="text-3xl font-bold leading-tight tracking-tight text-gray-900">
|
||||
Dashboard
|
||||
</h1>
|
||||
<h1 className="text-3xl font-bold leading-tight tracking-tight text-gray-900">Dashboard</h1>
|
||||
</header>
|
||||
<dl className="grid gap-5 mt-8 md:grid-cols-3 ">
|
||||
<dl className="mt-8 grid gap-5 md:grid-cols-3 ">
|
||||
{stats.map((item) => (
|
||||
<Link href={item.link} key={item.name}>
|
||||
<div className="px-4 py-3 overflow-hidden bg-white rounded-lg shadow md:p-6 sm:py-5">
|
||||
<dt className="text-sm font-medium text-gray-500 truncate ">
|
||||
<div className="overflow-hidden rounded-lg bg-white px-4 py-3 shadow sm:py-5 md:p-6">
|
||||
<dt className="truncate text-sm font-medium text-gray-500 ">
|
||||
<item.icon
|
||||
className="flex-shrink-0 inline w-5 h-5 mr-3 text-neon sm:w-6 sm:h-6"
|
||||
aria-hidden="true"
|
||||
></item.icon>
|
||||
className="text-neon mr-3 inline h-5 w-5 flex-shrink-0 sm:h-6 sm:w-6"
|
||||
aria-hidden="true"></item.icon>
|
||||
{item.name}
|
||||
</dt>
|
||||
<dd className="mt-1 text-2xl font-semibold tracking-tight text-gray-900 sm:text-3xl">
|
||||
@ -92,25 +84,20 @@ const DashboardPage: NextPageWithLayout = (props: any) => {
|
||||
onClick={() => {
|
||||
document?.getElementById("fileUploadHelper")?.click();
|
||||
}}
|
||||
className="relative block w-full p-12 text-center border-2 border-gray-300 border-dashed rounded-lg cursor-pointer hover:border-neon focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
className="hover:border-neon relative block w-full cursor-pointer rounded-lg border-2 border-dashed border-gray-300 p-12 text-center focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
|
||||
<svg
|
||||
className="w-12 h-12 mx-auto text-gray-400"
|
||||
className="mx-auto h-12 w-12 text-gray-400"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
viewBox="0 00 20 25"
|
||||
aria-hidden="true"
|
||||
>
|
||||
aria-hidden="true">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
id="add_document"
|
||||
className="mt-2 block text-sm font-medium text-neon"
|
||||
>
|
||||
<span id="add_document" className="text-neon mt-2 block text-sm font-medium">
|
||||
Add a new PDF document.
|
||||
</span>
|
||||
</div>
|
||||
@ -147,9 +134,7 @@ export async function getServerSideProps(context: any) {
|
||||
|
||||
const documents: any[] = await getDocumentsForUserFromToken(context);
|
||||
|
||||
const drafts: PrismaDocument[] = documents.filter(
|
||||
(d) => d.status === DocumentStatus.DRAFT
|
||||
);
|
||||
const drafts: PrismaDocument[] = documents.filter((d) => d.status === DocumentStatus.DRAFT);
|
||||
|
||||
const waiting: any[] = documents.filter(
|
||||
(e) =>
|
||||
@ -158,9 +143,7 @@ export async function getServerSideProps(context: any) {
|
||||
e.Recipient.some((r: any) => r.signingStatus === SigningStatus.NOT_SIGNED)
|
||||
);
|
||||
|
||||
const completed: PrismaDocument[] = documents.filter(
|
||||
(d) => d.status === DocumentStatus.COMPLETED
|
||||
);
|
||||
const completed: PrismaDocument[] = documents.filter((d) => d.status === DocumentStatus.COMPLETED);
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import { ReactElement, useEffect, useState } from "react";
|
||||
import { NextPageContext } from "next";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { uploadDocument } from "@documenso/features";
|
||||
import { deleteDocument, getDocuments } from "@documenso/lib/api";
|
||||
import { Button, IconButton, SelectBox } from "@documenso/ui";
|
||||
import Layout from "../components/layout";
|
||||
import type { NextPageWithLayout } from "./_app";
|
||||
import Head from "next/head";
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
CheckBadgeIcon,
|
||||
@ -13,12 +18,7 @@ import {
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { useRouter } from "next/router";
|
||||
import { uploadDocument } from "@documenso/features";
|
||||
import { DocumentStatus } from "@prisma/client";
|
||||
import { Button, IconButton, SelectBox } from "@documenso/ui";
|
||||
import { NextPageContext } from "next";
|
||||
import { deleteDocument, getDocuments } from "@documenso/lib/api";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
|
||||
const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
@ -42,12 +42,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
{ label: "Last 12 months", value: 366 },
|
||||
];
|
||||
|
||||
const [selectedStatusFilter, setSelectedStatusFilter] = useState(
|
||||
statusFilters[0]
|
||||
);
|
||||
const [selectedCreatedFilter, setSelectedCreatedFilter] = useState(
|
||||
createdFilter[0]
|
||||
);
|
||||
const [selectedStatusFilter, setSelectedStatusFilter] = useState(statusFilters[0]);
|
||||
const [selectedCreatedFilter, setSelectedCreatedFilter] = useState(createdFilter[0]);
|
||||
|
||||
const loadDocuments = async () => {
|
||||
if (!documents.length) setLoading(true);
|
||||
@ -62,9 +58,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
useEffect(() => {
|
||||
loadDocuments().finally(() => {
|
||||
setSelectedStatusFilter(
|
||||
statusFilters.filter(
|
||||
(status) => status.value === props.filter.toUpperCase()
|
||||
)[0]
|
||||
statusFilters.filter((status) => status.value === props.filter.toUpperCase())[0]
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
@ -79,9 +73,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
|
||||
function filterDocumentes(documents: []): any {
|
||||
let filteredDocuments = documents.filter(
|
||||
(d: any) =>
|
||||
d.status === selectedStatusFilter.value ||
|
||||
selectedStatusFilter.value === "ALL"
|
||||
(d: any) => d.status === selectedStatusFilter.value || selectedStatusFilter.value === "ALL"
|
||||
);
|
||||
|
||||
filteredDocuments = filteredDocuments.filter((document: any) =>
|
||||
@ -98,9 +90,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
const today: Date = new Date(); // Today's date
|
||||
|
||||
// Calculate the difference between the two dates in days
|
||||
const diffInDays = Math.floor(
|
||||
(today.getTime() - documentDate.getTime()) / millisecondsInDay
|
||||
);
|
||||
const diffInDays = Math.floor((today.getTime() - documentDate.getTime()) / millisecondsInDay);
|
||||
|
||||
console.log(diffInDays);
|
||||
|
||||
@ -114,12 +104,10 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
<title>Documents | Documenso</title>
|
||||
</Head>
|
||||
<div className="px-4 sm:px-6 lg:px-8">
|
||||
<div className="sm:flex sm:items-center mt-10">
|
||||
<div className="mt-10 sm:flex sm:items-center">
|
||||
<div className="sm:flex-auto">
|
||||
<header>
|
||||
<h1 className="text-3xl font-bold leading-tight tracking-tight text-gray-900">
|
||||
Documents
|
||||
</h1>
|
||||
<h1 className="text-3xl font-bold leading-tight tracking-tight text-gray-900">Documents</h1>
|
||||
</header>
|
||||
</div>
|
||||
<div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||
@ -127,27 +115,24 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
icon={DocumentPlusIcon}
|
||||
onClick={() => {
|
||||
document?.getElementById("fileUploadHelper")?.click();
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Add Document
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 mb-12">
|
||||
<div className="w-fit block float-right ml-3 mt-7">
|
||||
{filteredDocuments.length != 1
|
||||
? filteredDocuments.length + " Documents"
|
||||
: "1 Document"}
|
||||
<div className="float-right ml-3 mt-7 block w-fit">
|
||||
{filteredDocuments.length != 1 ? filteredDocuments.length + " Documents" : "1 Document"}
|
||||
</div>
|
||||
<SelectBox
|
||||
className="w-1/4 block float-right"
|
||||
className="float-right block w-1/4"
|
||||
label="Created"
|
||||
options={createdFilter}
|
||||
value={selectedCreatedFilter}
|
||||
onChange={setSelectedCreatedFilter}
|
||||
/>
|
||||
<SelectBox
|
||||
className="w-1/4 block float-right ml-3"
|
||||
className="float-right ml-3 block w-1/4"
|
||||
label="Status"
|
||||
options={statusFilters}
|
||||
value={selectedStatusFilter}
|
||||
@ -171,47 +156,28 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="mt-28 flex flex-col"
|
||||
hidden={!documents.length || loading}
|
||||
>
|
||||
<div className="mt-28 flex flex-col" hidden={!documents.length || loading}>
|
||||
<div
|
||||
className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8"
|
||||
hidden={!documents.length || loading}
|
||||
>
|
||||
hidden={!documents.length || loading}>
|
||||
<div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
|
||||
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||
Title
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||
Recipients
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||
Status
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||
Created
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="relative py-3.5 pl-3 pr-4 sm:pr-6"
|
||||
>
|
||||
<th scope="col" className="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
||||
<span className="sr-only">Delete</span>
|
||||
</th>
|
||||
</tr>
|
||||
@ -220,9 +186,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
{filteredDocuments.map((document: any, index: number) => (
|
||||
<tr
|
||||
key={document.id}
|
||||
className="hover:bg-gray-100 cursor-pointer"
|
||||
onClick={(event) => showDocument(document.id)}
|
||||
>
|
||||
className="cursor-pointer hover:bg-gray-100"
|
||||
onClick={(event) => showDocument(document.id)}>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||
{document.title || "#" + document.id}
|
||||
</td>
|
||||
@ -232,43 +197,32 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
{item.sendStatus === "NOT_SENT" ? (
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"
|
||||
>
|
||||
{item.name
|
||||
? item.name + " <" + item.email + ">"
|
||||
: item.email}
|
||||
className="inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
|
||||
{item.name ? item.name + " <" + item.email + ">" : item.email}
|
||||
</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{item.sendStatus === "SENT" &&
|
||||
item.readStatus !== "OPENED" ? (
|
||||
{item.sendStatus === "SENT" && item.readStatus !== "OPENED" ? (
|
||||
<span id="sent_icon">
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-green-800"
|
||||
>
|
||||
<EnvelopeIcon className="inline h-5 mr-1"></EnvelopeIcon>
|
||||
{item.name
|
||||
? item.name + " <" + item.email + ">"
|
||||
: item.email}
|
||||
className="inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-green-800">
|
||||
<EnvelopeIcon className="mr-1 inline h-5"></EnvelopeIcon>
|
||||
{item.name ? item.name + " <" + item.email + ">" : item.email}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{item.readStatus === "OPENED" &&
|
||||
item.signingStatus === "NOT_SIGNED" ? (
|
||||
{item.readStatus === "OPENED" && item.signingStatus === "NOT_SIGNED" ? (
|
||||
<span id="read_icon">
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-green-800"
|
||||
>
|
||||
<CheckIcon className="inline h-5 -mr-2"></CheckIcon>
|
||||
<CheckIcon className="inline h-5 mr-1"></CheckIcon>
|
||||
{item.name
|
||||
? item.name + " <" + item.email + ">"
|
||||
: item.email}
|
||||
className="inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-green-800">
|
||||
<CheckIcon className="-mr-2 inline h-5"></CheckIcon>
|
||||
<CheckIcon className="mr-1 inline h-5"></CheckIcon>
|
||||
{item.name ? item.name + " <" + item.email + ">" : item.email}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
@ -277,8 +231,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
{item.signingStatus === "SIGNED" ? (
|
||||
<span id="signed_icon">
|
||||
<span className="inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
|
||||
<CheckBadgeIcon className="inline h-5 mr-1"></CheckBadgeIcon>{" "}
|
||||
{item.email}
|
||||
<CheckBadgeIcon className="mr-1 inline h-5"></CheckBadgeIcon> {item.email}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
@ -307,9 +260,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
{formatDocumentStatus(document.status)}
|
||||
<p>
|
||||
<small hidden={document.Recipient.length === 0}>
|
||||
{document.Recipient.filter(
|
||||
(r: any) => r.signingStatus === "SIGNED"
|
||||
).length || 0}
|
||||
{document.Recipient.filter((r: any) => r.signingStatus === "SIGNED").length ||
|
||||
0}
|
||||
/{document.Recipient.length || 0}
|
||||
</small>
|
||||
</p>
|
||||
@ -342,30 +294,20 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
onClick={(event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to delete this document"
|
||||
)
|
||||
) {
|
||||
if (confirm("Are you sure you want to delete this document")) {
|
||||
const documentsWithoutIndex = [...documents];
|
||||
const removedItem: any =
|
||||
documentsWithoutIndex.splice(index, 1);
|
||||
const removedItem: any = documentsWithoutIndex.splice(index, 1);
|
||||
setDocuments(documentsWithoutIndex);
|
||||
deleteDocument(document.id)
|
||||
.catch((err) => {
|
||||
documentsWithoutIndex.splice(
|
||||
index,
|
||||
0,
|
||||
removedItem
|
||||
);
|
||||
documentsWithoutIndex.splice(index, 0, removedItem);
|
||||
setDocuments(documentsWithoutIndex);
|
||||
})
|
||||
.then(() => {
|
||||
loadDocuments();
|
||||
});
|
||||
}
|
||||
}}
|
||||
></IconButton>
|
||||
}}></IconButton>
|
||||
<span className="sr-only">, {document.name}</span>
|
||||
</div>
|
||||
</td>
|
||||
@ -374,29 +316,21 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
hidden={filteredDocuments.length > 0}
|
||||
className="mx-auto w-fit mt-12 p-3"
|
||||
>
|
||||
<FunnelIcon className="w-5 inline mr-1 align-middle" /> Nothing
|
||||
here. Maybe try a different filter.
|
||||
<div hidden={filteredDocuments.length > 0} className="mx-auto mt-12 w-fit p-3">
|
||||
<FunnelIcon className="mr-1 inline w-5 align-middle" /> Nothing here. Maybe try a different
|
||||
filter.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="text-center mt-24"
|
||||
id="empty"
|
||||
hidden={documents.length > 0 || loading}
|
||||
>
|
||||
<div className="mt-24 text-center" id="empty" hidden={documents.length > 0 || loading}>
|
||||
<svg
|
||||
className="mx-auto h-12 w-12 text-gray-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
aria-hidden="true">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
@ -405,16 +339,13 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
</svg>
|
||||
|
||||
<h3 className="mt-2 text-sm font-medium text-gray-900">No documents</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Get started by adding a document. Any PDF will do.
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-gray-500">Get started by adding a document. Any PDF will do.</p>
|
||||
<div className="mt-6">
|
||||
<Button
|
||||
icon={PlusIcon}
|
||||
onClick={() => {
|
||||
document?.getElementById("fileUploadHelper")?.click();
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Add Document
|
||||
</Button>
|
||||
<input
|
||||
@ -428,11 +359,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ReactTooltip
|
||||
anchorId="empty"
|
||||
place="bottom"
|
||||
content="No preparation needed. Any PDF will do."
|
||||
/>
|
||||
<ReactTooltip anchorId="empty" place="bottom" content="No preparation needed. Any PDF will do." />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,20 +1,16 @@
|
||||
import { ReactElement } from "react";
|
||||
import Layout from "../../../components/layout";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import Link from "next/link";
|
||||
import { DocumentStatus } from "@prisma/client";
|
||||
import {
|
||||
InformationCircleIcon,
|
||||
PaperAirplaneIcon,
|
||||
UsersIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
import { Button, Breadcrumb } from "@documenso/ui";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import { Breadcrumb, Button } from "@documenso/ui";
|
||||
import PDFEditor from "../../../components/editor/pdf-editor";
|
||||
import Layout from "../../../components/layout";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import { InformationCircleIcon, PaperAirplaneIcon, UsersIcon } from "@heroicons/react/24/outline";
|
||||
import { DocumentStatus } from "@prisma/client";
|
||||
import { Document as PrismaDocument } from "@prisma/client";
|
||||
|
||||
const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
||||
const router = useRouter();
|
||||
@ -32,8 +28,7 @@ const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
||||
},
|
||||
{
|
||||
title: props.document.title,
|
||||
href:
|
||||
NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id,
|
||||
href: NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@ -45,10 +40,7 @@ const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
||||
</h2>
|
||||
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
|
||||
<div className="mt-2 flex items-center text-sm text-gray-500">
|
||||
<UsersIcon
|
||||
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<UsersIcon className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
|
||||
|
||||
<Link href={`/documents/${props.document.id}/recipients`}>
|
||||
{props?.document?.Recipient?.length} Recipients
|
||||
@ -67,21 +59,11 @@ const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
||||
<Button
|
||||
icon={PaperAirplaneIcon}
|
||||
className="ml-3"
|
||||
href={
|
||||
NEXT_PUBLIC_WEBAPP_URL +
|
||||
"/documents/" +
|
||||
props.document.id +
|
||||
"/recipients"
|
||||
}
|
||||
href={NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + "/recipients"}
|
||||
onClick={() => {
|
||||
if (
|
||||
confirm(
|
||||
`Send document out to ${props?.document?.Recipient?.length} recipients?`
|
||||
)
|
||||
) {
|
||||
if (confirm(`Send document out to ${props?.document?.Recipient?.length} recipients?`)) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Prepare to Send
|
||||
</Button>
|
||||
</div>
|
||||
@ -120,11 +102,7 @@ export async function getServerSideProps(context: any) {
|
||||
const { id: documentId } = context.query;
|
||||
|
||||
try {
|
||||
const document: PrismaDocument = await getDocument(
|
||||
+documentId,
|
||||
context.req,
|
||||
context.res
|
||||
);
|
||||
const document: PrismaDocument = await getDocument(+documentId, context.req, context.res);
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@ -1,25 +1,24 @@
|
||||
import Head from "next/head";
|
||||
import { ReactElement, useRef, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { NEXT_PUBLIC_WEBAPP_URL, classNames } from "@documenso/lib";
|
||||
import { createOrUpdateRecipient, deleteRecipient, sendSigningRequests } from "@documenso/lib/api";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import { Breadcrumb, Button, Dialog, IconButton } from "@documenso/ui";
|
||||
import Layout from "../../../components/layout";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import { classNames, NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib";
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
CheckBadgeIcon,
|
||||
CheckIcon,
|
||||
EnvelopeIcon,
|
||||
PaperAirplaneIcon,
|
||||
PencilSquareIcon,
|
||||
TrashIcon,
|
||||
UserPlusIcon,
|
||||
EnvelopeIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { getUserFromToken } from "@documenso/lib/server";
|
||||
import { getDocument } from "@documenso/lib/query";
|
||||
import { Document as PrismaDocument, DocumentStatus } from "@prisma/client";
|
||||
import { Breadcrumb, Button, Dialog, IconButton } from "@documenso/ui";
|
||||
import { createOrUpdateRecipient, deleteRecipient, sendSigningRequests } from "@documenso/lib/api";
|
||||
|
||||
import { DocumentStatus, Document as PrismaDocument } from "@prisma/client";
|
||||
import { FormProvider, useFieldArray, useForm, useWatch } from "react-hook-form";
|
||||
|
||||
export type FormValues = {
|
||||
@ -71,23 +70,22 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
</Head>
|
||||
<div className="px-6 mt-10 sm:px-0">
|
||||
<div className="mt-10 px-6 sm:px-0">
|
||||
<div>
|
||||
<Breadcrumb document={props.document} items={breadcrumbItems} />
|
||||
</div>
|
||||
<div className="mt-2 md:flex md:items-center md:justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
|
||||
{props.document.title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 mt-4 md:mt-0 md:ml-4">
|
||||
<div className="mt-4 flex flex-shrink-0 md:mt-0 md:ml-4">
|
||||
<Button
|
||||
icon={ArrowDownTrayIcon}
|
||||
color="secondary"
|
||||
className="mr-2"
|
||||
href={"/api/documents/" + props.document.id}
|
||||
>
|
||||
href={"/api/documents/" + props.document.id}>
|
||||
Download
|
||||
</Button>
|
||||
<Button
|
||||
@ -95,8 +93,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
disabled={props.document.status === DocumentStatus.COMPLETED}
|
||||
color={props.document.status === DocumentStatus.COMPLETED ? "primary" : "secondary"}
|
||||
className="mr-2"
|
||||
href={breadcrumbItems[1].href}
|
||||
>
|
||||
href={breadcrumbItems[1].href}>
|
||||
Edit Document
|
||||
</Button>
|
||||
<Button
|
||||
@ -108,43 +105,33 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
}}
|
||||
disabled={
|
||||
(formValues.length || 0) === 0 ||
|
||||
!formValues.some(
|
||||
(r: any) => r.email && !hasEmailError(r) && r.sendStatus === "NOT_SENT"
|
||||
) ||
|
||||
!formValues.some((r: any) => r.email && !hasEmailError(r) && r.sendStatus === "NOT_SENT") ||
|
||||
loading
|
||||
}
|
||||
>
|
||||
}>
|
||||
Send
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 mt-10 overflow-hidden bg-white rounded-md shadow sm:p-6">
|
||||
<div className="pb-3 border-b border-gray-200 sm:pb-5">
|
||||
<div className="mt-10 overflow-hidden rounded-md bg-white p-4 shadow sm:p-6">
|
||||
<div className="border-b border-gray-200 pb-3 sm:pb-5">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900 ">Signers</h3>
|
||||
<p className="max-w-4xl mt-2 text-sm text-gray-500">
|
||||
The people who will sign the document.
|
||||
</p>
|
||||
<p className="mt-2 max-w-4xl text-sm text-gray-500">The people who will sign the document.</p>
|
||||
</div>
|
||||
<FormProvider {...form}>
|
||||
<form
|
||||
onChange={() => {
|
||||
trigger();
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<ul role="list" className="divide-y divide-gray-200">
|
||||
{fields.map((item: any, index: number) => (
|
||||
<li
|
||||
key={index}
|
||||
className="w-full px-2 py-3 border-0 hover:bg-green-50 group sm:py-4"
|
||||
>
|
||||
<li key={index} className="group w-full border-0 px-2 py-3 hover:bg-green-50 sm:py-4">
|
||||
<div id="container" className="block w-full lg:flex lg:justify-between">
|
||||
<div className="block space-y-2 md:space-x-2 md:space-y-0 md:flex">
|
||||
<div className="block space-y-2 md:flex md:space-x-2 md:space-y-0">
|
||||
<div
|
||||
className={classNames(
|
||||
"md:w-[250px] rounded-md border border-gray-300 px-3 py-2 shadow-sm focus-within:border-neon focus-within:ring-1 focus-within:ring-neon",
|
||||
"focus-within:border-neon focus-within:ring-neon rounded-md border border-gray-300 px-3 py-2 shadow-sm focus-within:ring-1 md:w-[250px]",
|
||||
item.sendStatus === "SENT" ? "bg-gray-100" : ""
|
||||
)}
|
||||
>
|
||||
)}>
|
||||
<label htmlFor="name" className="block text-xs font-medium text-gray-900">
|
||||
Email
|
||||
</label>
|
||||
@ -170,7 +157,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
documentId: props.document.id,
|
||||
});
|
||||
}}
|
||||
className="block w-full p-0 text-gray-900 placeholder-gray-500 disabled:bg-neutral-100 border-0 outline-none sm:text-sm bg-inherit"
|
||||
className="block w-full border-0 bg-inherit p-0 text-gray-900 placeholder-gray-500 outline-none disabled:bg-neutral-100 sm:text-sm"
|
||||
placeholder="john.dorian@loremipsum.com"
|
||||
/>
|
||||
{errors?.signers?.[index] ? (
|
||||
@ -183,10 +170,9 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"md:w-[250px] rounded-md border border-gray-300 px-3 py-2 shadow-sm focus-within:border-neon focus-within:ring-1 focus-within:ring-neon",
|
||||
"focus-within:border-neon focus-within:ring-neon rounded-md border border-gray-300 px-3 py-2 shadow-sm focus-within:ring-1 md:w-[250px]",
|
||||
item.sendStatus === "SENT" ? "bg-gray-100" : ""
|
||||
)}
|
||||
>
|
||||
)}>
|
||||
<label htmlFor="name" className="block text-xs font-medium text-gray-900">
|
||||
Name (optional)
|
||||
</label>
|
||||
@ -209,19 +195,18 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
documentId: props.document.id,
|
||||
});
|
||||
}}
|
||||
className="block w-full p-0 text-gray-900 placeholder-gray-500 disabled:bg-neutral-100 border-0 outline-none sm:text-sm bg-inherit"
|
||||
className="block w-full border-0 bg-inherit p-0 text-gray-900 placeholder-gray-500 outline-none disabled:bg-neutral-100 sm:text-sm"
|
||||
placeholder="John Dorian"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 lg:ml-2">
|
||||
<div className="flex mb-2 mr-2 lg:mr-0">
|
||||
<div className="mb-2 mr-2 flex lg:mr-0">
|
||||
<div key={item.id} className="space-x-2">
|
||||
{item.sendStatus === "NOT_SENT" ? (
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block mt-3 flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800"
|
||||
>
|
||||
className="mt-3 inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800">
|
||||
Not Sent
|
||||
</span>
|
||||
) : (
|
||||
@ -231,9 +216,8 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
<span id="sent_icon">
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block mt-3 flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800 "
|
||||
>
|
||||
<CheckIcon className="inline h-5 mr-1" /> Sent
|
||||
className="mt-3 inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800 ">
|
||||
<CheckIcon className="mr-1 inline h-5" /> Sent
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
@ -243,10 +227,9 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
<span id="read_icon">
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block mt-3 flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800"
|
||||
>
|
||||
<CheckIcon className="inline h-5 -mr-2"></CheckIcon>
|
||||
<CheckIcon className="inline h-5 mr-1"></CheckIcon>
|
||||
className="mt-3 inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800">
|
||||
<CheckIcon className="-mr-2 inline h-5"></CheckIcon>
|
||||
<CheckIcon className="mr-1 inline h-5"></CheckIcon>
|
||||
Seen
|
||||
</span>
|
||||
</span>
|
||||
@ -257,9 +240,8 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
<span id="signed_icon">
|
||||
<span
|
||||
id="sent_icon"
|
||||
className="inline-block mt-3 flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"
|
||||
>
|
||||
<CheckBadgeIcon className="inline h-5 mr-1"></CheckBadgeIcon>
|
||||
className="mt-3 inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
|
||||
<CheckBadgeIcon className="mr-1 inline h-5"></CheckBadgeIcon>
|
||||
Signed
|
||||
</span>
|
||||
</span>
|
||||
@ -268,7 +250,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex mr-1">
|
||||
<div className="mr-1 flex">
|
||||
<IconButton
|
||||
icon={PaperAirplaneIcon}
|
||||
disabled={
|
||||
@ -286,8 +268,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Resend
|
||||
</IconButton>
|
||||
<IconButton
|
||||
@ -320,8 +301,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
}).then((res) => {
|
||||
append(res);
|
||||
});
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Add Signer
|
||||
</Button>
|
||||
</form>
|
||||
@ -336,7 +316,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
open={open}
|
||||
setLoading={setLoading}
|
||||
setOpen={setOpen}
|
||||
icon={<EnvelopeIcon className="w-6 h-6 text-green-600" aria-hidden="true" />}
|
||||
icon={<EnvelopeIcon className="h-6 w-6 text-green-600" aria-hidden="true" />}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import prisma from "@documenso/prisma";
|
||||
import Head from "next/head";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import { ReadStatus } from "@prisma/client";
|
||||
import PDFSigner from "../../../components/editor/pdf-signer";
|
||||
import Link from "next/link";
|
||||
import prisma from "@documenso/prisma";
|
||||
import PDFSigner from "../../../components/editor/pdf-signer";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import { ClockIcon } from "@heroicons/react/24/outline";
|
||||
import { FieldType, DocumentStatus } from "@prisma/client";
|
||||
import { ReadStatus } from "@prisma/client";
|
||||
import { DocumentStatus, FieldType } from "@prisma/client";
|
||||
|
||||
const SignPage: NextPageWithLayout = (props: any) => {
|
||||
return (
|
||||
@ -14,36 +14,22 @@ const SignPage: NextPageWithLayout = (props: any) => {
|
||||
<title>Sign | Documenso</title>
|
||||
</Head>
|
||||
{!props.expired ? (
|
||||
<PDFSigner
|
||||
document={props.document}
|
||||
recipient={props.recipient}
|
||||
fields={props.fields}
|
||||
/>
|
||||
<PDFSigner document={props.document} recipient={props.recipient} fields={props.fields} />
|
||||
) : (
|
||||
<>
|
||||
<div className="mx-auto w-fit px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
|
||||
<ClockIcon className="text-neon w-10 inline mr-1"></ClockIcon>
|
||||
<h1 className="text-base font-medium text-neon inline align-middle">
|
||||
Time flies.
|
||||
</h1>
|
||||
<p className="mt-2 text-4xl font-bold tracking-tight">
|
||||
This signing link is expired.
|
||||
</p>
|
||||
<ClockIcon className="text-neon mr-1 inline w-10"></ClockIcon>
|
||||
<h1 className="text-neon inline align-middle text-base font-medium">Time flies.</h1>
|
||||
<p className="mt-2 text-4xl font-bold tracking-tight">This signing link is expired.</p>
|
||||
<p className="mt-2 text-base text-gray-500">
|
||||
Please ask{" "}
|
||||
{props.document.User.name
|
||||
? `${props.document.User.name}`
|
||||
: `the sender`}{" "}
|
||||
to resend it.
|
||||
Please ask {props.document.User.name ? `${props.document.User.name}` : `the sender`} to resend
|
||||
it.
|
||||
</p>
|
||||
<div className="mx-auto w-fit text-xl pt-20"></div>
|
||||
<div className="mx-auto w-fit pt-20 text-xl"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="relative mx-96">
|
||||
<div
|
||||
className="absolute inset-0 flex items-center"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
||||
<div className="w-full border-t border-gray-300" />
|
||||
</div>
|
||||
<div className="relative flex justify-center"></div>
|
||||
@ -51,10 +37,7 @@ const SignPage: NextPageWithLayout = (props: any) => {
|
||||
</div>
|
||||
<p className="mt-4 text-center text-sm text-gray-600">
|
||||
Want to send of your own?{" "}
|
||||
<Link
|
||||
href="/signup?source=expired"
|
||||
className="font-medium text-neon hover:text-neon"
|
||||
>
|
||||
<Link href="/signup?source=expired" className="text-neon hover:text-neon font-medium">
|
||||
Create your own Account
|
||||
</Link>
|
||||
</p>
|
||||
@ -118,13 +101,9 @@ export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: {
|
||||
recipient: JSON.parse(JSON.stringify(recipient)),
|
||||
document: JSON.parse(
|
||||
JSON.stringify({ ...recipient.Document, document: "" })
|
||||
),
|
||||
document: JSON.parse(JSON.stringify({ ...recipient.Document, document: "" })),
|
||||
fields: JSON.parse(JSON.stringify(unsignedFields)),
|
||||
expired: recipient.expired
|
||||
? new Date(recipient.expired) < new Date()
|
||||
: false,
|
||||
expired: recipient.expired ? new Date(recipient.expired) < new Date() : false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import prisma from "@documenso/prisma";
|
||||
import Head from "next/head";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import { ArrowDownTrayIcon, CheckBadgeIcon } from "@heroicons/react/24/outline";
|
||||
import { Button, IconButton } from "@documenso/ui";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import prisma from "@documenso/prisma";
|
||||
import { Button, IconButton } from "@documenso/ui";
|
||||
import { NextPageWithLayout } from "../../_app";
|
||||
import { ArrowDownTrayIcon, CheckBadgeIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
const Signed: NextPageWithLayout = (props: any) => {
|
||||
const router = useRouter();
|
||||
const allRecipientsSigned = props.document.Recipient?.every(
|
||||
(r: any) => r.signingStatus === "SIGNED"
|
||||
);
|
||||
const allRecipientsSigned = props.document.Recipient?.every((r: any) => r.signingStatus === "SIGNED");
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -18,53 +16,31 @@ const Signed: NextPageWithLayout = (props: any) => {
|
||||
<title>Sign | Documenso</title>
|
||||
</Head>
|
||||
<div className="mx-auto w-fit px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
|
||||
<CheckBadgeIcon className="text-neon w-10 inline mr-1"></CheckBadgeIcon>
|
||||
<h1 className="text-base font-medium text-neon inline align-middle">
|
||||
It's done!
|
||||
</h1>
|
||||
<p className="mt-2 text-4xl font-bold tracking-tight">
|
||||
You signed "{props.document.title}"
|
||||
</p>
|
||||
<p
|
||||
className="mt-2 text-base text-gray-500 max-w-sm"
|
||||
hidden={allRecipientsSigned}
|
||||
>
|
||||
<CheckBadgeIcon className="text-neon mr-1 inline w-10"></CheckBadgeIcon>
|
||||
<h1 className="text-neon inline align-middle text-base font-medium">It's done!</h1>
|
||||
<p className="mt-2 text-4xl font-bold tracking-tight">You signed "{props.document.title}"</p>
|
||||
<p className="mt-2 max-w-sm text-base text-gray-500" hidden={allRecipientsSigned}>
|
||||
You will be notfied when all recipients have signed.
|
||||
</p>
|
||||
<p
|
||||
className="mt-2 text-base text-gray-500 max-w-sm"
|
||||
hidden={!allRecipientsSigned}
|
||||
>
|
||||
<p className="mt-2 max-w-sm text-base text-gray-500" hidden={!allRecipientsSigned}>
|
||||
All recipients signed.
|
||||
</p>
|
||||
<div
|
||||
className="mx-auto w-fit text-xl pt-20"
|
||||
hidden={!allRecipientsSigned}
|
||||
>
|
||||
<div className="mx-auto w-fit pt-20 text-xl" hidden={!allRecipientsSigned}>
|
||||
<Button
|
||||
icon={ArrowDownTrayIcon}
|
||||
color="secondary"
|
||||
onClick={(event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
router.push(
|
||||
"/api/documents/" +
|
||||
props.document.id +
|
||||
"?token=" +
|
||||
props.recipient.token
|
||||
);
|
||||
}}
|
||||
>
|
||||
router.push("/api/documents/" + props.document.id + "?token=" + props.recipient.token);
|
||||
}}>
|
||||
Download "{props.document.title}"
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="relative mx-96">
|
||||
<div
|
||||
className="absolute inset-0 flex items-center"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
||||
<div className="w-full border-t border-gray-300" />
|
||||
</div>
|
||||
<div className="relative flex justify-center"></div>
|
||||
@ -72,10 +48,7 @@ const Signed: NextPageWithLayout = (props: any) => {
|
||||
</div>
|
||||
<p className="mt-4 text-center text-sm text-gray-600">
|
||||
Want to send slick signing links like this one?{" "}
|
||||
<Link
|
||||
href="https://documenso.com"
|
||||
className="font-medium text-neon hover:text-neon"
|
||||
>
|
||||
<Link href="https://documenso.com" className="text-neon hover:text-neon font-medium">
|
||||
Hosted Documenso is coming soon™
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
import SettingsPage from ".";
|
||||
|
||||
export default SettingsPage;
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
import SettingsPage from ".";
|
||||
|
||||
export default SettingsPage;
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
import SettingsPage from ".";
|
||||
|
||||
export default SettingsPage;
|
||||
|
||||
Reference in New Issue
Block a user