mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
Update prettier styling
This commit is contained in:
@ -92,9 +92,15 @@ export default function PDFEditor(props: any) {
|
|||||||
<div
|
<div
|
||||||
hidden={noRecipients}
|
hidden={noRecipients}
|
||||||
className="fixed left-0 top-1/3 max-w-xs rounded-md border border-slate-300 bg-white py-4 pr-5">
|
className="fixed left-0 top-1/3 max-w-xs rounded-md border border-slate-300 bg-white py-4 pr-5">
|
||||||
<RecipientSelector recipients={props?.document?.Recipient} onChange={setSelectedRecipient} />
|
<RecipientSelector
|
||||||
|
recipients={props?.document?.Recipient}
|
||||||
|
onChange={setSelectedRecipient}
|
||||||
|
/>
|
||||||
<hr className="m-3 border-slate-300"></hr>
|
<hr className="m-3 border-slate-300"></hr>
|
||||||
<FieldTypeSelector selectedRecipient={selectedRecipient} onChange={setSelectedFieldType} />
|
<FieldTypeSelector
|
||||||
|
selectedRecipient={selectedRecipient}
|
||||||
|
onChange={setSelectedFieldType}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -87,9 +87,13 @@ export default function PDFSigner(props: any) {
|
|||||||
icon={CheckBadgeIcon}
|
icon={CheckBadgeIcon}
|
||||||
className="float-right"
|
className="float-right"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
signDocument(props.document, localSignatures, `${router.query.token}`).then(() => {
|
signDocument(props.document, localSignatures, `${router.query.token}`).then(
|
||||||
router.push(`/documents/${props.document.id}/signed?token=${router.query.token}`);
|
() => {
|
||||||
});
|
router.push(
|
||||||
|
`/documents/${props.document.id}/signed?token=${router.query.token}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}}>
|
}}>
|
||||||
Done
|
Done
|
||||||
</Button>
|
</Button>
|
||||||
@ -135,7 +139,9 @@ export default function PDFSigner(props: any) {
|
|||||||
// Check if all fields are signed..
|
// Check if all fields are signed..
|
||||||
if (signatureFields.length > 0) {
|
if (signatureFields.length > 0) {
|
||||||
// If there are no fields to sign at least one signature is enough
|
// If there are no fields to sign at least one signature is enough
|
||||||
return fields.filter((field) => field.type === FieldType.SIGNATURE).every((field) => field.signature);
|
return fields
|
||||||
|
.filter((field) => field.type === FieldType.SIGNATURE)
|
||||||
|
.every((field) => field.signature);
|
||||||
} else {
|
} else {
|
||||||
return localSignatures.length > 0;
|
return localSignatures.length > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,10 @@ export default function PDFViewer(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div hidden={loading} onMouseUp={props.onMouseUp} style={{ height: numPages * pageHeight + 1000 }}>
|
<div
|
||||||
|
hidden={loading}
|
||||||
|
onMouseUp={props.onMouseUp}
|
||||||
|
style={{ height: numPages * pageHeight + 1000 }}>
|
||||||
<div className="mt-6 max-w-xs"></div>
|
<div className="mt-6 max-w-xs"></div>
|
||||||
<Document
|
<Document
|
||||||
file={props.pdfUrl}
|
file={props.pdfUrl}
|
||||||
@ -76,7 +79,11 @@ export default function PDFViewer(props) {
|
|||||||
onDelete={onDeleteHandler}></SignableField>
|
onDelete={onDeleteHandler}></SignableField>
|
||||||
) : (
|
) : (
|
||||||
<EditableField
|
<EditableField
|
||||||
hidden={field.Signature || field.inserted || field.type === FieldType.FREE_SIGNATURE}
|
hidden={
|
||||||
|
field.Signature ||
|
||||||
|
field.inserted ||
|
||||||
|
field.type === FieldType.FREE_SIGNATURE
|
||||||
|
}
|
||||||
key={field.id}
|
key={field.id}
|
||||||
field={field}
|
field={field}
|
||||||
className="absolute"
|
className="absolute"
|
||||||
|
|||||||
@ -71,7 +71,9 @@ export default function SignatureDialog(props: any) {
|
|||||||
aria-current={tab.current ? "page" : undefined}>
|
aria-current={tab.current ? "page" : undefined}>
|
||||||
<tab.icon
|
<tab.icon
|
||||||
className={classNames(
|
className={classNames(
|
||||||
tab.current ? "text-neon" : "text-gray-400 group-hover:text-gray-500",
|
tab.current
|
||||||
|
? "text-neon"
|
||||||
|
: "text-gray-400 group-hover:text-gray-500",
|
||||||
"-ml-0.5 mr-2 h-5 w-5"
|
"-ml-0.5 mr-2 h-5 w-5"
|
||||||
)}
|
)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
|||||||
@ -148,7 +148,9 @@ export default function Login(props: any) {
|
|||||||
) : (
|
) : (
|
||||||
<p className="mt-2 text-center text-sm text-gray-600">
|
<p className="mt-2 text-center text-sm text-gray-600">
|
||||||
Like Documenso{" "}
|
Like Documenso{" "}
|
||||||
<Link href="https://documenso.com" className="text-neon hover:text-neon font-medium">
|
<Link
|
||||||
|
href="https://documenso.com"
|
||||||
|
className="text-neon hover:text-neon font-medium">
|
||||||
Hosted Documenso will be availible soon™
|
Hosted Documenso will be availible soon™
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -77,7 +77,9 @@ export default function Setttings() {
|
|||||||
<h1 className="text-brown text-3xl font-bold leading-tight tracking-tight">Settings</h1>
|
<h1 className="text-brown text-3xl font-bold leading-tight tracking-tight">Settings</h1>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="mx-auto max-w-screen-xl px-4 pb-6 sm:px-6 lg:px-8 lg:pb-16" hidden={!user.email}>
|
<div
|
||||||
|
className="mx-auto max-w-screen-xl px-4 pb-6 sm:px-6 lg:px-8 lg:pb-16"
|
||||||
|
hidden={!user.email}>
|
||||||
<div className="overflow-hidden rounded-lg bg-white shadow">
|
<div className="overflow-hidden rounded-lg bg-white shadow">
|
||||||
<div className="divide-y divide-gray-200 lg:grid lg:grid-cols-12 lg:divide-y-0 lg:divide-x">
|
<div className="divide-y divide-gray-200 lg:grid lg:grid-cols-12 lg:divide-y-0 lg:divide-x">
|
||||||
<aside className="py-6 lg:col-span-3">
|
<aside className="py-6 lg:col-span-3">
|
||||||
|
|||||||
@ -12,7 +12,9 @@ export default function Custom404() {
|
|||||||
|
|
||||||
<div className="mx-auto max-w-7xl px-6 py-48 text-center sm:py-40 lg:px-8">
|
<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>
|
<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>
|
<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">
|
<p className="mt-4 text-base text-gray-700 sm:mt-6">
|
||||||
Sorry, we couldn’t find the page you’re looking for.
|
Sorry, we couldn’t find the page you’re looking for.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -15,7 +15,9 @@ export default function Custom500() {
|
|||||||
<p className="inline-flex items-center text-3xl font-bold sm:text-5xl">
|
<p className="inline-flex items-center text-3xl font-bold sm:text-5xl">
|
||||||
500
|
500
|
||||||
<span className="relative -top-1.5 px-3 font-thin sm:text-6xl">|</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>
|
<span className="align-middle text-base font-semibold sm:text-2xl">
|
||||||
|
Something went wrong.
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-10 flex justify-center">
|
<div className="mt-10 flex justify-center">
|
||||||
<Button color="secondary" href="/" icon={ArrowSmallLeftIcon}>
|
<Button color="secondary" href="/" icon={ArrowSmallLeftIcon}>
|
||||||
|
|||||||
@ -18,7 +18,10 @@ type AppPropsWithLayout = AppProps & {
|
|||||||
Component: NextPageWithLayout;
|
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);
|
const getLayout = Component.getLayout || ((page: any) => page);
|
||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
|
|||||||
@ -12,7 +12,12 @@ import {
|
|||||||
ExclamationTriangleIcon,
|
ExclamationTriangleIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
import { DocumentStatus, Document as PrismaDocument, SendStatus, SigningStatus } from "@prisma/client";
|
import {
|
||||||
|
DocumentStatus,
|
||||||
|
Document as PrismaDocument,
|
||||||
|
SendStatus,
|
||||||
|
SigningStatus,
|
||||||
|
} from "@prisma/client";
|
||||||
import { truncate } from "fs";
|
import { truncate } from "fs";
|
||||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||||
|
|
||||||
@ -50,7 +55,9 @@ const DashboardPage: NextPageWithLayout = (props: any) => {
|
|||||||
|
|
||||||
<div className="py-10 max-sm:px-4">
|
<div className="py-10 max-sm:px-4">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<dl className="mt-8 grid gap-5 md:grid-cols-3 ">
|
<dl className="mt-8 grid gap-5 md:grid-cols-3 ">
|
||||||
{stats.map((item) => (
|
{stats.map((item) => (
|
||||||
@ -143,7 +150,9 @@ export async function getServerSideProps(context: any) {
|
|||||||
e.Recipient.some((r: any) => r.signingStatus === SigningStatus.NOT_SIGNED)
|
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 {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@ -107,7 +107,9 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
<div className="mt-10 sm:flex sm:items-center">
|
<div className="mt-10 sm:flex sm:items-center">
|
||||||
<div className="sm:flex-auto">
|
<div className="sm:flex-auto">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
<div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
@ -165,16 +167,24 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
<table className="min-w-full divide-y divide-gray-300">
|
<table className="min-w-full divide-y divide-gray-300">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50">
|
||||||
<tr>
|
<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
|
Title
|
||||||
</th>
|
</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
|
Recipients
|
||||||
</th>
|
</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
|
Status
|
||||||
</th>
|
</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
|
Created
|
||||||
</th>
|
</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">
|
||||||
@ -215,7 +225,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
)}
|
)}
|
||||||
{item.readStatus === "OPENED" && item.signingStatus === "NOT_SIGNED" ? (
|
{item.readStatus === "OPENED" &&
|
||||||
|
item.signingStatus === "NOT_SIGNED" ? (
|
||||||
<span id="read_icon">
|
<span id="read_icon">
|
||||||
<span
|
<span
|
||||||
id="sent_icon"
|
id="sent_icon"
|
||||||
@ -231,7 +242,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
{item.signingStatus === "SIGNED" ? (
|
{item.signingStatus === "SIGNED" ? (
|
||||||
<span id="signed_icon">
|
<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">
|
<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="mr-1 inline h-5"></CheckBadgeIcon> {item.email}
|
<CheckBadgeIcon className="mr-1 inline h-5"></CheckBadgeIcon>{" "}
|
||||||
|
{item.email}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
@ -260,8 +272,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
{formatDocumentStatus(document.status)}
|
{formatDocumentStatus(document.status)}
|
||||||
<p>
|
<p>
|
||||||
<small hidden={document.Recipient.length === 0}>
|
<small hidden={document.Recipient.length === 0}>
|
||||||
{document.Recipient.filter((r: any) => r.signingStatus === "SIGNED").length ||
|
{document.Recipient.filter((r: any) => r.signingStatus === "SIGNED")
|
||||||
0}
|
.length || 0}
|
||||||
/{document.Recipient.length || 0}
|
/{document.Recipient.length || 0}
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
@ -317,8 +329,8 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div hidden={filteredDocuments.length > 0} className="mx-auto mt-12 w-fit p-3">
|
<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
|
<FunnelIcon className="mr-1 inline w-5 align-middle" /> Nothing here. Maybe try a
|
||||||
filter.
|
different filter.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -339,7 +351,9 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<h3 className="mt-2 text-sm font-medium text-gray-900">No documents</h3>
|
<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">
|
<div className="mt-6">
|
||||||
<Button
|
<Button
|
||||||
icon={PlusIcon}
|
icon={PlusIcon}
|
||||||
@ -359,7 +373,11 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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."
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -40,7 +40,10 @@ const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
|||||||
</h2>
|
</h2>
|
||||||
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
|
<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">
|
<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`}>
|
<Link href={`/documents/${props.document.id}/recipients`}>
|
||||||
{props?.document?.Recipient?.length} Recipients
|
{props?.document?.Recipient?.length} Recipients
|
||||||
@ -61,7 +64,9 @@ const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
|
|||||||
className="ml-3"
|
className="ml-3"
|
||||||
href={NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + "/recipients"}
|
href={NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + "/recipients"}
|
||||||
onClick={() => {
|
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
|
Prepare to Send
|
||||||
|
|||||||
@ -105,7 +105,9 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
|||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
(formValues.length || 0) === 0 ||
|
(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
|
loading
|
||||||
}>
|
}>
|
||||||
Send
|
Send
|
||||||
@ -115,7 +117,9 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
|||||||
<div className="mt-10 overflow-hidden rounded-md bg-white p-4 shadow sm:p-6">
|
<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">
|
<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>
|
<h3 className="text-lg font-medium leading-6 text-gray-900 ">Signers</h3>
|
||||||
<p className="mt-2 max-w-4xl 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>
|
</div>
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<form
|
<form
|
||||||
@ -124,7 +128,9 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
|||||||
}}>
|
}}>
|
||||||
<ul role="list" className="divide-y divide-gray-200">
|
<ul role="list" className="divide-y divide-gray-200">
|
||||||
{fields.map((item: any, index: number) => (
|
{fields.map((item: any, index: number) => (
|
||||||
<li key={index} className="group w-full border-0 px-2 py-3 hover:bg-green-50 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 id="container" className="block w-full lg:flex lg:justify-between">
|
||||||
<div className="block space-y-2 md:flex md:space-x-2 md:space-y-0">
|
<div className="block space-y-2 md:flex md:space-x-2 md:space-y-0">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -22,8 +22,8 @@ const SignPage: NextPageWithLayout = (props: any) => {
|
|||||||
<h1 className="text-neon inline align-middle text-base font-medium">Time flies.</h1>
|
<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-4xl font-bold tracking-tight">This signing link is expired.</p>
|
||||||
<p className="mt-2 text-base text-gray-500">
|
<p className="mt-2 text-base text-gray-500">
|
||||||
Please ask {props.document.User.name ? `${props.document.User.name}` : `the sender`} to resend
|
Please ask {props.document.User.name ? `${props.document.User.name}` : `the sender`}{" "}
|
||||||
it.
|
to resend it.
|
||||||
</p>
|
</p>
|
||||||
<div className="mx-auto w-fit pt-20 text-xl"></div>
|
<div className="mx-auto w-fit pt-20 text-xl"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import { ArrowDownTrayIcon, CheckBadgeIcon } from "@heroicons/react/24/outline";
|
|||||||
|
|
||||||
const Signed: NextPageWithLayout = (props: any) => {
|
const Signed: NextPageWithLayout = (props: any) => {
|
||||||
const router = useRouter();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -18,7 +20,9 @@ const Signed: NextPageWithLayout = (props: any) => {
|
|||||||
<div className="mx-auto w-fit px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
|
<div className="mx-auto w-fit px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
|
||||||
<CheckBadgeIcon className="text-neon mr-1 inline w-10"></CheckBadgeIcon>
|
<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>
|
<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 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}>
|
<p className="mt-2 max-w-sm text-base text-gray-500" hidden={allRecipientsSigned}>
|
||||||
You will be notfied when all recipients have signed.
|
You will be notfied when all recipients have signed.
|
||||||
</p>
|
</p>
|
||||||
@ -32,7 +36,9 @@ const Signed: NextPageWithLayout = (props: any) => {
|
|||||||
onClick={(event: any) => {
|
onClick={(event: any) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
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}"
|
Download "{props.document.title}"
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -24,8 +24,8 @@ body,
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("/fonts/montserrat.woff2") format("woff2");
|
src: url("/fonts/montserrat.woff2") format("woff2");
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074,
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
|
||||||
U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* latin */
|
/* latin */
|
||||||
@ -35,6 +35,6 @@ body,
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("/fonts/montserrat.woff2") format("woff2");
|
src: url("/fonts/montserrat.woff2") format("woff2");
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074,
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
|
||||||
U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,9 @@ export const uploadDocument = async (event: any) => {
|
|||||||
)
|
)
|
||||||
.then((response: Response) => {
|
.then((response: Response) => {
|
||||||
response.json().then((createdDocumentIdFromBody) => {
|
response.json().then((createdDocumentIdFromBody) => {
|
||||||
router.push(`${NEXT_PUBLIC_WEBAPP_URL}/documents/${createdDocumentIdFromBody}/recipients`);
|
router.push(
|
||||||
|
`${NEXT_PUBLIC_WEBAPP_URL}/documents/${createdDocumentIdFromBody}/recipients`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,9 @@ export function isPasswordValid(password: string, breakdown?: boolean, strict?:
|
|||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CtxOrReq = { req: NextApiRequest; ctx?: never } | { ctx: { req: NextApiRequest }; req?: never };
|
type CtxOrReq =
|
||||||
|
| { req: NextApiRequest; ctx?: never }
|
||||||
|
| { ctx: { req: NextApiRequest }; req?: never };
|
||||||
|
|
||||||
export const ensureSession = async (ctxOrReq: CtxOrReq) => {
|
export const ensureSession = async (ctxOrReq: CtxOrReq) => {
|
||||||
const session = await getSession(ctxOrReq);
|
const session = await getSession(ctxOrReq);
|
||||||
|
|||||||
@ -12,7 +12,11 @@ export const getSafeRedirectUrl = (url = "") => {
|
|||||||
const urlParsed = new URL(url);
|
const urlParsed = new URL(url);
|
||||||
|
|
||||||
// Avoid open redirection security vulnerability
|
// Avoid open redirection security vulnerability
|
||||||
if (!["CONSOLE_URL", "WEBAPP_URL", "WEBSITE_URL"].some((u) => new URL(u).origin === urlParsed.origin)) {
|
if (
|
||||||
|
!["CONSOLE_URL", "WEBAPP_URL", "WEBSITE_URL"].some(
|
||||||
|
(u) => new URL(u).origin === urlParsed.origin
|
||||||
|
)
|
||||||
|
) {
|
||||||
url = `${"WEBAPP_URL"}/`;
|
url = `${"WEBAPP_URL"}/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,11 @@ import { getUserFromToken } from "@documenso/lib/server";
|
|||||||
import prisma from "@documenso/prisma";
|
import prisma from "@documenso/prisma";
|
||||||
import { Document as PrismaDocument } from "@prisma/client";
|
import { Document as PrismaDocument } from "@prisma/client";
|
||||||
|
|
||||||
export const getDocument = async (documentId: number, req: any, res: any): Promise<PrismaDocument> => {
|
export const getDocument = async (
|
||||||
|
documentId: number,
|
||||||
|
req: any,
|
||||||
|
res: any
|
||||||
|
): Promise<PrismaDocument> => {
|
||||||
const user = await getUserFromToken(req, res);
|
const user = await getUserFromToken(req, res);
|
||||||
if (!user) return Promise.reject("Invalid user or token.");
|
if (!user) return Promise.reject("Invalid user or token.");
|
||||||
if (!documentId) Promise.reject("No documentId");
|
if (!documentId) Promise.reject("No documentId");
|
||||||
|
|||||||
@ -1,24 +1,27 @@
|
|||||||
import type { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
type Handlers = {
|
type Handlers = {
|
||||||
[method in "GET" | "POST" | "PATCH" | "PUT" | "DELETE"]?: Promise<{ default: NextApiHandler }>;
|
[method in "GET" | "POST" | "PATCH" | "PUT" | "DELETE"]?: Promise<{
|
||||||
|
default: NextApiHandler;
|
||||||
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Allows us to split big API handlers by method */
|
/** Allows us to split big API handlers by method */
|
||||||
export const defaultHandler = (handlers: Handlers) => async (req: NextApiRequest, res: NextApiResponse) => {
|
export const defaultHandler =
|
||||||
const handler = (await handlers[req.method as keyof typeof handlers])?.default;
|
(handlers: Handlers) => async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
// auto catch unsupported methods.
|
const handler = (await handlers[req.method as keyof typeof handlers])?.default;
|
||||||
if (!handler) {
|
// auto catch unsupported methods.
|
||||||
return res
|
if (!handler) {
|
||||||
.status(405)
|
return res.status(405).json({
|
||||||
.json({ message: `Method Not Allowed (Allow: ${Object.keys(handlers).join(",")})` });
|
message: `Method Not Allowed (Allow: ${Object.keys(handlers).join(",")})`,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await handler(req, res);
|
await handler(req, res);
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return res.status(500).json({ message: "Something went wrong" });
|
return res.status(500).json({ message: "Something went wrong" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,13 @@ export class HttpError<TCode extends number = number> extends Error {
|
|||||||
public readonly url: string | undefined;
|
public readonly url: string | undefined;
|
||||||
public readonly method: string | undefined;
|
public readonly method: string | undefined;
|
||||||
|
|
||||||
constructor(opts: { url?: string; method?: string; message?: string; statusCode: TCode; cause?: Error }) {
|
constructor(opts: {
|
||||||
|
url?: string;
|
||||||
|
method?: string;
|
||||||
|
message?: string;
|
||||||
|
statusCode: TCode;
|
||||||
|
cause?: Error;
|
||||||
|
}) {
|
||||||
super(opts.message ?? `HTTP Error ${opts.statusCode} `);
|
super(opts.message ?? `HTTP Error ${opts.statusCode} `);
|
||||||
|
|
||||||
Object.setPrototypeOf(this, HttpError.prototype);
|
Object.setPrototypeOf(this, HttpError.prototype);
|
||||||
|
|||||||
@ -15,7 +15,9 @@ async function createUser(userData: { email: string; password: string }) {
|
|||||||
|
|
||||||
return user;
|
return user;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.info(`WARN: Could not create user "${userData.email}". Maybe the email is already taken?`);
|
console.info(
|
||||||
|
`WARN: Could not create user "${userData.email}". Maybe the email is already taken?`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,10 @@ const extractSignature = (pdf, signatureCount = 1) => {
|
|||||||
const byteRangePos = getSubstringIndex(pdf, "/ByteRange [", signatureCount);
|
const byteRangePos = getSubstringIndex(pdf, "/ByteRange [", signatureCount);
|
||||||
|
|
||||||
if (byteRangePos === -1) {
|
if (byteRangePos === -1) {
|
||||||
throw new _SignPdfError.default("Failed to locate ByteRange.", _SignPdfError.default.TYPE_PARSE);
|
throw new _SignPdfError.default(
|
||||||
|
"Failed to locate ByteRange.",
|
||||||
|
_SignPdfError.default.TYPE_PARSE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const byteRangeEnd = pdf.indexOf("]", byteRangePos);
|
const byteRangeEnd = pdf.indexOf("]", byteRangePos);
|
||||||
@ -56,7 +59,10 @@ const extractSignature = (pdf, signatureCount = 1) => {
|
|||||||
const matches = /\/ByteRange \[(\d+) +(\d+) +(\d+) +(\d+) *\]/.exec(byteRange);
|
const matches = /\/ByteRange \[(\d+) +(\d+) +(\d+) +(\d+) *\]/.exec(byteRange);
|
||||||
|
|
||||||
if (matches === null) {
|
if (matches === null) {
|
||||||
throw new _SignPdfError.default("Failed to parse the ByteRange.", _SignPdfError.default.TYPE_PARSE);
|
throw new _SignPdfError.default(
|
||||||
|
"Failed to parse the ByteRange.",
|
||||||
|
_SignPdfError.default.TYPE_PARSE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ByteRange = matches.slice(1).map(Number);
|
const ByteRange = matches.slice(1).map(Number);
|
||||||
|
|||||||
@ -87,7 +87,10 @@ class PDFObject {
|
|||||||
let string = `D:${pad(object.getUTCFullYear(), 4)}${pad(object.getUTCMonth() + 1, 2)}${pad(
|
let string = `D:${pad(object.getUTCFullYear(), 4)}${pad(object.getUTCMonth() + 1, 2)}${pad(
|
||||||
object.getUTCDate(),
|
object.getUTCDate(),
|
||||||
2
|
2
|
||||||
)}${pad(object.getUTCHours(), 2)}${pad(object.getUTCMinutes(), 2)}${pad(object.getUTCSeconds(), 2)}Z`; // Encrypt the string when necessary
|
)}${pad(object.getUTCHours(), 2)}${pad(object.getUTCMinutes(), 2)}${pad(
|
||||||
|
object.getUTCSeconds(),
|
||||||
|
2
|
||||||
|
)}Z`; // Encrypt the string when necessary
|
||||||
|
|
||||||
if (encryptFn) {
|
if (encryptFn) {
|
||||||
string = encryptFn(Buffer.from(string, "ascii")).toString("binary"); // Escape characters as required by the spec
|
string = encryptFn(Buffer.from(string, "ascii")).toString("binary"); // Escape characters as required by the spec
|
||||||
|
|||||||
@ -21,7 +21,10 @@ const getIndexFromRef = (refTable, ref) => {
|
|||||||
index = parseInt(index);
|
index = parseInt(index);
|
||||||
|
|
||||||
if (!refTable.offsets.has(index)) {
|
if (!refTable.offsets.has(index)) {
|
||||||
throw new _SignPdfError.default(`Failed to locate object "${ref}".`, _SignPdfError.default.TYPE_PARSE);
|
throw new _SignPdfError.default(
|
||||||
|
`Failed to locate object "${ref}".`,
|
||||||
|
_SignPdfError.default.TYPE_PARSE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
|
|||||||
@ -21,9 +21,13 @@ var _readPdf = _interopRequireDefault(require("./readPdf"));
|
|||||||
|
|
||||||
var _getPageRef = _interopRequireDefault(require("./getPageRef"));
|
var _getPageRef = _interopRequireDefault(require("./getPageRef"));
|
||||||
|
|
||||||
var _createBufferRootWithAcroform = _interopRequireDefault(require("./createBufferRootWithAcroform"));
|
var _createBufferRootWithAcroform = _interopRequireDefault(
|
||||||
|
require("./createBufferRootWithAcroform")
|
||||||
|
);
|
||||||
|
|
||||||
var _createBufferPageWithAnnotation = _interopRequireDefault(require("./createBufferPageWithAnnotation"));
|
var _createBufferPageWithAnnotation = _interopRequireDefault(
|
||||||
|
require("./createBufferPageWithAnnotation")
|
||||||
|
);
|
||||||
|
|
||||||
var _createBufferTrailer = _interopRequireDefault(require("./createBufferTrailer"));
|
var _createBufferTrailer = _interopRequireDefault(require("./createBufferTrailer"));
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,11 @@
|
|||||||
Object.defineProperty(exports, "__esModule", {
|
Object.defineProperty(exports, "__esModule", {
|
||||||
value: true,
|
value: true,
|
||||||
});
|
});
|
||||||
exports.getXref = exports.getLastTrailerPosition = exports.getFullXrefTable = exports.default = void 0;
|
exports.getXref =
|
||||||
|
exports.getLastTrailerPosition =
|
||||||
|
exports.getFullXrefTable =
|
||||||
|
exports.default =
|
||||||
|
void 0;
|
||||||
|
|
||||||
var _SignPdfError = _interopRequireDefault(require("../../SignPdfError"));
|
var _SignPdfError = _interopRequireDefault(require("../../SignPdfError"));
|
||||||
|
|
||||||
@ -16,7 +20,9 @@ function _interopRequireDefault(obj) {
|
|||||||
const getLastTrailerPosition = (pdf) => {
|
const getLastTrailerPosition = (pdf) => {
|
||||||
const trailerStart = pdf.lastIndexOf(Buffer.from("trailer", "utf8"));
|
const trailerStart = pdf.lastIndexOf(Buffer.from("trailer", "utf8"));
|
||||||
const trailer = pdf.slice(trailerStart, pdf.length - 6);
|
const trailer = pdf.slice(trailerStart, pdf.length - 6);
|
||||||
const xRefPosition = trailer.slice(trailer.lastIndexOf(Buffer.from("startxref", "utf8")) + 10).toString();
|
const xRefPosition = trailer
|
||||||
|
.slice(trailer.lastIndexOf(Buffer.from("startxref", "utf8")) + 10)
|
||||||
|
.toString();
|
||||||
return parseInt(xRefPosition);
|
return parseInt(xRefPosition);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,13 +69,19 @@ const getXref = (pdf, position) => {
|
|||||||
let size = refTable.toString().split("/Size")[1];
|
let size = refTable.toString().split("/Size")[1];
|
||||||
|
|
||||||
if (!size) {
|
if (!size) {
|
||||||
throw new _SignPdfError.default("Size not found in xref table.", _SignPdfError.default.TYPE_PARSE);
|
throw new _SignPdfError.default(
|
||||||
|
"Size not found in xref table.",
|
||||||
|
_SignPdfError.default.TYPE_PARSE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
size = /^\s*(\d+)/.exec(size);
|
size = /^\s*(\d+)/.exec(size);
|
||||||
|
|
||||||
if (size === null) {
|
if (size === null) {
|
||||||
throw new _SignPdfError.default("Failed to parse size of xref table.", _SignPdfError.default.TYPE_PARSE);
|
throw new _SignPdfError.default(
|
||||||
|
"Failed to parse size of xref table.",
|
||||||
|
_SignPdfError.default.TYPE_PARSE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
size = parseInt(size[1]);
|
size = parseInt(size[1]);
|
||||||
|
|||||||
@ -26,7 +26,10 @@ const xrefToRefMap = (xrefString) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (expectedLines <= 0) {
|
if (expectedLines <= 0) {
|
||||||
throw new _SignPdfError.default("Too many lines in xref table.", _SignPdfError.default.TYPE_PARSE);
|
throw new _SignPdfError.default(
|
||||||
|
"Too many lines in xref table.",
|
||||||
|
_SignPdfError.default.TYPE_PARSE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedLines -= 1;
|
expectedLines -= 1;
|
||||||
|
|||||||
23
packages/signing/node-signpdf/dist/signpdf.js
vendored
23
packages/signing/node-signpdf/dist/signpdf.js
vendored
@ -102,9 +102,16 @@ class SignPdf {
|
|||||||
let actualByteRange = `/ByteRange [${byteRange.join(" ")}]`;
|
let actualByteRange = `/ByteRange [${byteRange.join(" ")}]`;
|
||||||
actualByteRange += " ".repeat(byteRangePlaceholder.length - actualByteRange.length); // Replace the /ByteRange placeholder with the actual ByteRange
|
actualByteRange += " ".repeat(byteRangePlaceholder.length - actualByteRange.length); // Replace the /ByteRange placeholder with the actual ByteRange
|
||||||
|
|
||||||
pdf = Buffer.concat([pdf.slice(0, byteRangePos), Buffer.from(actualByteRange), pdf.slice(byteRangeEnd)]); // Remove the placeholder signature
|
pdf = Buffer.concat([
|
||||||
|
pdf.slice(0, byteRangePos),
|
||||||
|
Buffer.from(actualByteRange),
|
||||||
|
pdf.slice(byteRangeEnd),
|
||||||
|
]); // Remove the placeholder signature
|
||||||
|
|
||||||
pdf = Buffer.concat([pdf.slice(0, byteRange[1]), pdf.slice(byteRange[2], byteRange[2] + byteRange[3])]); // Convert Buffer P12 to a forge implementation.
|
pdf = Buffer.concat([
|
||||||
|
pdf.slice(0, byteRange[1]),
|
||||||
|
pdf.slice(byteRange[2], byteRange[2] + byteRange[3]),
|
||||||
|
]); // Convert Buffer P12 to a forge implementation.
|
||||||
|
|
||||||
const forgeCert = _nodeForge.default.util.createBuffer(p12Buffer.toString("binary"));
|
const forgeCert = _nodeForge.default.util.createBuffer(p12Buffer.toString("binary"));
|
||||||
|
|
||||||
@ -190,11 +197,15 @@ class SignPdf {
|
|||||||
|
|
||||||
this.lastSignature = signature; // Pad the signature with zeroes so the it is the same length as the placeholder
|
this.lastSignature = signature; // Pad the signature with zeroes so the it is the same length as the placeholder
|
||||||
|
|
||||||
signature += Buffer.from(String.fromCharCode(0).repeat(placeholderLength / 2 - raw.length)).toString(
|
signature += Buffer.from(
|
||||||
"hex"
|
String.fromCharCode(0).repeat(placeholderLength / 2 - raw.length)
|
||||||
); // Place it in the document.
|
).toString("hex"); // Place it in the document.
|
||||||
|
|
||||||
pdf = Buffer.concat([pdf.slice(0, byteRange[1]), Buffer.from(`<${signature}>`), pdf.slice(byteRange[1])]); // Magic. Done.
|
pdf = Buffer.concat([
|
||||||
|
pdf.slice(0, byteRange[1]),
|
||||||
|
Buffer.from(`<${signature}>`),
|
||||||
|
pdf.slice(byteRange[1]),
|
||||||
|
]); // Magic. Done.
|
||||||
|
|
||||||
return pdf;
|
return pdf;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,14 @@ export function Breadcrumb(props: any) {
|
|||||||
<>
|
<>
|
||||||
<nav className="sm:hidden" aria-label="Back">
|
<nav className="sm:hidden" aria-label="Back">
|
||||||
<Link
|
<Link
|
||||||
href={props.items.length > 1 ? props.items[props.items.length - 2].href : props.items[0].href}
|
href={
|
||||||
|
props.items.length > 1 ? props.items[props.items.length - 2].href : props.items[0].href
|
||||||
|
}
|
||||||
className="flex items-center text-sm font-medium text-gray-500 hover:text-gray-700">
|
className="flex items-center text-sm font-medium text-gray-500 hover:text-gray-700">
|
||||||
<ChevronLeftIcon className="-ml-1 mr-1 h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
|
<ChevronLeftIcon
|
||||||
|
className="-ml-1 mr-1 h-5 w-5 flex-shrink-0 text-gray-400"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
Back
|
Back
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
@ -18,13 +23,18 @@ export function Breadcrumb(props: any) {
|
|||||||
{props?.items.map((item: any, index: number) => (
|
{props?.items.map((item: any, index: number) => (
|
||||||
<React.Fragment key={item.href}>
|
<React.Fragment key={item.href}>
|
||||||
{index > 0 ? (
|
{index > 0 ? (
|
||||||
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
|
<ChevronRightIcon
|
||||||
|
className="h-5 w-5 flex-shrink-0 text-gray-400"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
)}
|
)}
|
||||||
<li>
|
<li>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Link href={item.href} className="text-sm font-medium text-gray-500 hover:text-gray-700">
|
<Link
|
||||||
|
href={item.href}
|
||||||
|
className="text-sm font-medium text-gray-500 hover:text-gray-700">
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,8 +21,18 @@ type DialogProps = {
|
|||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Dialog({ title, open, setOpen, document, formValues, setLoading, icon }: DialogProps) {
|
export function Dialog({
|
||||||
const unsentEmailsLength = formValues.filter((s: any) => s.email && s.sendStatus != "SENT").length;
|
title,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
document,
|
||||||
|
formValues,
|
||||||
|
setLoading,
|
||||||
|
icon,
|
||||||
|
}: DialogProps) {
|
||||||
|
const unsentEmailsLength = formValues.filter(
|
||||||
|
(s: any) => s.email && s.sendStatus != "SENT"
|
||||||
|
).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition.Root show={open} as={Fragment}>
|
<Transition.Root show={open} as={Fragment}>
|
||||||
@ -54,7 +64,9 @@ export function Dialog({ title, open, setOpen, document, formValues, setLoading,
|
|||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
<DialogComponent.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
|
<DialogComponent.Title
|
||||||
|
as="h3"
|
||||||
|
className="text-lg font-medium leading-6 text-gray-900">
|
||||||
{title}
|
{title}
|
||||||
</DialogComponent.Title>
|
</DialogComponent.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
|
|||||||
@ -13,7 +13,9 @@ export function SelectBox(props: any) {
|
|||||||
}}>
|
}}>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<Listbox.Label className="block text-sm font-medium text-gray-700">{props.label}</Listbox.Label>
|
<Listbox.Label className="block text-sm font-medium text-gray-700">
|
||||||
|
{props.label}
|
||||||
|
</Listbox.Label>
|
||||||
<div className="relative mt-1">
|
<div className="relative mt-1">
|
||||||
<Listbox.Button className="focus:border-neon focus:ring-neon relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm focus:outline-none focus:ring-1 sm:text-sm">
|
<Listbox.Button className="focus:border-neon focus:ring-neon relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm focus:outline-none focus:ring-1 sm:text-sm">
|
||||||
<span className="block truncate">{props?.value?.label}</span>
|
<span className="block truncate">{props?.value?.label}</span>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module.exports = {
|
|||||||
jsxSingleQuote: false,
|
jsxSingleQuote: false,
|
||||||
trailingComma: "es5",
|
trailingComma: "es5",
|
||||||
semi: true,
|
semi: true,
|
||||||
printWidth: 110,
|
printWidth: 100,
|
||||||
arrowParens: "always",
|
arrowParens: "always",
|
||||||
importOrder: [
|
importOrder: [
|
||||||
"^(react/(.*)$)|^(react$)",
|
"^(react/(.*)$)|^(react$)",
|
||||||
|
|||||||
Reference in New Issue
Block a user