mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
status tooltip, recipient page
This commit is contained in:
@ -36,6 +36,7 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.41.5",
|
"react-hook-form": "^7.41.5",
|
||||||
"react-pdf": "^6.2.2",
|
"react-pdf": "^6.2.2",
|
||||||
|
"react-tooltip": "^5.7.2",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"short-uuid": "^4.2.2",
|
"short-uuid": "^4.2.2",
|
||||||
"typescript": "4.8.4"
|
"typescript": "4.8.4"
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import "../styles/tailwind.css";
|
import "../styles/tailwind.css";
|
||||||
import "../../../node_modules/placeholder-loading/src/scss/placeholder-loading.scss";
|
import "../../../node_modules/placeholder-loading/src/scss/placeholder-loading.scss";
|
||||||
|
import "react-tooltip/dist/react-tooltip.css";
|
||||||
import { ReactElement, ReactNode } from "react";
|
import { ReactElement, ReactNode } from "react";
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
|
|||||||
@ -3,11 +3,18 @@ import { ReactElement, useEffect, useState } from "react";
|
|||||||
import Layout from "../components/layout";
|
import Layout from "../components/layout";
|
||||||
import type { NextPageWithLayout } from "./_app";
|
import type { NextPageWithLayout } from "./_app";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import {
|
||||||
|
CheckBadgeIcon,
|
||||||
|
EnvelopeIcon,
|
||||||
|
EyeIcon,
|
||||||
|
PlusIcon,
|
||||||
|
TrashIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { uploadDocument } from "@documenso/features";
|
import { uploadDocument } from "@documenso/features";
|
||||||
import { DocumentStatus } from "@prisma/client";
|
import { DocumentStatus } from "@prisma/client";
|
||||||
|
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||||
|
|
||||||
const DocumentsPage: NextPageWithLayout = (req, res) => {
|
const DocumentsPage: NextPageWithLayout = (req, res) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -129,13 +136,62 @@ const DocumentsPage: NextPageWithLayout = (req, res) => {
|
|||||||
{document.title || "#" + document.id}
|
{document.title || "#" + document.id}
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||||
{document.recipients || "-"}
|
{document.Recipient.map((item: any) => (
|
||||||
|
<div>
|
||||||
|
{item.sendStatus === "SENT" &&
|
||||||
|
item.readStatus !== "OPENED" &&
|
||||||
|
item.signingStatus !== "SIGNED" ? (
|
||||||
|
<span id="sent_icon">
|
||||||
|
<EnvelopeIcon className="inline h-5 mr-1"></EnvelopeIcon>
|
||||||
|
{item.email}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
{item.sendStatus === "SENT" &&
|
||||||
|
item.readStatus === "OPENED" ? (
|
||||||
|
<span id="read_icon">
|
||||||
|
<EyeIcon className="inline h-5 mr-1"></EyeIcon>{" "}
|
||||||
|
{item.email}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
{item.sendStatus === "SENT" &&
|
||||||
|
item.readStatus === "OPENED" &&
|
||||||
|
item.signingStatus === "SIGNED" ? (
|
||||||
|
<span id="signed_icon">
|
||||||
|
<CheckBadgeIcon className="inline h-5 mr-1"></CheckBadgeIcon>{" "}
|
||||||
|
{item.email}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{document.Recipient.length === 0 ? "-" : null}
|
||||||
|
<ReactTooltip
|
||||||
|
anchorId="sent_icon"
|
||||||
|
place="bottom"
|
||||||
|
content="Document was sent to recipient."
|
||||||
|
/>
|
||||||
|
<ReactTooltip
|
||||||
|
anchorId="read_icon"
|
||||||
|
place="bottom"
|
||||||
|
content="Document was opened but not signed yet."
|
||||||
|
/>
|
||||||
|
<ReactTooltip
|
||||||
|
anchorId="signed_icon"
|
||||||
|
place="bottom"
|
||||||
|
content="Document was signed by the recipient."
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||||
{formatDocumentStatus(document.status)}
|
{formatDocumentStatus(document.status)}
|
||||||
<p>
|
<p>
|
||||||
<small>
|
<small>
|
||||||
{document.recipients || 0}/{document.signed || 0}
|
{document.signed || 0}/
|
||||||
|
{document.Recipient.length || 0}
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@ -5,23 +5,69 @@ import { Document, Page, pdfjs } from "react-pdf";
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib";
|
import { NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib";
|
||||||
|
import prisma from "@documenso/prisma";
|
||||||
|
import { getUserFromToken } from "@documenso/lib/server";
|
||||||
|
import Logo from "../../../components/logo";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
const PDFViewer = dynamic(() => import("../../../components/pdf-viewer"), {
|
const PDFViewer = dynamic(() => import("../../../components/pdf-viewer"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const DocumentsDetailPage: NextPageWithLayout = () => {
|
const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-fit">
|
<div className="mx-auto w-fit p-4">
|
||||||
<PDFViewer
|
<div className="mx-auto w-auto text-left">
|
||||||
pdfUrl={`${NEXT_PUBLIC_WEBAPP_URL}/api/documents/${router.query.id}`}
|
<div>
|
||||||
/>
|
<h3 className="font-medium leading-tight text-3xl mt-0 mb-2 text-neon">
|
||||||
|
{props.document.title}
|
||||||
|
</h3>
|
||||||
|
<Link
|
||||||
|
type="a"
|
||||||
|
href={
|
||||||
|
NEXT_PUBLIC_WEBAPP_URL +
|
||||||
|
"/documents/" +
|
||||||
|
props.document.id +
|
||||||
|
"/recipients"
|
||||||
|
}
|
||||||
|
className="inline-flex items-center justify-center rounded-md border border-transparent bg-neon px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-neon-dark focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto"
|
||||||
|
>
|
||||||
|
Add Signers
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<PDFViewer
|
||||||
|
pdfUrl={`${NEXT_PUBLIC_WEBAPP_URL}/api/documents/${router.query.id}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: any) {
|
||||||
|
const user = await getUserFromToken(context.req, context.res);
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
const { id: documentId } = context.query;
|
||||||
|
const document = await prisma.document.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: +documentId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// todo optimize querys
|
||||||
|
// todo no intersection groups
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
document: document,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
DocumentsDetailPage.getLayout = function getLayout(page: ReactElement) {
|
DocumentsDetailPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <Layout>{page}</Layout>;
|
return <Layout>{page}</Layout>;
|
||||||
};
|
};
|
||||||
|
|||||||
48
apps/web/pages/documents/[id]/recipients.tsx
Normal file
48
apps/web/pages/documents/[id]/recipients.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import prisma from "@documenso/prisma";
|
||||||
|
import Head from "next/head";
|
||||||
|
import { ReactElement } from "react";
|
||||||
|
import Layout from "../../../components/layout";
|
||||||
|
import { NextPageWithLayout } from "../../_app";
|
||||||
|
import { Fragment } from "react";
|
||||||
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
|
import {
|
||||||
|
ArchiveBoxIcon,
|
||||||
|
ArrowRightCircleIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
DocumentDuplicateIcon,
|
||||||
|
HeartIcon,
|
||||||
|
PencilSquareIcon,
|
||||||
|
TrashIcon,
|
||||||
|
UserPlusIcon,
|
||||||
|
} from "@heroicons/react/20/solid";
|
||||||
|
import { classNames } from "@documenso/lib";
|
||||||
|
import {
|
||||||
|
UserGroupIcon,
|
||||||
|
UserIcon,
|
||||||
|
UsersIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
|
const RecipientsPage: NextPageWithLayout = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Documenttitle - Recipients | Documenso</title>
|
||||||
|
</Head>
|
||||||
|
-todo add signers ui -todo add breadcrumps -todo who will sign this
|
||||||
|
dropdown
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
RecipientsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <Layout>{page}</Layout>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: any) {
|
||||||
|
// todo get current document
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RecipientsPage;
|
||||||
60
package-lock.json
generated
60
package-lock.json
generated
@ -69,6 +69,7 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.41.5",
|
"react-hook-form": "^7.41.5",
|
||||||
"react-pdf": "^6.2.2",
|
"react-pdf": "^6.2.2",
|
||||||
|
"react-tooltip": "^5.7.2",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"short-uuid": "^4.2.2",
|
"short-uuid": "^4.2.2",
|
||||||
"typescript": "4.8.4"
|
"typescript": "4.8.4"
|
||||||
@ -183,6 +184,19 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-PL7g3dhA4dHgZfujkuD8Q+tfJJynEtnNQSPzmucCnxMvkxf4cLBJw/ZYqZUn4HCh33U3WHrAfv2R2tbi9UCSmw=="
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@headlessui/react": {
|
"node_modules/@headlessui/react": {
|
||||||
"version": "1.7.7",
|
"version": "1.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz",
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz",
|
||||||
@ -1636,6 +1650,11 @@
|
|||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||||
|
},
|
||||||
"node_modules/client-only": {
|
"node_modules/client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
@ -7175,6 +7194,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-tooltip": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-Lv6SCgDCejnjs8EKEFW0f+5V0KXbgZYAQvbhMNleF2AJfcXsLn1EbL9XeCoCDeMziHCaC/HxOBnsw+xbMmZ3Og==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.0.4",
|
||||||
|
"classnames": "^2.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.14.0",
|
||||||
|
"react-dom": ">=16.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
@ -8482,6 +8514,7 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.41.5",
|
"react-hook-form": "^7.41.5",
|
||||||
"react-pdf": "^6.2.2",
|
"react-pdf": "^6.2.2",
|
||||||
|
"react-tooltip": "*",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"short-uuid": "^4.2.2",
|
"short-uuid": "^4.2.2",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
@ -8523,6 +8556,19 @@
|
|||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@floating-ui/core": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-PL7g3dhA4dHgZfujkuD8Q+tfJJynEtnNQSPzmucCnxMvkxf4cLBJw/ZYqZUn4HCh33U3WHrAfv2R2tbi9UCSmw=="
|
||||||
|
},
|
||||||
|
"@floating-ui/dom": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==",
|
||||||
|
"requires": {
|
||||||
|
"@floating-ui/core": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@headlessui/react": {
|
"@headlessui/react": {
|
||||||
"version": "1.7.7",
|
"version": "1.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz",
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz",
|
||||||
@ -9578,6 +9624,11 @@
|
|||||||
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
|
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"classnames": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||||
|
},
|
||||||
"client-only": {
|
"client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
@ -13362,6 +13413,15 @@
|
|||||||
"tiny-warning": "^1.0.0"
|
"tiny-warning": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-tooltip": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-Lv6SCgDCejnjs8EKEFW0f+5V0KXbgZYAQvbhMNleF2AJfcXsLn1EbL9XeCoCDeMziHCaC/HxOBnsw+xbMmZ3Og==",
|
||||||
|
"requires": {
|
||||||
|
"@floating-ui/dom": "^1.0.4",
|
||||||
|
"classnames": "^2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"read-cache": {
|
"read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|||||||
Reference in New Issue
Block a user