From fdf5b3908df581131dabb061b7c9e9e905c8cf69 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 24 Nov 2023 23:50:51 +0000 Subject: [PATCH 1/6] feat: add more posthog analytics to the application --- apps/web/src/app/(dashboard)/documents/page.tsx | 2 +- .../src/app/(dashboard)/documents/upload-document.tsx | 11 ++++++++++- apps/web/src/app/(signing)/sign/[token]/form.tsx | 10 +++++++++- apps/web/src/components/forms/signup.tsx | 7 +++++++ .../stripe/webhook/on-early-adopters-checkout.ts | 2 +- packages/lib/universal/get-feature-flag.ts | 10 ++++++---- 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/apps/web/src/app/(dashboard)/documents/page.tsx b/apps/web/src/app/(dashboard)/documents/page.tsx index f38668fd9..cd7f868ad 100644 --- a/apps/web/src/app/(dashboard)/documents/page.tsx +++ b/apps/web/src/app/(dashboard)/documents/page.tsx @@ -63,7 +63,7 @@ export default async function DocumentsPage({ searchParams = {} }: DocumentsPage return (
- +

Documents

diff --git a/apps/web/src/app/(dashboard)/documents/upload-document.tsx b/apps/web/src/app/(dashboard)/documents/upload-document.tsx index 644c9017a..eec881e67 100644 --- a/apps/web/src/app/(dashboard)/documents/upload-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/upload-document.tsx @@ -8,6 +8,7 @@ import { useRouter } from 'next/navigation'; import { Loader } from 'lucide-react'; import { useLimits } from '@documenso/ee/server-only/limits/provider/client'; +import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data'; import { putFile } from '@documenso/lib/universal/upload/put-file'; import { TRPCClientError } from '@documenso/trpc/client'; @@ -18,10 +19,12 @@ import { useToast } from '@documenso/ui/primitives/use-toast'; export type UploadDocumentProps = { className?: string; + userId?: number; }; -export const UploadDocument = ({ className }: UploadDocumentProps) => { +export const UploadDocument = ({ className, userId }: UploadDocumentProps) => { const router = useRouter(); + const analytics = useAnalytics(); const { toast } = useToast(); @@ -53,6 +56,12 @@ export const UploadDocument = ({ className }: UploadDocumentProps) => { duration: 5000, }); + analytics.capture('App: Document Uploaded', { + userId, + documentId: id, + timestamp: new Date().toISOString(), + }); + router.push(`/documents/${id}`); } catch (error) { console.error(error); diff --git a/apps/web/src/app/(signing)/sign/[token]/form.tsx b/apps/web/src/app/(signing)/sign/[token]/form.tsx index 5c6779c62..034b93c6d 100644 --- a/apps/web/src/app/(signing)/sign/[token]/form.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/form.tsx @@ -7,9 +7,10 @@ import { useRouter } from 'next/navigation'; import { useSession } from 'next-auth/react'; import { useForm } from 'react-hook-form'; +import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token'; import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields'; -import { Document, Field, Recipient } from '@documenso/prisma/client'; +import type { Document, Field, Recipient } from '@documenso/prisma/client'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -29,6 +30,7 @@ export type SigningFormProps = { export const SigningForm = ({ document, recipient, fields }: SigningFormProps) => { const router = useRouter(); + const analytics = useAnalytics(); const { data: session } = useSession(); const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext(); @@ -57,6 +59,12 @@ export const SigningForm = ({ document, recipient, fields }: SigningFormProps) = documentId: document.id, }); + analytics.capture('App: Document Signing Complete', { + signerId: recipient.id, + documentId: document.id, + timestamp: new Date().toISOString(), + }); + router.push(`/sign/${recipient.token}/complete`); }; diff --git a/apps/web/src/components/forms/signup.tsx b/apps/web/src/components/forms/signup.tsx index fc85510f3..8eb2ac0cc 100644 --- a/apps/web/src/components/forms/signup.tsx +++ b/apps/web/src/components/forms/signup.tsx @@ -8,6 +8,7 @@ import { signIn } from 'next-auth/react'; import { Controller, useForm } from 'react-hook-form'; import { z } from 'zod'; +import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { TRPCClientError } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; @@ -33,6 +34,7 @@ export type SignUpFormProps = { export const SignUpForm = ({ className }: SignUpFormProps) => { const { toast } = useToast(); + const analytics = useAnalytics(); const [showPassword, setShowPassword] = useState(false); const { @@ -61,6 +63,11 @@ export const SignUpForm = ({ className }: SignUpFormProps) => { password, callbackUrl: '/', }); + + analytics.capture('App: User Sign Up', { + email, + timestamp: new Date().toISOString(), + }); } catch (err) { if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { toast({ diff --git a/packages/ee/server-only/stripe/webhook/on-early-adopters-checkout.ts b/packages/ee/server-only/stripe/webhook/on-early-adopters-checkout.ts index a8403e05a..22f60069e 100644 --- a/packages/ee/server-only/stripe/webhook/on-early-adopters-checkout.ts +++ b/packages/ee/server-only/stripe/webhook/on-early-adopters-checkout.ts @@ -1,4 +1,4 @@ -import Stripe from 'stripe'; +import type Stripe from 'stripe'; import { hashSync } from '@documenso/lib/server-only/auth/hash'; import { sealDocument } from '@documenso/lib/server-only/document/seal-document'; diff --git a/packages/lib/universal/get-feature-flag.ts b/packages/lib/universal/get-feature-flag.ts index 38707d41b..bf79f79ce 100644 --- a/packages/lib/universal/get-feature-flag.ts +++ b/packages/lib/universal/get-feature-flag.ts @@ -1,9 +1,7 @@ import { z } from 'zod'; -import { - TFeatureFlagValue, - ZFeatureFlagValueSchema, -} from '@documenso/lib/client-only/providers/feature-flag.types'; +import type { TFeatureFlagValue } from '@documenso/lib/client-only/providers/feature-flag.types'; +import { ZFeatureFlagValueSchema } from '@documenso/lib/client-only/providers/feature-flag.types'; import { APP_BASE_URL } from '@documenso/lib/constants/app'; import { LOCAL_FEATURE_FLAGS, isFeatureFlagEnabled } from '@documenso/lib/constants/feature-flags'; @@ -20,6 +18,10 @@ export const getFlag = async ( ): Promise => { const requestHeaders = options?.requestHeaders ?? {}; + if (!LOCAL_FEATURE_FLAGS[flag]) { + return LOCAL_FEATURE_FLAGS[flag]; + } + if (!isFeatureFlagEnabled()) { return LOCAL_FEATURE_FLAGS[flag] ?? true; } From 0e40658201f7f33c61fd3f8eb7239f4c6c1458ae Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 26 Nov 2023 19:33:45 +0000 Subject: [PATCH 2/6] feat: track when the signing of a document has completed --- apps/web/src/app/(signing)/sign/[token]/form.tsx | 2 +- packages/lib/server-only/document/seal-document.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/(signing)/sign/[token]/form.tsx b/apps/web/src/app/(signing)/sign/[token]/form.tsx index 034b93c6d..57b737c0c 100644 --- a/apps/web/src/app/(signing)/sign/[token]/form.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/form.tsx @@ -59,7 +59,7 @@ export const SigningForm = ({ document, recipient, fields }: SigningFormProps) = documentId: document.id, }); - analytics.capture('App: Document Signing Complete', { + analytics.capture('App: Recipient has completed signing', { signerId: recipient.id, documentId: document.id, timestamp: new Date().toISOString(), diff --git a/packages/lib/server-only/document/seal-document.ts b/packages/lib/server-only/document/seal-document.ts index 318d540b8..c2e68277b 100644 --- a/packages/lib/server-only/document/seal-document.ts +++ b/packages/lib/server-only/document/seal-document.ts @@ -1,8 +1,10 @@ 'use server'; +import { nanoid } from 'nanoid'; import path from 'node:path'; import { PDFDocument } from 'pdf-lib'; +import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { signPdf } from '@documenso/signing'; @@ -83,6 +85,18 @@ export const sealDocument = async ({ documentId, sendEmail = true }: SealDocumen arrayBuffer: async () => Promise.resolve(pdfBuffer), }); + const postHog = PostHogServerClient(); + + if (postHog) { + postHog.capture({ + distinctId: nanoid(), + event: 'App: Document Signed', + properties: { + documentId: document.id, + }, + }); + } + await prisma.documentData.update({ where: { id: documentData.id, From 741201822ae6e823821f09404aa4e40507294afd Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Tue, 5 Dec 2023 10:12:28 +0000 Subject: [PATCH 3/6] fix: use useSession instead of prop drilling --- apps/web/src/app/(dashboard)/documents/page.tsx | 2 +- apps/web/src/app/(dashboard)/documents/upload-document.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/(dashboard)/documents/page.tsx b/apps/web/src/app/(dashboard)/documents/page.tsx index cd7f868ad..f38668fd9 100644 --- a/apps/web/src/app/(dashboard)/documents/page.tsx +++ b/apps/web/src/app/(dashboard)/documents/page.tsx @@ -63,7 +63,7 @@ export default async function DocumentsPage({ searchParams = {} }: DocumentsPage return (
- +

Documents

diff --git a/apps/web/src/app/(dashboard)/documents/upload-document.tsx b/apps/web/src/app/(dashboard)/documents/upload-document.tsx index eec881e67..7c59ffb4c 100644 --- a/apps/web/src/app/(dashboard)/documents/upload-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/upload-document.tsx @@ -6,6 +6,7 @@ import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { Loader } from 'lucide-react'; +import { useSession } from 'next-auth/react'; import { useLimits } from '@documenso/ee/server-only/limits/provider/client'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; @@ -19,12 +20,12 @@ import { useToast } from '@documenso/ui/primitives/use-toast'; export type UploadDocumentProps = { className?: string; - userId?: number; }; -export const UploadDocument = ({ className, userId }: UploadDocumentProps) => { +export const UploadDocument = ({ className }: UploadDocumentProps) => { const router = useRouter(); const analytics = useAnalytics(); + const { data: session } = useSession(); const { toast } = useToast(); @@ -57,7 +58,7 @@ export const UploadDocument = ({ className, userId }: UploadDocumentProps) => { }); analytics.capture('App: Document Uploaded', { - userId, + userId: session?.user.id, documentId: id, timestamp: new Date().toISOString(), }); From 0baa2696b41764ac1ead0354a8bae8ec43437115 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Tue, 5 Dec 2023 10:13:24 +0000 Subject: [PATCH 4/6] fix: removed unused code --- packages/lib/universal/get-feature-flag.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/lib/universal/get-feature-flag.ts b/packages/lib/universal/get-feature-flag.ts index bf79f79ce..0fc0aa131 100644 --- a/packages/lib/universal/get-feature-flag.ts +++ b/packages/lib/universal/get-feature-flag.ts @@ -18,10 +18,6 @@ export const getFlag = async ( ): Promise => { const requestHeaders = options?.requestHeaders ?? {}; - if (!LOCAL_FEATURE_FLAGS[flag]) { - return LOCAL_FEATURE_FLAGS[flag]; - } - if (!isFeatureFlagEnabled()) { return LOCAL_FEATURE_FLAGS[flag] ?? true; } From c313da5028e43bb8f4d86f8ebbab54cf120d42b8 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Thu, 7 Dec 2023 16:29:20 +1100 Subject: [PATCH 5/6] fix: update seal event --- packages/lib/server-only/document/seal-document.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/server-only/document/seal-document.ts b/packages/lib/server-only/document/seal-document.ts index c2e68277b..5fa4b1a00 100644 --- a/packages/lib/server-only/document/seal-document.ts +++ b/packages/lib/server-only/document/seal-document.ts @@ -90,7 +90,7 @@ export const sealDocument = async ({ documentId, sendEmail = true }: SealDocumen if (postHog) { postHog.capture({ distinctId: nanoid(), - event: 'App: Document Signed', + event: 'App: Document Sealed', properties: { documentId: document.id, }, From 2c5d547cdfcdc9506033135f5728629d7b947d02 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Thu, 7 Dec 2023 18:00:54 +1100 Subject: [PATCH 6/6] fix: add missing import --- apps/web/src/app/(signing)/sign/[token]/form.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/app/(signing)/sign/[token]/form.tsx b/apps/web/src/app/(signing)/sign/[token]/form.tsx index 66f3cb5a7..29cd77995 100644 --- a/apps/web/src/app/(signing)/sign/[token]/form.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/form.tsx @@ -10,6 +10,7 @@ import { useForm } from 'react-hook-form'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields'; import type { Document, Field, Recipient } from '@documenso/prisma/client'; +import { trpc } from '@documenso/trpc/react'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button';