From b5efa0d3ea7c6d64ffb1729dd5b8c29a3bcc79a8 Mon Sep 17 00:00:00 2001 From: Mythie Date: Thu, 31 Aug 2023 13:33:13 +1000 Subject: [PATCH] fix: fix eslint warnings --- .../src/app/(marketing)/claimed/page.tsx | 7 +- .../components/(marketing)/pricing-table.tsx | 3 - .../src/components/(marketing)/widget.tsx | 5 +- .../src/pages/api/claim-plan/index.ts | 2 - .../src/pages/api/stripe/webhook/index.ts | 5 +- .../(dashboard)/period-selector/types.ts | 1 + .../src/components/(marketing)/callout.tsx | 66 --- .../(marketing)/claim-plan-dialog.tsx | 150 ------- .../faster-smarter-beautiful-bento.tsx | 77 ---- .../web/src/components/(marketing)/footer.tsx | 86 ---- .../web/src/components/(marketing)/header.tsx | 32 -- apps/web/src/components/(marketing)/hero.tsx | 225 ---------- .../(marketing)/open-build-template-bento.tsx | 74 ---- .../(marketing)/password-reveal.tsx | 33 -- .../components/(marketing)/pricing-table.tsx | 180 -------- .../share-connect-paid-widget-bento.tsx | 91 ---- .../web/src/components/(marketing)/widget.tsx | 402 ------------------ apps/web/src/pages/api/claim-plan/index.ts | 2 - .../web/src/pages/api/stripe/webhook/index.ts | 5 +- .../field/set-fields-for-document.ts | 2 + packages/lib/server-only/stripe/index.ts | 3 +- .../lib/server-only/user/update-password.ts | 10 +- packages/lib/types/find-result-set.ts | 2 +- packages/lib/types/is-document-status.ts | 1 + turbo.json | 8 +- 25 files changed, 31 insertions(+), 1441 deletions(-) delete mode 100644 apps/web/src/components/(marketing)/callout.tsx delete mode 100644 apps/web/src/components/(marketing)/claim-plan-dialog.tsx delete mode 100644 apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx delete mode 100644 apps/web/src/components/(marketing)/footer.tsx delete mode 100644 apps/web/src/components/(marketing)/header.tsx delete mode 100644 apps/web/src/components/(marketing)/hero.tsx delete mode 100644 apps/web/src/components/(marketing)/open-build-template-bento.tsx delete mode 100644 apps/web/src/components/(marketing)/password-reveal.tsx delete mode 100644 apps/web/src/components/(marketing)/pricing-table.tsx delete mode 100644 apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx delete mode 100644 apps/web/src/components/(marketing)/widget.tsx diff --git a/apps/marketing/src/app/(marketing)/claimed/page.tsx b/apps/marketing/src/app/(marketing)/claimed/page.tsx index ce748006e..f56ae2b26 100644 --- a/apps/marketing/src/app/(marketing)/claimed/page.tsx +++ b/apps/marketing/src/app/(marketing)/claimed/page.tsx @@ -27,7 +27,11 @@ export type ClaimedPlanPageProps = { export default async function ClaimedPlanPage({ searchParams = {} }: ClaimedPlanPageProps) { const { sessionId } = searchParams; - const session = await stripe.checkout.sessions.retrieve(sessionId as string); + if (typeof sessionId !== 'string') { + redirect('/'); + } + + const session = await stripe.checkout.sessions.retrieve(sessionId); const user = await prisma.user.findFirst({ where: { @@ -157,7 +161,6 @@ export default async function ClaimedPlanPage({ searchParams = {} }: ClaimedPlan

{ const event = usePlausible(); const [period, setPeriod] = useState<'MONTHLY' | 'YEARLY'>(() => - // eslint-disable-next-line turbo/no-undeclared-env-vars params?.get('planId') === process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID ? 'YEARLY' : 'MONTHLY', @@ -30,11 +29,9 @@ export const PricingTable = ({ className, ...props }: PricingTableProps) => { const planId = useMemo(() => { if (period === 'MONTHLY') { - // eslint-disable-next-line turbo/no-undeclared-env-vars return process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID; } - // eslint-disable-next-line turbo/no-undeclared-env-vars return process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID; }, [period]); diff --git a/apps/marketing/src/components/(marketing)/widget.tsx b/apps/marketing/src/components/(marketing)/widget.tsx index 15e15d04c..def90e0cd 100644 --- a/apps/marketing/src/components/(marketing)/widget.tsx +++ b/apps/marketing/src/components/(marketing)/widget.tsx @@ -139,7 +139,6 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => { setTimeout(resolve, 1000); }); - // eslint-disable-next-line turbo/no-undeclared-env-vars const planId = process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID; const claimPlanInput = signatureDataUrl @@ -147,7 +146,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => { name, email, planId, - signatureDataUrl: signatureDataUrl!, + signatureDataUrl: signatureDataUrl, signatureText: null, } : { @@ -155,7 +154,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => { email, planId, signatureDataUrl: null, - signatureText: signatureText!, + signatureText: signatureText ?? '', }; const [result] = await Promise.all([claimPlan(claimPlanInput), delay]); diff --git a/apps/marketing/src/pages/api/claim-plan/index.ts b/apps/marketing/src/pages/api/claim-plan/index.ts index a2e4108d2..abad354a8 100644 --- a/apps/marketing/src/pages/api/claim-plan/index.ts +++ b/apps/marketing/src/pages/api/claim-plan/index.ts @@ -43,7 +43,6 @@ export default async function handler( if (user && user.Subscription.length > 0) { return res.status(200).json({ - // eslint-disable-next-line turbo/no-undeclared-env-vars redirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/login`, }); } @@ -104,7 +103,6 @@ export default async function handler( mode: 'subscription', metadata, allow_promotion_codes: true, - // eslint-disable-next-line turbo/no-undeclared-env-vars success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing?email=${encodeURIComponent( email, diff --git a/apps/marketing/src/pages/api/stripe/webhook/index.ts b/apps/marketing/src/pages/api/stripe/webhook/index.ts index a0a4ccebb..3f3810fd4 100644 --- a/apps/marketing/src/pages/api/stripe/webhook/index.ts +++ b/apps/marketing/src/pages/api/stripe/webhook/index.ts @@ -17,14 +17,13 @@ import { SigningStatus, } from '@documenso/prisma/client'; -const log = (...args: any[]) => console.log('[stripe]', ...args); +const log = (...args: unknown[]) => console.log('[stripe]', ...args); export const config = { api: { bodyParser: false }, }; export default async function handler(req: NextApiRequest, res: NextApiResponse) { - // eslint-disable-next-line turbo/no-undeclared-env-vars // if (!process.env.NEXT_PUBLIC_ALLOW_SUBSCRIPTIONS) { // return res.status(500).json({ // success: false, @@ -55,6 +54,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) log('event-type:', event.type); if (event.type === 'checkout.session.completed') { + // This typecast is required since we don't want to create a guard for every event type + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const session = event.data.object as Stripe.Checkout.Session; if (session.metadata?.source === 'landing') { diff --git a/apps/web/src/components/(dashboard)/period-selector/types.ts b/apps/web/src/components/(dashboard)/period-selector/types.ts index 4ebfe47f1..2b50f5d6c 100644 --- a/apps/web/src/components/(dashboard)/period-selector/types.ts +++ b/apps/web/src/components/(dashboard)/period-selector/types.ts @@ -1,5 +1,6 @@ export type PeriodSelectorValue = '' | '7d' | '14d' | '30d'; export const isPeriodSelectorValue = (value: unknown): value is PeriodSelectorValue => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return ['', '7d', '14d', '30d'].includes(value as string); }; diff --git a/apps/web/src/components/(marketing)/callout.tsx b/apps/web/src/components/(marketing)/callout.tsx deleted file mode 100644 index d83983141..000000000 --- a/apps/web/src/components/(marketing)/callout.tsx +++ /dev/null @@ -1,66 +0,0 @@ -'use client'; - -import Link from 'next/link'; - -import { Github } from 'lucide-react'; -import { usePlausible } from 'next-plausible'; - -import { Button } from '@documenso/ui/primitives/button'; - -export type CalloutProps = { - starCount?: number; - [key: string]: unknown; -}; - -export const Callout = ({ starCount }: CalloutProps) => { - const event = usePlausible(); - - const onSignUpClick = () => { - const el = document.getElementById('email'); - - if (el) { - const { top } = el.getBoundingClientRect(); - - window.scrollTo({ - top: top - 120, - behavior: 'smooth', - }); - - setTimeout(() => { - el.focus(); - }, 500); - } - }; - - return ( -
- - - event('view-github')} - > - - -
- ); -}; diff --git a/apps/web/src/components/(marketing)/claim-plan-dialog.tsx b/apps/web/src/components/(marketing)/claim-plan-dialog.tsx deleted file mode 100644 index 1f78c5292..000000000 --- a/apps/web/src/components/(marketing)/claim-plan-dialog.tsx +++ /dev/null @@ -1,150 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; - -import { useSearchParams } from 'next/navigation'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { Info, Loader } from 'lucide-react'; -import { usePlausible } from 'next-plausible'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Button } from '@documenso/ui/primitives/button'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '@documenso/ui/primitives/dialog'; -import { Input } from '@documenso/ui/primitives/input'; -import { Label } from '@documenso/ui/primitives/label'; -import { useToast } from '@documenso/ui/primitives/use-toast'; - -import { claimPlan } from '~/api/claim-plan/fetcher'; - -import { FormErrorMessage } from '../form/form-error-message'; - -export const ZClaimPlanDialogFormSchema = z.object({ - name: z.string().min(3), - email: z.string().email(), -}); - -export type TClaimPlanDialogFormSchema = z.infer; - -export type ClaimPlanDialogProps = { - className?: string; - planId: string; - children: React.ReactNode; -}; - -export const ClaimPlanDialog = ({ className, planId, children }: ClaimPlanDialogProps) => { - const params = useSearchParams(); - const { toast } = useToast(); - const event = usePlausible(); - - const [open, setOpen] = useState(() => params?.get('cancelled') === 'true'); - - const { - register, - handleSubmit, - formState: { errors, isSubmitting }, - } = useForm({ - mode: 'onBlur', - defaultValues: { - name: params?.get('name') ?? '', - email: params?.get('email') ?? '', - }, - resolver: zodResolver(ZClaimPlanDialogFormSchema), - }); - - const onFormSubmit = async ({ name, email }: TClaimPlanDialogFormSchema) => { - try { - const delay = new Promise((resolve) => { - setTimeout(resolve, 1000); - }); - - const [redirectUrl] = await Promise.all([ - claimPlan({ name, email, planId, signatureText: name, signatureDataUrl: null }), - delay, - ]); - - event('claim-plan-pricing'); - - window.location.href = redirectUrl; - } catch (error) { - event('claim-plan-failed'); - - toast({ - title: 'Something went wrong', - description: error instanceof Error ? error.message : 'Please try again later.', - variant: 'destructive', - }); - } - }; - - return ( - - {children} - - - - Claim your plan - - - We're almost there! Please enter your email address and name to claim your plan. - - - -
- {params?.get('cancelled') === 'true' && ( -
-
-
- -
-
-

- You have cancelled the payment process. If you didn't mean to do this, please - try again. -

-
-
-
- )} - -
- - - - - -
- -
- - - - - -
- - -
-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx b/apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx deleted file mode 100644 index 2cbaaef53..000000000 --- a/apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { HTMLAttributes } from 'react'; - -import Image from 'next/image'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Card, CardContent } from '@documenso/ui/primitives/card'; - -import backgroundPattern from '~/assets/background-pattern.png'; -import cardBeautifulFigure from '~/assets/card-beautiful-figure.png'; -import cardFastFigure from '~/assets/card-fast-figure.png'; -import cardSmartFigure from '~/assets/card-smart-figure.png'; - -export type FasterSmarterBeautifulBentoProps = HTMLAttributes; - -export const FasterSmarterBeautifulBento = ({ - className, - ...props -}: FasterSmarterBeautifulBentoProps) => { - return ( -
-
- background pattern -
-

- A 10x better signing experience. - Faster, smarter and more beautiful. -

- -
- - -

- Fast. - When it comes to sending or receiving a contract, you can count on lightning-fast - speeds. -

- -
- its fast -
-
-
- - - -

- Beautiful. - Because signing should be celebrated. That’s why we care about the smallest detail in - our product. -

- -
- its fast -
-
-
- - - -

- Smart. - Our custom templates come with smart rules that can help you save time and energy. -

- -
- its fast -
-
-
-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/footer.tsx b/apps/web/src/components/(marketing)/footer.tsx deleted file mode 100644 index 823ece92e..000000000 --- a/apps/web/src/components/(marketing)/footer.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { HTMLAttributes } from 'react'; - -import Image from 'next/image'; -import Link from 'next/link'; - -import { Github, Slack, Twitter } from 'lucide-react'; - -import { cn } from '@documenso/ui/lib/utils'; - -export type FooterProps = HTMLAttributes; - -export const Footer = ({ className, ...props }: FooterProps) => { - return ( -
-
-
- - Documenso Logo - - -
- - - - - - - - - - - -
-
- -
- - Pricing - - - - Status - - - - Support - - - {/* - Privacy - */} -
-
-
-

- © {new Date().getFullYear()} Documenso, Inc. All rights reserved. -

-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/header.tsx b/apps/web/src/components/(marketing)/header.tsx deleted file mode 100644 index 5a1fa3b89..000000000 --- a/apps/web/src/components/(marketing)/header.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { HTMLAttributes } from 'react'; - -import Image from 'next/image'; -import Link from 'next/link'; - -import { cn } from '@documenso/ui/lib/utils'; - -export type HeaderProps = HTMLAttributes; - -export const Header = ({ className, ...props }: HeaderProps) => { - return ( -
- - Documenso Logo - - -
- - Pricing - - - - Sign in - -
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/hero.tsx b/apps/web/src/components/(marketing)/hero.tsx deleted file mode 100644 index 7896a010e..000000000 --- a/apps/web/src/components/(marketing)/hero.tsx +++ /dev/null @@ -1,225 +0,0 @@ -'use client'; - -import Image from 'next/image'; -import Link from 'next/link'; - -import { Variants, motion } from 'framer-motion'; -import { Github } from 'lucide-react'; -import { usePlausible } from 'next-plausible'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Button } from '@documenso/ui/primitives/button'; - -import backgroundPattern from '~/assets/background-pattern.png'; - -import { Widget } from './widget'; - -export type HeroProps = { - className?: string; - starCount?: number; - [key: string]: unknown; -}; - -const BackgroundPatternVariants: Variants = { - initial: { - opacity: 0, - }, - - animate: { - opacity: 1, - - transition: { - delay: 1, - duration: 1.2, - }, - }, -}; - -const HeroTitleVariants: Variants = { - initial: { - opacity: 0, - y: 60, - }, - animate: { - opacity: 1, - y: 0, - transition: { - duration: 0.5, - }, - }, -}; - -export const Hero = ({ className, starCount, ...props }: HeroProps) => { - const event = usePlausible(); - - const onSignUpClick = () => { - const el = document.getElementById('email'); - - if (el) { - const { top } = el.getBoundingClientRect(); - - window.scrollTo({ - top: top - 120, - behavior: 'smooth', - }); - - requestAnimationFrame(() => { - el.focus(); - }); - } - }; - - return ( - -
- - background pattern - -
- -
- - Document signing, - finally open source. - - - - - - event('view-github')}> - - - - -
- - - Documenso - The open source DocuSign alternative | Product Hunt - - -
- - - - Documenso Supporter Pledge -

- Our mission is to create an open signing infrastructure that empowers the world, - enabling businesses to embrace openness, cooperation, and transparency. We believe - that signing, as a fundamental act, should embody these values. By offering an - open-source signing solution, we aim to make document signing accessible, transparent, - and trustworthy. -

- -

- Through our platform, called Documenso, we strive to earn your trust by allowing - self-hosting and providing complete visibility into its inner workings. We value - inclusivity and foster an environment where diverse perspectives and contributions are - welcomed, even though we may not implement them all. -

- -

- At Documenso, we envision a web-enabled future for business and contracts, and we are - committed to being the leading provider of open signing infrastructure. By combining - exceptional product design with open-source principles, we aim to deliver a robust and - well-designed application that exceeds your expectations. -

- -

- We understand that exceptional products are born from exceptional communities, and we - invite you to join our open-source community. Your contributions, whether technical or - non-technical, will help shape the future of signing. Together, we can create a better - future for everyone. -

- -

- Today we invite you to join us on this journey: By signing this mission statement you - signal your support of Documenso's mission{' '} - - (in a non-legally binding, but heartfelt way) - {' '} - and lock in the early supporter plan for forever, including everything we build this - year. -

- -
-

Timur & Lucas

-
- -
- Timur Ercan & Lucas Smith -

Co-Founders, Documenso

-
-
-
-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/open-build-template-bento.tsx b/apps/web/src/components/(marketing)/open-build-template-bento.tsx deleted file mode 100644 index e7920500b..000000000 --- a/apps/web/src/components/(marketing)/open-build-template-bento.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { HTMLAttributes } from 'react'; - -import Image from 'next/image'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Card, CardContent } from '@documenso/ui/primitives/card'; - -import backgroundPattern from '~/assets/background-pattern.png'; -import cardBuildFigure from '~/assets/card-build-figure.png'; -import cardOpenFigure from '~/assets/card-open-figure.png'; -import cardTemplateFigure from '~/assets/card-template-figure.png'; - -export type OpenBuildTemplateBentoProps = HTMLAttributes; - -export const OpenBuildTemplateBento = ({ className, ...props }: OpenBuildTemplateBentoProps) => { - return ( -
-
- background pattern -
-

- Truly your own. - Customise and expand. -

- -
- - -

- Open Source or Hosted. - It’s up to you. Either clone our repository or rely on our easy to use hosting - solution. -

- -
- its fast -
-
-
- - - -

- Build on top. - Make it your own through advanced customization and adjustability. -

- -
- its fast -
-
-
- - - -

- Template Store (Soon). - Choose a template from the community app store. Or submit your own template for others - to use. -

- -
- its fast -
-
-
-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/password-reveal.tsx b/apps/web/src/components/(marketing)/password-reveal.tsx deleted file mode 100644 index b31765943..000000000 --- a/apps/web/src/components/(marketing)/password-reveal.tsx +++ /dev/null @@ -1,33 +0,0 @@ -'use client'; - -import { useToast } from '@documenso/ui/primitives/use-toast'; - -import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard'; - -export type PasswordRevealProps = { - password: string; -}; - -export const PasswordReveal = ({ password }: PasswordRevealProps) => { - const { toast } = useToast(); - const [, copy] = useCopyToClipboard(); - - const onCopyClick = () => { - void copy(password).then(() => { - toast({ - title: 'Copied to clipboard', - description: 'Your password has been copied to your clipboard.', - }); - }); - }; - - return ( - - ); -}; diff --git a/apps/web/src/components/(marketing)/pricing-table.tsx b/apps/web/src/components/(marketing)/pricing-table.tsx deleted file mode 100644 index 73003abdc..000000000 --- a/apps/web/src/components/(marketing)/pricing-table.tsx +++ /dev/null @@ -1,180 +0,0 @@ -'use client'; - -import { HTMLAttributes, useMemo, useState } from 'react'; - -import Link from 'next/link'; -import { useSearchParams } from 'next/navigation'; - -import { AnimatePresence, motion } from 'framer-motion'; -import { usePlausible } from 'next-plausible'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Button } from '@documenso/ui/primitives/button'; - -import { ClaimPlanDialog } from './claim-plan-dialog'; - -export type PricingTableProps = HTMLAttributes; - -const SELECTED_PLAN_BAR_LAYOUT_ID = 'selected-plan-bar'; - -export const PricingTable = ({ className, ...props }: PricingTableProps) => { - const params = useSearchParams(); - const event = usePlausible(); - - const [period, setPeriod] = useState<'MONTHLY' | 'YEARLY'>(() => - // eslint-disable-next-line turbo/no-undeclared-env-vars - params?.get('planId') === process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID - ? 'YEARLY' - : 'MONTHLY', - ); - - const planId = useMemo(() => { - if (period === 'MONTHLY') { - // eslint-disable-next-line turbo/no-undeclared-env-vars - return process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID; - } - - // eslint-disable-next-line turbo/no-undeclared-env-vars - return process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID; - }, [period]); - - return ( -
-
- - setPeriod('MONTHLY')} - > - Monthly - {period === 'MONTHLY' && ( - - )} - - - setPeriod('YEARLY')} - > - Yearly -
- Save $60 -
- {period === 'YEARLY' && ( - - )} -
-
-
- -
-
-

Self Hosted

-

Free

- -

- For small teams and individuals who need a simple solution -

- - - -
-

Host your own instance

-

Full Control

-

Customizability

-

Docker Ready

-

Community Support

-

Free, Forever

-
-
- -
-

Community

-
- - {period === 'MONTHLY' && $30} - {period === 'YEARLY' && $300} - -
- -

- For fast-growing companies that aim to scale across multiple teams. -

- - - - - -
-

Documenso Early Adopter Deal:

-

Join the movement

-

Simple signing solution

-

Email and Slack assistance

-

- Includes all upcoming features -

-

Fixed, straightforward pricing

-
-
- -
-

Enterprise

-

Pricing on request

- -

- For large organizations that need extra flexibility and control. -

- - event('enterprise-contact')} - > - - - -
-

Everything in Community, plus:

-

Custom Subdomain

-

Compliance Check

-

Guaranteed Uptime

-

Reporting & Analysis

-

24/7 Support

-
-
-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx b/apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx deleted file mode 100644 index 05b6a3232..000000000 --- a/apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { HTMLAttributes } from 'react'; - -import Image from 'next/image'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Card, CardContent } from '@documenso/ui/primitives/card'; - -import backgroundPattern from '~/assets/background-pattern.png'; -import cardConnectionsFigure from '~/assets/card-connections-figure.png'; -import cardPaidFigure from '~/assets/card-paid-figure.png'; -import cardSharingFigure from '~/assets/card-sharing-figure.png'; -import cardWidgetFigure from '~/assets/card-widget-figure.png'; - -export type ShareConnectPaidWidgetBentoProps = HTMLAttributes; - -export const ShareConnectPaidWidgetBento = ({ - className, - ...props -}: ShareConnectPaidWidgetBentoProps) => { - return ( -
-
- background pattern -
-

- Integrates with all your favourite tools. - Send, connect, receive and embed everywhere. -

- -
- - -

- Easy Sharing (Soon). - Receive your personal link to share with everyone you care about. -

- -
- its fast -
-
-
- - - -

- Connections (Soon). - Create connections and automations with Zapier and more to integrate with your - favorite tools. -

- -
- its fast -
-
-
- - - -

- Get paid (Soon). - Integrated payments with stripe so you don’t have to worry about getting paid. -

- -
- its fast -
-
-
- - - -

- React Widget (Soon). - Easily embed Documenso into your product. Simply copy and paste our react widget into - your application. -

- -
- its fast -
-
-
-
-
- ); -}; diff --git a/apps/web/src/components/(marketing)/widget.tsx b/apps/web/src/components/(marketing)/widget.tsx deleted file mode 100644 index 15e15d04c..000000000 --- a/apps/web/src/components/(marketing)/widget.tsx +++ /dev/null @@ -1,402 +0,0 @@ -'use client'; - -import { HTMLAttributes, KeyboardEvent, useMemo, useState } from 'react'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { AnimatePresence, motion } from 'framer-motion'; -import { Loader } from 'lucide-react'; -import { usePlausible } from 'next-plausible'; -import { Controller, useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { cn } from '@documenso/ui/lib/utils'; -import { Button } from '@documenso/ui/primitives/button'; -import { Card, CardContent } from '@documenso/ui/primitives/card'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@documenso/ui/primitives/dialog'; -import { Input } from '@documenso/ui/primitives/input'; -import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; -import { useToast } from '@documenso/ui/primitives/use-toast'; - -import { claimPlan } from '~/api/claim-plan/fetcher'; - -import { FormErrorMessage } from '../form/form-error-message'; - -const ZWidgetFormSchema = z - .object({ - email: z.string().email({ message: 'Please enter a valid email address.' }), - name: z.string().min(3, { message: 'Please enter a valid name.' }), - }) - .and( - z.union([ - z.object({ - signatureDataUrl: z.string().min(1), - signatureText: z.null().or(z.string().max(0)), - }), - z.object({ - signatureDataUrl: z.null().or(z.string().max(0)), - signatureText: z.string().min(1), - }), - ]), - ); - -export type TWidgetFormSchema = z.infer; - -export type WidgetProps = HTMLAttributes; - -export const Widget = ({ className, children, ...props }: WidgetProps) => { - const { toast } = useToast(); - const event = usePlausible(); - - const [step, setStep] = useState<'EMAIL' | 'NAME' | 'SIGN'>('EMAIL'); - const [showSigningDialog, setShowSigningDialog] = useState(false); - const [draftSignatureDataUrl, setDraftSignatureDataUrl] = useState(null); - - const { - control, - register, - handleSubmit, - setValue, - trigger, - watch, - formState: { errors, isSubmitting, isValid }, - } = useForm({ - mode: 'onChange', - defaultValues: { - email: '', - name: '', - signatureDataUrl: null, - signatureText: '', - }, - resolver: zodResolver(ZWidgetFormSchema), - }); - - const signatureDataUrl = watch('signatureDataUrl'); - const signatureText = watch('signatureText'); - - const stepsRemaining = useMemo(() => { - if (step === 'NAME') { - return 2; - } - - if (step === 'SIGN') { - return 1; - } - - return 3; - }, [step]); - - const onNextStepClick = () => { - if (step === 'EMAIL') { - setStep('NAME'); - - setTimeout(() => { - document.querySelector('#name')?.focus(); - }, 0); - } - - if (step === 'NAME') { - setStep('SIGN'); - - setTimeout(() => { - document.querySelector('#signatureText')?.focus(); - }, 0); - } - }; - - const onEnterPress = (callback: () => void) => { - return (e: KeyboardEvent) => { - if (e.key === 'Enter') { - e.preventDefault(); - - callback(); - } - }; - }; - - const onSignatureConfirmClick = () => { - setValue('signatureDataUrl', draftSignatureDataUrl); - setValue('signatureText', ''); - - void trigger('signatureDataUrl'); - setShowSigningDialog(false); - }; - - const onFormSubmit = async ({ - email, - name, - signatureDataUrl, - signatureText, - }: TWidgetFormSchema) => { - try { - const delay = new Promise((resolve) => { - setTimeout(resolve, 1000); - }); - - // eslint-disable-next-line turbo/no-undeclared-env-vars - const planId = process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID; - - const claimPlanInput = signatureDataUrl - ? { - name, - email, - planId, - signatureDataUrl: signatureDataUrl!, - signatureText: null, - } - : { - name, - email, - planId, - signatureDataUrl: null, - signatureText: signatureText!, - }; - - const [result] = await Promise.all([claimPlan(claimPlanInput), delay]); - - event('claim-plan-widget'); - - window.location.href = result; - } catch (error) { - event('claim-plan-failed'); - - toast({ - title: 'Something went wrong', - description: error instanceof Error ? error.message : 'Please try again later.', - variant: 'destructive', - }); - } - }; - - return ( - <> - -
-
- {children} -
- -
-

Sign up for the community plan

-

- with Timur Ercan & Lucas Smith from Documenso -

- -
- - - - - - ( -
- - field.value !== '' && - !errors.email?.message && - onEnterPress(onNextStepClick)(e) - } - {...field} - /> - -
- -
-
- )} - /> - - -
- - {(step === 'NAME' || step === 'SIGN') && ( - - - - ( -
- - field.value !== '' && - !errors.name?.message && - onEnterPress(onNextStepClick)(e) - } - {...field} - /> - -
- -
-
- )} - /> - - -
- )} -
- -
- -
-

{stepsRemaining} step(s) until signed

-

Minimise contract

-
- -
-
-
- - - setShowSigningDialog(true)} - > -
- {!signatureText && signatureDataUrl && ( - user signature - )} - - {signatureText && ( -

- {signatureText} -

- )} -
- -
e.stopPropagation()} - > - { - if (e.target.value !== '') { - setValue('signatureDataUrl', null); - } - }, - })} - /> - - -
-
-
- -
- - - - - - Add your signature - - - - By signing you signal your support of Documenso's mission in a

