Merge branch 'main' into bugfix/long_filename

This commit is contained in:
Ansari
2023-05-30 14:30:55 +05:30
committed by GitHub
75 changed files with 3266 additions and 152 deletions

View File

@ -1,14 +1,16 @@
import { Button } from "@documenso/ui";
import Logo from "../components/logo";
import { ArrowSmallLeftIcon } from "@heroicons/react/20/solid";
import Link from "next/link";
export default function Custom404() {
return (
<>
<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>
<Link href="/" className="absolute top-10 left-10 flex gap-x-2 items-center">
<Logo className="w-10" />
<h2 className="text-2xl font-semibold">Documenso</h2>
</Link>
<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>

View File

@ -2,14 +2,16 @@ import { Button } from "@documenso/ui";
import Logo from "../components/logo";
import { ArrowSmallLeftIcon } from "@heroicons/react/20/solid";
import { EllipsisVerticalIcon } from "@heroicons/react/20/solid";
import Link from "next/link";
export default function Custom500() {
return (
<>
<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>
<Link href="/" className="absolute top-10 left-10 flex gap-x-2 items-center invert">
<Logo className="w-10" />
<h2 className="text-2xl font-semibold text-black">Documenso</h2>
</Link>
<div className="mt-20 max-w-7xl px-4 py-10">
<p className="inline-flex items-center text-3xl font-bold sm:text-5xl">

View File

@ -1,6 +1,8 @@
import { ReactElement, ReactNode } from "react";
import { NextPage } from "next";
import type { AppProps } from "next/app";
import { Montserrat, Qwigley } from "next/font/google";
import { SubscriptionProvider } from "@documenso/lib/stripe/providers/subscription-provider";
import "../../../node_modules/placeholder-loading/src/scss/placeholder-loading.scss";
import "../../../node_modules/react-resizable/css/styles.css";
import "../styles/tailwind.css";
@ -10,6 +12,20 @@ import "react-tooltip/dist/react-tooltip.css";
export { coloredConsole } from "@documenso/lib";
const montserrat = Montserrat({
subsets: ["latin"],
weight: ["400", "700"],
display: "swap",
variable: "--font-sans",
});
const qwigley = Qwigley({
subsets: ["latin"],
weight: ["400"],
display: "swap",
variable: "--font-qwigley",
});
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
};
@ -20,13 +36,17 @@ type AppPropsWithLayout = AppProps & {
export default function App({
Component,
pageProps: { session, ...pageProps },
pageProps: { session, initialSubscription, ...pageProps },
}: AppPropsWithLayout) {
const getLayout = Component.getLayout || ((page: any) => page);
return (
<SessionProvider session={session}>
<Toaster position="top-center"></Toaster>
{getLayout(<Component {...pageProps} />)}
<SubscriptionProvider initialSubscription={initialSubscription}>
<main className={`${montserrat.variable} h-full font-sans`}>
<Toaster position="top-center" />
{getLayout(<Component {...pageProps} />)}
</main>
</SubscriptionProvider>
</SessionProvider>
);
}

View File

@ -1,13 +1,14 @@
import { Head, Html, Main, NextScript } from "next/document";
import Script from "next/script";
export default function Document(props) {
let pageProps = props.__NEXT_DATA__?.props?.pageProps;
export default function Document() {
return (
<Html className="h-full scroll-smooth bg-gray-100 font-normal antialiased" lang="en">
<Head>
<meta name="color-scheme"></meta>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
</Head>
<body className="flex h-full flex-col">
<Main />

View File

@ -4,6 +4,7 @@ import { defaultHandler, defaultResponder } from "@documenso/lib/server";
import { getUserFromToken } from "@documenso/lib/server";
import prisma from "@documenso/prisma";
import formidable from "formidable";
import { isSubscribedServer } from "@documenso/lib/stripe";
export const config = {
api: {
@ -15,7 +16,17 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
const form = formidable();
const user = await getUserFromToken(req, res);
if (!user) return;
if (!user) {
return res.status(401).end();
};
const isSubscribed = await isSubscribedServer(req);
if (!isSubscribed) {
throw new Error("User is not subscribed.");
}
form.parse(req, async (err, fields, files) => {
if (err) throw err;

View File

@ -0,0 +1 @@
export { checkoutSessionHandler as default } from '@documenso/lib/stripe/handlers/checkout-session'

View File

@ -0,0 +1 @@
export { portalSessionHandler as default } from "@documenso/lib/stripe/handlers/portal-session";

View File

@ -0,0 +1 @@
export { getSubscriptionHandler as default } from '@documenso/lib/stripe/handlers/get-subscription'

View File

@ -0,0 +1,5 @@
export const config = {
api: { bodyParser: false },
};
export { webhookHandler as default } from "@documenso/lib/stripe/handlers/webhook";

View File

@ -1,4 +1,5 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { hashPassword } from "@documenso/lib/auth";
import { defaultHandler, defaultResponder, getUserFromToken } from "@documenso/lib/server";
import prisma from "@documenso/prisma";
@ -24,6 +25,13 @@ async function patchHandler(req: NextApiRequest, res: NextApiResponse) {
if (!user) return;
const updatedUser = req.body;
let password: string | undefined = undefined;
if (typeof updatedUser.password === "string" && updatedUser.password.length >= 6) {
password = await hashPassword(updatedUser.password);
}
await prisma.user
.update({
where: {
@ -31,6 +39,7 @@ async function patchHandler(req: NextApiRequest, res: NextApiResponse) {
},
data: {
name: updatedUser.name,
password,
},
})
.then(() => {

View File

@ -20,12 +20,15 @@ import {
} from "@prisma/client";
import { truncate } from "fs";
import { Tooltip as ReactTooltip } from "react-tooltip";
import { useSubscription } from "@documenso/lib/stripe";
type FormValues = {
document: File;
};
const DashboardPage: NextPageWithLayout = (props: any) => {
const { hasSubscription } = useSubscription();
const stats = [
{
name: "Draft",
@ -90,9 +93,12 @@ const DashboardPage: NextPageWithLayout = (props: any) => {
</div>
<div
onClick={() => {
document?.getElementById("fileUploadHelper")?.click();
if (hasSubscription) {
document?.getElementById("fileUploadHelper")?.click();
}
}}
className="group hover:border-neon-600 duration-200 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">
aria-disabled={!hasSubscription}
className="group hover:border-neon-600 duration-200 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 aria-disabled:opacity-50 aria-disabled:pointer-events-none">
<svg
className="mx-auto h-12 w-12 text-gray-400 group-hover:text-gray-700 duration-200"

View File

@ -20,9 +20,11 @@ import {
} from "@heroicons/react/24/outline";
import { DocumentStatus } from "@prisma/client";
import { Tooltip as ReactTooltip } from "react-tooltip";
import { useSubscription } from "@documenso/lib/stripe";
const DocumentsPage: NextPageWithLayout = (props: any) => {
const router = useRouter();
const { hasSubscription } = useSubscription();
const [documents, setDocuments]: any[] = useState([]);
const [filteredDocuments, setFilteredDocuments] = useState([]);
@ -135,6 +137,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
<div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<Button
icon={DocumentPlusIcon}
disabled={!hasSubscription}
onClick={() => {
document?.getElementById("fileUploadHelper")?.click();
}}>
@ -378,6 +381,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
<div className="mt-6">
<Button
icon={PlusIcon}
disabled={!hasSubscription}
onClick={() => {
document?.getElementById("fileUploadHelper")?.click();
}}>

View File

@ -4,6 +4,7 @@ import { useRouter } from "next/router";
import { NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib";
import { getDocument } from "@documenso/lib/query";
import { getUserFromToken } from "@documenso/lib/server";
import { useSubscription } from "@documenso/lib/stripe";
import { Breadcrumb, Button } from "@documenso/ui";
import PDFEditor from "../../../components/editor/pdf-editor";
import Layout from "../../../components/layout";
@ -14,6 +15,7 @@ import { Document as PrismaDocument } from "@prisma/client";
const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
const router = useRouter();
const { hasSubscription } = useSubscription();
return (
<div className="mt-4">

View File

@ -21,6 +21,7 @@ import {
import { DocumentStatus, Document as PrismaDocument, Recipient } from "@prisma/client";
import { FormProvider, useFieldArray, useForm, useWatch } from "react-hook-form";
import { toast } from "react-hot-toast";
import { useSubscription } from "@documenso/lib/stripe";
export type FormValues = {
signers: Array<Pick<Recipient, 'id' | 'email' | 'name' | 'sendStatus' | 'readStatus' | 'signingStatus'>>;
@ -29,6 +30,7 @@ export type FormValues = {
type FormSigner = FormValues["signers"][number];
const RecipientsPage: NextPageWithLayout = (props: any) => {
const { hasSubscription } = useSubscription();
const title: string = `"` + props?.document?.title + `"` + "Recipients | Documenso";
const breadcrumbItems = [
{
@ -116,6 +118,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
: setOpen(true);
}}
disabled={
!hasSubscription ||
(formValues.length || 0) === 0 ||
!formValues.some(
(r) => r.email && !hasEmailError(r) && r.sendStatus === "NOT_SENT"

View File

@ -59,7 +59,7 @@ export async function getServerSideProps(context: any) {
},
});
const recipient = await prisma.recipient.findFirstOrThrow({
const recipient = await prisma.recipient.findFirst({
where: {
token: recipientToken,
},
@ -68,12 +68,21 @@ export async function getServerSideProps(context: any) {
},
});
if (!recipient) {
return {
redirect: {
permanent: false,
destination: "/404",
},
};
}
// Document is already signed
if (recipient.Document.status === DocumentStatus.COMPLETED) {
return {
redirect: {
permanent: false,
destination: `/documents/${recipient.Document.id}/signed`,
destination: `/documents/${recipient.Document.id}/signed?token=${recipientToken}`,
},
};
}

View File

@ -56,7 +56,7 @@ const Signed: NextPageWithLayout = (props: any) => {
<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="text-neon hover:text-neon font-medium">
Hosted Documenso is coming soon
Hosted Documenso is here!
</Link>
</p>
</>

View File

@ -24,11 +24,11 @@ export async function getServerSideProps(context: any) {
},
};
const ALLOW_SIGNUP = process.env.ALLOW_SIGNUP === "true";
const ALLOW_SIGNUP = process.env.NEXT_PUBLIC_ALLOW_SIGNUP === "true";
return {
props: {
ALLOW_SIGNUP: ALLOW_SIGNUP,
ALLOW_SIGNUP,
},
};
}

View File

@ -0,0 +1 @@
export { default } from ".";

View File

@ -15,7 +15,7 @@ export default function SignupPage(props: { source: string }) {
}
export async function getServerSideProps(context: any) {
if (process.env.ALLOW_SIGNUP !== "true")
if (process.env.NEXT_PUBLIC_ALLOW_SIGNUP !== "true")
return {
redirect: {
destination: "/login",