mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
Merge branch 'feat/refresh' into feat/update-email-templates
This commit is contained in:
54
apps/marketing/content/blog/malfunction-mania.mdx
Normal file
54
apps/marketing/content/blog/malfunction-mania.mdx
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Announcing Malfunction Mania
|
||||
description: Launch Week Day 2 Y'all! We're getting ready to release Documenso 1.0! Join in on the fun of making sure the open-source alternative to DocuSign is on point. We're calling a 'MALFUNCTION MANIA.'
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2023-09-26
|
||||
tags:
|
||||
- Testing
|
||||
- Rewrite
|
||||
- Bounties
|
||||
---
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/mm.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Malfunctioning Documenso Logo inbroken colors"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
We're calling a MALFUNCTION MANIA! 🚨
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
> TLDR; Documenso 1.0 is in the [staging environment](https://documen.so/staging). Go check it out.
|
||||
|
||||
It's been a minute since Lucas proclaimed, ["We're doing a rewrite"](https://documenso.com/blog/why-were-doing-a-rewrite), and many of you have been asking when the new version will be available. I'm happy to say that the wait has come to an end. The work on Documenso 1.0 has reached a level we feel comfortable going into the next phase with. We had a lot of community feedback, contributions, and moral support to get us this far, which is why we're excited to announce the most extensive community project yet:
|
||||
|
||||
As Documenso 1.0 just hit the staging environment, we're calling a MALFUNCTION MANIA. An enormous, public testing phase, where we invite everyone to try out the new version, hunt down, report, and fix any malfunctions (aka bugs), and give feedback before release. Malfunction Mania will happen alongside our internal testing, and by combining the two, we want to ensure the best possible release we can have for Version 1.0. We know many of you have been eager to contribute; this is your chance (the first of many to come).
|
||||
|
||||
## As part of Malfunction Mania, we're offering special bug bounties
|
||||
|
||||
- We award $25 - $100 per report/ issue/ fix, depending on the severity and if the problem is already known
|
||||
- Bounties will be awarded for fixing reported or other critical issues via accepted Pull Requests (PR)
|
||||
- Just reporting issues in a reproducible way can also be awarded
|
||||
- Smaller but notable contributions like minor issues and documentation will be awarded with exclusive merch as we see fit.
|
||||
|
||||
## What you can do
|
||||
|
||||
- Head over to the [staging environment](https://documen.so/staging), check out the new version and give it a spin;
|
||||
- Check out the [source code](https://github.com/documenso/documenso) on GitHub and look it over;
|
||||
- Spin up the new version locally and try it out.
|
||||
|
||||
## How to get the bounties
|
||||
|
||||
- Report bugs by creating an issue here: [documen.so/issues](https://documen.so/issues);
|
||||
- Fix bugs by creating a Pull Request (PR);
|
||||
- Look over and add missing documentation/ Quickstarts and other useful resources.
|
||||
|
||||
We don't have a specific end date for Malfunction Mania. We plan to move the staging version into the production environment by the end of the month once we're happy with the results. Bug reports and fixes are, of course, always welcome going forward.
|
||||
|
||||
**[Follow Documenso on Twitter / X](https://documen.so/tw) and [join the Discord server](https://documen.so/discord) to get the latest about Malfunction Mania.**
|
||||
@ -20,11 +20,11 @@ Today, I'm pleased to share with you a preview of the next Documenso.
|
||||
|
||||
We redesigned the whole signing flow to make it more appealing and more convenient.
|
||||
|
||||
We improved the overall look and feel by making it more elegant and appropriately playful. Focused on the task at hand, but explicitly enjoying doing it.
|
||||
We improved the overall look and feel by making it more elegant and appropriately playful. Focused on the task at hand, but explicitly enjoying doing it.
|
||||
|
||||
**We call it happy minimalism.**
|
||||
|
||||
We paid particular attention to the moment of signing, which should be celebrated.
|
||||
We paid particular attention to the moment of signing, which should be celebrated.
|
||||
|
||||
The image below is the final bloom of the completion celebration we added:
|
||||
|
||||
|
||||
62
apps/marketing/content/blog/shop.mdx
Normal file
62
apps/marketing/content/blog/shop.mdx
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Shop and Limited Edition "Mania" Shirt
|
||||
description: Happy Launch Week Day 3. The limited edition "Malfunction Mania" shirt is here. Grab it, while you can.
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2023-09-27
|
||||
tags:
|
||||
- Testing
|
||||
- Rewrite
|
||||
- Bounties
|
||||
---
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/mania-shirt.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Malfunctioning Mania Themed Documenso Shirt"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
The Limited Edition "Malfunction Mania" Shirt - Only during Malfunction Mania
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
> TLDR; We have a fancy limited edition shirt. Contribute to Malfunction Mania to get one.
|
||||
|
||||
We kicked off [Malfunction Mania](https://documenso.com/blog/malfunction-mania) yesterday, and the first [Issues](https://github.com/documenso/documenso/issues) are coming in. As mentioned, there will be dollar bounties, but we also wanted to celebrate entering the final stage of version 1.0 with something special. This is why we created the limited edition shirt for Malfunction Mania. It will only be available during the runtime of Malfunction Mania. We have yet to set an exact end date, the next event in October, however, is looming, ready to end MM.
|
||||
|
||||
## Documenso Merch Shop
|
||||
|
||||
The shirt will be available in our [merch shop](https://documen.so/shop) via a unique discount code. While the shirt will be gone after Malfunction Mania, the shop is here to stay and provide a well-deserved reward for great community members and contributors.
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/shop.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Malfunctioning Mania Themed Documenso Shirt"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
Merch at Documenso is always given to those who deserve it.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## How to get the shirt
|
||||
|
||||
If you have been following us, you know we are not big on formalities but highly value rewarding merit. That being said, any worthwhile contribution has a chance to get one. To inspire, here are a few ideas on how to contribute to securing one:
|
||||
|
||||
- Report a bug with detailed reproduction details
|
||||
- Fix a bug (you or somebody else reported)
|
||||
- Analyze and describe a usability or user experience shortcoming
|
||||
- Test the product in a systematic and least somewhat documented way
|
||||
- Engage in discussion about the current version and its choices
|
||||
- Raise awareness for Malfunction Mania and try out the [version currently in staging](https://documen.so/staging)
|
||||
- Review the version with a video, stream, or screenshots and post about it
|
||||
- Review existing or create missing documenso
|
||||
- ...
|
||||
|
||||
**[Follow Documenso on Twitter / X](https://documen.so/tw) and [join the Discord server](https://documen.so/discord) to get the latest about Malfunction Mania.**
|
||||
BIN
apps/marketing/public/blog/mania-shirt.png
Normal file
BIN
apps/marketing/public/blog/mania-shirt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 839 KiB |
BIN
apps/marketing/public/blog/mm.png
Normal file
BIN
apps/marketing/public/blog/mm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 862 KiB |
BIN
apps/marketing/public/blog/shop.png
Normal file
BIN
apps/marketing/public/blog/shop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 956 KiB |
@ -73,7 +73,7 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
|
||||
<ConfettiScreen duration={3000} gravity={0.075} initialVelocityY={50} wind={0.005} />
|
||||
)}
|
||||
|
||||
<h2 className="text-center text-2xl font-semibold leading-normal md:text-3xl lg:mb-2 lg:text-4xl">
|
||||
<h2 className="relative z-10 text-center text-2xl font-semibold leading-normal md:text-3xl lg:mb-2 lg:text-4xl">
|
||||
You have signed
|
||||
<span className="mt-2 block">{document.title}</span>
|
||||
</h2>
|
||||
@ -84,17 +84,17 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
|
||||
signingCelebrationImage={signingCelebration}
|
||||
/>
|
||||
|
||||
<div className="mt-8 w-full">
|
||||
<div className="relative mt-8 w-full">
|
||||
<div className={cn('flex flex-col items-center', className)}>
|
||||
<div className="grid w-full max-w-sm grid-cols-2 gap-4">
|
||||
{/* TODO: Hook this up */}
|
||||
<Button variant="outline" className="flex-1" disabled>
|
||||
<Button variant="outline" className="flex-1 bg-transparent backdrop-blur-sm" disabled>
|
||||
<Share className="mr-2 h-5 w-5" />
|
||||
Share
|
||||
</Button>
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
className="flex-1 bg-transparent backdrop-blur-sm"
|
||||
fileName={document.title}
|
||||
documentData={document.documentData}
|
||||
disabled={document.status !== DocumentStatus.COMPLETED}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ImageResponse } from 'next/server';
|
||||
import { ImageResponse, NextResponse } from 'next/server';
|
||||
|
||||
import { P, match } from 'ts-pattern';
|
||||
|
||||
@ -21,7 +21,7 @@ type SharePageOpenGraphImageProps = {
|
||||
params: { slug: string };
|
||||
};
|
||||
|
||||
export default async function Image({ params: { slug } }: SharePageOpenGraphImageProps) {
|
||||
export async function GET(_request: Request, { params: { slug } }: SharePageOpenGraphImageProps) {
|
||||
const [interSemiBold, interRegular, caveatRegular, shareFrameImage] = await Promise.all([
|
||||
getAssetBuffer('/fonts/inter-semibold.ttf'),
|
||||
getAssetBuffer('/fonts/inter-regular.ttf'),
|
||||
@ -32,7 +32,7 @@ export default async function Image({ params: { slug } }: SharePageOpenGraphImag
|
||||
const recipientOrSender = await getRecipientOrSenderByShareLinkSlug({ slug }).catch(() => null);
|
||||
|
||||
if (!recipientOrSender) {
|
||||
return null;
|
||||
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const isRecipient = 'Signature' in recipientOrSender;
|
||||
@ -1,11 +1,39 @@
|
||||
import { Metadata } from 'next';
|
||||
import { headers } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { Redirect } from './redirect';
|
||||
import { APP_BASE_URL } from '@documenso/lib/constants/app';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Documenso - Share',
|
||||
type SharePageProps = {
|
||||
params: { slug: string };
|
||||
};
|
||||
|
||||
export default function SharePage() {
|
||||
return <Redirect />;
|
||||
export function generateMetadata({ params: { slug } }: SharePageProps) {
|
||||
return {
|
||||
title: 'Documenso - Share',
|
||||
description: 'I just signed a document with Documenso!',
|
||||
openGraph: {
|
||||
title: 'Documenso - Join the open source signing revolution',
|
||||
description: 'I just signed with Documenso!',
|
||||
type: 'website',
|
||||
images: [`${APP_BASE_URL}/share/${slug}/opengraph`],
|
||||
},
|
||||
twitter: {
|
||||
site: '@documenso',
|
||||
card: 'summary_large_image',
|
||||
images: [`${APP_BASE_URL}/share/${slug}/opengraph`],
|
||||
description: 'I just signed with Documenso!',
|
||||
},
|
||||
} satisfies Metadata;
|
||||
}
|
||||
|
||||
export default function SharePage() {
|
||||
const userAgent = headers().get('User-Agent') ?? '';
|
||||
|
||||
// https://stackoverflow.com/questions/47026171/how-to-detect-bots-for-open-graph-with-user-agent
|
||||
if (/bot|facebookexternalhit|WhatsApp|google|bing|duckduckbot|MetaInspector/i.test(userAgent)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
redirect(process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001');
|
||||
}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const Redirect = () => {
|
||||
useEffect(() => {
|
||||
window.location.href = process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001';
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -9,7 +9,7 @@ import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-f
|
||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
|
||||
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
|
||||
import { SigningCard } from '@documenso/ui/components/signing-card';
|
||||
import { SigningCard3D } from '@documenso/ui/components/signing-card';
|
||||
|
||||
import signingCelebration from '~/assets/signing-celebration.png';
|
||||
|
||||
@ -53,11 +53,11 @@ export default async function CompletedSigningPage({
|
||||
recipient.email;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center pt-24">
|
||||
<div className="flex flex-col items-center pt-24 lg:pt-36 xl:pt-44">
|
||||
{/* Card with recipient */}
|
||||
<SigningCard name={recipientName} signingCelebrationImage={signingCelebration} />
|
||||
<SigningCard3D name={recipientName} signingCelebrationImage={signingCelebration} />
|
||||
|
||||
<div className="mt-6">
|
||||
<div className="relative mt-6 flex w-full flex-col items-center">
|
||||
{match(document.status)
|
||||
.with(DocumentStatus.COMPLETED, () => (
|
||||
<div className="text-documenso-700 flex items-center text-center">
|
||||
@ -71,41 +71,44 @@ export default async function CompletedSigningPage({
|
||||
<span className="text-sm">Waiting for others to sign</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<h2 className="mt-6 max-w-[35ch] text-center text-2xl font-semibold leading-normal md:text-3xl lg:text-4xl">
|
||||
You have signed "{document.title}"
|
||||
</h2>
|
||||
|
||||
{match(document.status)
|
||||
.with(DocumentStatus.COMPLETED, () => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
Everyone has signed! You will receive an Email copy of the signed document.
|
||||
</p>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
You will receive an Email copy of the signed document once everyone has signed.
|
||||
</p>
|
||||
))}
|
||||
|
||||
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
|
||||
<ShareButton documentId={document.id} token={recipient.token} />
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
fileName={document.title}
|
||||
documentData={documentData}
|
||||
disabled={document.status !== DocumentStatus.COMPLETED}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground/60 mt-36 text-sm">
|
||||
Want to send slick signing links like this one?{' '}
|
||||
<Link
|
||||
href="https://documenso.com"
|
||||
className="text-documenso-700 hover:text-documenso-600"
|
||||
>
|
||||
Check out Documenso.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 className="mt-6 max-w-[35ch] text-center text-2xl font-semibold leading-normal md:text-3xl lg:text-4xl">
|
||||
You have signed "{document.title}"
|
||||
</h2>
|
||||
|
||||
{match(document.status)
|
||||
.with(DocumentStatus.COMPLETED, () => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
Everyone has signed! You will receive an Email copy of the signed document.
|
||||
</p>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
You will receive an Email copy of the signed document once everyone has signed.
|
||||
</p>
|
||||
))}
|
||||
|
||||
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
|
||||
<ShareButton documentId={document.id} token={recipient.token} />
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
fileName={document.title}
|
||||
documentData={documentData}
|
||||
disabled={document.status !== DocumentStatus.COMPLETED}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground/60 mt-36 text-sm">
|
||||
Want to send slick signing links like this one?{' '}
|
||||
<Link href="https://documenso.com" className="text-documenso-700 hover:text-documenso-600">
|
||||
Check out Documenso.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { HTMLAttributes } from 'react';
|
||||
import { HTMLAttributes, useState } from 'react';
|
||||
|
||||
import { Share } from 'lucide-react';
|
||||
import { Copy, Share, Twitter } from 'lucide-react';
|
||||
|
||||
import { generateTwitterIntent } from '@documenso/lib/universal/generate-twitter-intent';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
|
||||
@ -19,14 +28,36 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
const { toast } = useToast();
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
const { mutateAsync: createOrGetShareLink, isLoading } =
|
||||
trpc.shareLink.createOrGetShareLink.useMutation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const onShareClick = async () => {
|
||||
const { slug } = await createOrGetShareLink({
|
||||
token: token,
|
||||
documentId,
|
||||
});
|
||||
const {
|
||||
mutateAsync: createOrGetShareLink,
|
||||
data: shareLink,
|
||||
isLoading,
|
||||
} = trpc.shareLink.createOrGetShareLink.useMutation();
|
||||
|
||||
const onOpenChange = (nextOpen: boolean) => {
|
||||
if (nextOpen) {
|
||||
void createOrGetShareLink({
|
||||
token,
|
||||
documentId,
|
||||
});
|
||||
}
|
||||
|
||||
setIsOpen(nextOpen);
|
||||
};
|
||||
|
||||
const onCopyClick = async () => {
|
||||
let { slug = '' } = shareLink || {};
|
||||
|
||||
if (!slug) {
|
||||
const result = await createOrGetShareLink({
|
||||
token,
|
||||
documentId,
|
||||
});
|
||||
|
||||
slug = result.slug;
|
||||
}
|
||||
|
||||
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
|
||||
|
||||
@ -34,18 +65,82 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
title: 'Copied to clipboard',
|
||||
description: 'The sharing link has been copied to your clipboard.',
|
||||
});
|
||||
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const onTweetClick = async () => {
|
||||
let { slug = '' } = shareLink || {};
|
||||
|
||||
if (!slug) {
|
||||
const result = await createOrGetShareLink({
|
||||
token,
|
||||
documentId,
|
||||
});
|
||||
|
||||
slug = result.slug;
|
||||
}
|
||||
|
||||
window.open(
|
||||
generateTwitterIntent(
|
||||
`I just ${token ? 'signed' : 'sent'} a document with @documenso. Check it out!`,
|
||||
`${window.location.origin}/share/${slug}`,
|
||||
),
|
||||
'_blank',
|
||||
);
|
||||
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
disabled={!token || !documentId}
|
||||
loading={isLoading}
|
||||
onClick={onShareClick}
|
||||
>
|
||||
{!isLoading && <Share className="mr-2 h-5 w-5" />}
|
||||
Share
|
||||
</Button>
|
||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={!token || !documentId}
|
||||
className="flex-1"
|
||||
loading={isLoading}
|
||||
>
|
||||
{!isLoading && <Share className="mr-2 h-5 w-5" />}
|
||||
Share
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Share</DialogTitle>
|
||||
|
||||
<DialogDescription className="mt-4">Share your signing experience!</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex w-full flex-col">
|
||||
<div className="rounded-md border p-4">
|
||||
I just {token ? 'signed' : 'sent'} a document with{' '}
|
||||
<span className="font-medium text-blue-400">@documenso</span>
|
||||
. Check it out!
|
||||
<span className="mt-2 block" />
|
||||
<span className="font-medium text-blue-400">
|
||||
{window.location.origin}/share/{shareLink?.slug || '...'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" className="mt-4" onClick={onTweetClick}>
|
||||
<Twitter className="mr-2 h-4 w-4" />
|
||||
Tweet
|
||||
</Button>
|
||||
|
||||
<div className="relative flex items-center justify-center gap-x-4 py-4 text-xs uppercase">
|
||||
<div className="bg-border h-px flex-1" />
|
||||
<span className="text-muted-foreground bg-transparent">Or</span>
|
||||
<div className="bg-border h-px flex-1" />
|
||||
</div>
|
||||
|
||||
<Button variant="outline" onClick={onCopyClick}>
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
Copy Link
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { notFound, redirect } from 'next/navigation';
|
||||
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
@ -9,7 +9,7 @@ import { viewedDocument } from '@documenso/lib/server-only/document/viewed-docum
|
||||
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
|
||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, FieldType, SigningStatus } from '@documenso/prisma/client';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
||||
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
||||
@ -53,6 +53,13 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
|
||||
|
||||
const user = await getServerComponentSession();
|
||||
|
||||
if (
|
||||
document.status === DocumentStatus.COMPLETED ||
|
||||
recipient.signingStatus === SigningStatus.SIGNED
|
||||
) {
|
||||
redirect(`/sign/${token}/complete`);
|
||||
}
|
||||
|
||||
return (
|
||||
<SigningProvider email={recipient.email} fullName={recipient.name} signature={user?.signature}>
|
||||
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
|
||||
|
||||
@ -40,7 +40,7 @@ export const StackAvatarsWithTooltip = ({
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<Tooltip delayDuration={200}>
|
||||
<TooltipTrigger className="flex cursor-pointer">
|
||||
{children || <StackAvatars recipients={recipients} />}
|
||||
</TooltipTrigger>
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Eye, EyeOff, Loader } from 'lucide-react';
|
||||
@ -40,8 +38,6 @@ export type SignInFormProps = {
|
||||
};
|
||||
|
||||
export const SignInForm = ({ className }: SignInFormProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const { toast } = useToast();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
@ -57,36 +53,29 @@ export const SignInForm = ({ className }: SignInFormProps) => {
|
||||
resolver: zodResolver(ZSignInFormSchema),
|
||||
});
|
||||
|
||||
const errorCode = searchParams?.get('error');
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
if (isErrorCode(errorCode)) {
|
||||
timeout = setTimeout(() => {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
description: ERROR_MESSAGES[errorCode] ?? 'An unknown error occurred',
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
};
|
||||
}, [errorCode, toast]);
|
||||
|
||||
const onFormSubmit = async ({ email, password }: TSignInFormSchema) => {
|
||||
try {
|
||||
await signIn('credentials', {
|
||||
const result = await signIn('credentials', {
|
||||
email,
|
||||
password,
|
||||
callbackUrl: LOGIN_REDIRECT_PATH,
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
redirect: false,
|
||||
});
|
||||
|
||||
if (result?.error && isErrorCode(result.error)) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
description: ERROR_MESSAGES[result.error],
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result?.url) {
|
||||
throw new Error('An unknown error occurred');
|
||||
}
|
||||
|
||||
window.location.href = result.url;
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'An unknown error occurred',
|
||||
|
||||
Reference in New Issue
Block a user