- non-legally binding, but heartfelt way.

-

You also unlock the option to purchase the early supporter plan including - everything we build this year for fixed price. -
- - - - - - - - -
-
- - ); -}; diff --git a/apps/web/src/pages/api/claim-plan/index.ts b/apps/web/src/pages/api/claim-plan/index.ts index a2e4108d2..abad354a8 100644 --- a/apps/web/src/pages/api/claim-plan/index.ts +++ b/apps/web/src/pages/api/claim-plan/index.ts @@ -43,7 +43,6 @@ export default async function handler( if (user && user.Subscription.length > 0) { return res.status(200).json({ - // eslint-disable-next-line turbo/no-undeclared-env-vars redirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/login`, }); } @@ -104,7 +103,6 @@ export default async function handler( mode: 'subscription', metadata, allow_promotion_codes: true, - // eslint-disable-next-line turbo/no-undeclared-env-vars success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing?email=${encodeURIComponent( email, diff --git a/apps/web/src/pages/api/stripe/webhook/index.ts b/apps/web/src/pages/api/stripe/webhook/index.ts index dd15d4d81..6c678a33c 100644 --- a/apps/web/src/pages/api/stripe/webhook/index.ts +++ b/apps/web/src/pages/api/stripe/webhook/index.ts @@ -17,14 +17,13 @@ import { SigningStatus, } from '@documenso/prisma/client'; -const log = (...args: any[]) => console.log('[stripe]', ...args); +const log = (...args: unknown[]) => console.log('[stripe]', ...args); export const config = { api: { bodyParser: false }, }; export default async function handler(req: NextApiRequest, res: NextApiResponse) { - // eslint-disable-next-line turbo/no-undeclared-env-vars // if (!process.env.NEXT_PUBLIC_ALLOW_SUBSCRIPTIONS) { // return res.status(500).json({ // success: false, @@ -55,6 +54,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) log('event-type:', event.type); if (event.type === 'checkout.session.completed') { + // This is required since we don't want to create a guard for every event type + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const session = event.data.object as Stripe.Checkout.Session; if (session.metadata?.source === 'landing') { diff --git a/packages/lib/server-only/field/set-fields-for-document.ts b/packages/lib/server-only/field/set-fields-for-document.ts index c54d35bc1..dc477bcea 100644 --- a/packages/lib/server-only/field/set-fields-for-document.ts +++ b/packages/lib/server-only/field/set-fields-for-document.ts @@ -84,6 +84,8 @@ export const setFieldsForDocument = async ({ }) : prisma.field.create({ data: { + // TODO: Rewrite this entire transaction because this is a mess + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion type: field.type!, page: field.pageNumber, positionX: field.pageX, diff --git a/packages/lib/server-only/stripe/index.ts b/packages/lib/server-only/stripe/index.ts index b2ab59fb9..505beaec8 100644 --- a/packages/lib/server-only/stripe/index.ts +++ b/packages/lib/server-only/stripe/index.ts @@ -1,7 +1,6 @@ import Stripe from 'stripe'; -// eslint-disable-next-line turbo/no-undeclared-env-vars -export const stripe = new Stripe(process.env.NEXT_PRIVATE_STRIPE_API_KEY!, { +export const stripe = new Stripe(process.env.NEXT_PRIVATE_STRIPE_API_KEY ?? '', { apiVersion: '2022-11-15', typescript: true, }); diff --git a/packages/lib/server-only/user/update-password.ts b/packages/lib/server-only/user/update-password.ts index 4133bc342..d987085ff 100644 --- a/packages/lib/server-only/user/update-password.ts +++ b/packages/lib/server-only/user/update-password.ts @@ -19,11 +19,13 @@ export const updatePassword = async ({ userId, password }: UpdatePasswordOptions const hashedPassword = await hash(password, SALT_ROUNDS); - // Compare the new password with the old password - const isSamePassword = await compare(password, user.password as string); + if (user.password) { + // Compare the new password with the old password + const isSamePassword = await compare(password, user.password); - if (isSamePassword) { - throw new Error('Your new password cannot be the same as your old password.'); + if (isSamePassword) { + throw new Error('Your new password cannot be the same as your old password.'); + } } const updatedUser = await prisma.user.update({ diff --git a/packages/lib/types/find-result-set.ts b/packages/lib/types/find-result-set.ts index 81b16f1ca..219b9b89c 100644 --- a/packages/lib/types/find-result-set.ts +++ b/packages/lib/types/find-result-set.ts @@ -1,5 +1,5 @@ export type FindResultSet = { - data: T extends Array ? T : T[]; + data: T extends Array ? T : T[]; count: number; currentPage: number; perPage: number; diff --git a/packages/lib/types/is-document-status.ts b/packages/lib/types/is-document-status.ts index 0666308a5..dbb5af489 100644 --- a/packages/lib/types/is-document-status.ts +++ b/packages/lib/types/is-document-status.ts @@ -1,5 +1,6 @@ import { DocumentStatus } from '@documenso/prisma/client'; export const isDocumentStatus = (value: unknown): value is DocumentStatus => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return Object.values(DocumentStatus).includes(value as DocumentStatus); }; diff --git a/turbo.json b/turbo.json index ab4460781..f7d3d342c 100644 --- a/turbo.json +++ b/turbo.json @@ -22,10 +22,13 @@ "globalEnv": [ "NEXTAUTH_URL", "NEXTAUTH_SECRET", + "NEXT_PUBLIC_APP_URL", "NEXT_PUBLIC_SITE_URL", "NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST", "NEXT_PUBLIC_FEATURE_BILLING_ENABLED", + "NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID", + "NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID", "NEXT_PRIVATE_DATABASE_URL", "NEXT_PRIVATE_NEXT_AUTH_SECRET", "NEXT_PRIVATE_GOOGLE_CLIENT_ID", @@ -44,6 +47,7 @@ "NEXT_PRIVATE_SMTP_APIKEY", "NEXT_PRIVATE_SMTP_SECURE", "NEXT_PRIVATE_SMTP_FROM_NAME", - "NEXT_PRIVATE_SMTP_FROM_ADDRESS" + "NEXT_PRIVATE_SMTP_FROM_ADDRESS", + "NEXT_PRIVATE_STRIPE_API_KEY" ] -} \ No newline at end of file +}