From df62c9757992d71c6e132b921339945c745bd31d Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 14 Sep 2023 21:38:00 +0530 Subject: [PATCH] fix: posthog integration fixes --- web/components/issues/modal.tsx | 8 +-- web/constants/posthog-events.ts | 118 ++++++++++++++++++++++++++++++++ web/hooks/use-user.tsx | 4 +- web/lib/app-providers.tsx | 84 +++++++++++++++++++++++ web/package.json | 1 + web/pages/_app.tsx | 33 ++------- web/pages/_document.tsx | 16 ++--- web/store/issues.ts | 2 + 8 files changed, 222 insertions(+), 44 deletions(-) create mode 100644 web/constants/posthog-events.ts create mode 100644 web/lib/app-providers.tsx diff --git a/web/components/issues/modal.tsx b/web/components/issues/modal.tsx index 2dfd4e2c4..8fbcfcf24 100644 --- a/web/components/issues/modal.tsx +++ b/web/components/issues/modal.tsx @@ -1,10 +1,6 @@ import React, { useEffect, useState, useCallback } from "react"; - import { useRouter } from "next/router"; - import { mutate } from "swr"; - -// headless ui import { Dialog, Transition } from "@headlessui/react"; // services import modulesService from "services/modules.service"; @@ -38,6 +34,8 @@ import { } from "constants/fetch-keys"; // constants import { INBOX_ISSUE_SOURCE } from "constants/inbox"; +import { usePostHog } from "posthog-js/react"; +import { ISSUE_CREATE } from "constants/posthog-events"; export interface IssuesModalProps { data?: IIssue | null; @@ -71,6 +69,7 @@ export const CreateUpdateIssueModal: React.FC = ({ fieldsToShow = ["all"], onSubmit, }) => { + const posthog = usePostHog(); // states const [createMore, setCreateMore] = useState(false); const [activeProject, setActiveProject] = useState(null); @@ -242,6 +241,7 @@ export const CreateUpdateIssueModal: React.FC = ({ await issuesService .createIssues(workspaceSlug as string, activeProject ?? "", payload, user) .then(async (res) => { + posthog?.capture(ISSUE_CREATE); mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle); if (payload.module && payload.module !== "") diff --git a/web/constants/posthog-events.ts b/web/constants/posthog-events.ts new file mode 100644 index 000000000..de77cb2ae --- /dev/null +++ b/web/constants/posthog-events.ts @@ -0,0 +1,118 @@ +// Workspace Events +export const CREATE_WORKSPACE = "CREATE_WORKSPACE"; +export const UPDATE_WORKSPACE = "UPDATE_WORKSPACE"; +export const DELETE_WORKSPACE = "DELETE_WORKSPACE"; +// Workspace invite related +export const WORKSPACE_USER_INVITE = "WORKSPACE_USER_INVITE"; +export const WORKSPACE_USER_INVITE_ACCEPT = "WORKSPACE_USER_INVITE_ACCEPT"; +export const WORKSPACE_USER_BULK_INVITE_ACCEPT = "WORKSPACE_USER_BULK_INVITE_ACCEPT"; +// Project Events +export const CREATE_PROJECT = "CREATE_PROJECT"; +export const UPDATE_PROJECT = "UPDATE_PROJECT"; +export const DELETE_PROJECT = "DELETE_PROJECT"; +export const PROJECT_MEMBER_INVITE = "PROJECT_MEMBER_INVITE"; +// Issue Events +export const ISSUE_CREATE = "ISSUE_CREATE"; +export const ISSUE_UPDATE = "ISSUE_UPDATE"; +export const ISSUE_DELETE = "ISSUE_DELETE"; +// Cycle Events +export const CYCLE_CREATE = "CYCLE_CREATE"; +export const CYCLE_UPDATE = "CYCLE_UPDATE"; +export const CYCLE_DELETE = "CYCLE_DELETE"; +// State Events +export const STATE_CREATE = "STATE_CREATE"; +export const STATE_UPDATE = "STATE_UPDATE"; +export const STATE_DELETE = "STATE_DELETE"; +// Module Events +export const MODULE_CREATE = "MODULE_CREATE"; +export const MODULE_UPDATE = "MODULE_UPDATE"; +export const MODULE_DELETE = "MODULE_DELETE"; +// Pages Events +export const PAGE_CREATE = "PAGE_CREATE"; +export const PAGE_UPDATE = "PAGE_UPDATE"; +export const PAGE_DELETE = "PAGE_DELETE"; +// View Events +export const VIEW_CREATE = "VIEW_CREATE"; +export const VIEW_UPDATE = "VIEW_UPDATE"; +export const VIEW_DELETE = "VIEW_DELETE"; +// Issue Comment Events +export const ISSUE_COMMENT_CREATE = "ISSUE_COMMENT_CREATE"; +export const ISSUE_COMMENT_UPDATE = "ISSUE_COMMENT_UPDATE"; +export const ISSUE_COMMENT_DELETE = "ISSUE_COMMENT_DELETE"; +// Miscellaneous Events +export const TOGGLE_CYCLE_ON = "TOGGLE_CYCLE_ON"; +export const TOGGLE_CYCLE_OFF = "TOGGLE_CYCLE_OFF"; +export const TOGGLE_MODULE_ON = "TOGGLE_MODULE_ON"; +export const TOGGLE_MODULE_OFF = "TOGGLE_MODULE_OFF"; +export const TOGGLE_VIEW_ON = "TOGGLE_VIEW_ON"; +export const TOGGLE_VIEW_OFF = "TOGGLE_VIEW_OFF"; +export const TOGGLE_PAGES_ON = "TOGGLE_PAGES_ON"; +export const TOGGLE_PAGES_OFF = "TOGGLE_PAGES_OFF"; +export const TOGGLE_STATE_ON = "TOGGLE_STATE_ON"; +export const TOGGLE_STATE_OFF = "TOGGLE_STATE_OFF"; +export const TOGGLE_INBOX_ON = "TOGGLE_INBOX_ON"; +export const TOGGLE_INBOX_OFF = "TOGGLE_INBOX_OFF"; +// Integration Events +export const ADD_WORKSPACE_INTEGRATION = "ADD_WORKSPACE_INTEGRATION"; +export const REMOVE_WORKSPACE_INTEGRATION = "REMOVE_WORKSPACE_INTEGRATION"; +// GitHub Sync Events +export const GITHUB_REPO_SYNC = "GITHUB_REPO_SYNC"; +// Page Blocks Events +export const PAGE_BLOCK_CREATE = "PAGE_BLOCK_CREATE"; +export const PAGE_BLOCK_UPDATE = "PAGE_BLOCK_UPDATE"; +export const PAGE_BLOCK_DELETE = "PAGE_BLOCK_DELETE"; +export const PAGE_BLOCK_CONVERTED_TO_ISSUE = "PAGE_BLOCK_CONVERTED_TO_ISSUE"; +// Issue Label Events +export const ISSUE_LABEL_CREATE = "ISSUE_LABEL_CREATE"; +export const ISSUE_LABEL_UPDATE = "ISSUE_LABEL_UPDATE"; +export const ISSUE_LABEL_DELETE = "ISSUE_LABEL_DELETE"; +// GPT Events +export const ASK_GPT = "ASK_GPT"; +export const USE_GPT_RESPONSE_IN_ISSUE = "USE_GPT_RESPONSE_IN_ISSUE"; +export const USE_GPT_RESPONSE_IN_PAGE_BLOCK = "USE_GPT_RESPONSE_IN_PAGE_BLOCK"; +// Issue Estimate Events +export const ESTIMATE_CREATE = "ESTIMATE_CREATE"; +export const ESTIMATE_UPDATE = "ESTIMATE_UPDATE"; +export const ESTIMATE_DELETE = "ESTIMATE_DELETE"; +// Inbox Events +export const INBOX_CREATE = "INBOX_CREATE"; +export const INBOX_UPDATE = "INBOX_UPDATE"; +export const INBOX_DELETE = "INBOX_DELETE"; +export const INBOX_ISSUE_CREATE = "INBOX_ISSUE_CREATE"; +export const INBOX_ISSUE_UPDATE = "INBOX_ISSUE_UPDATE"; +export const INBOX_ISSUE_DELETE = "INBOX_ISSUE_DELETE"; +export const INBOX_ISSUE_DUPLICATED = "INBOX_ISSUE_DUPLICATED"; +export const INBOX_ISSUE_ACCEPTED = "INBOX_ISSUE_ACCEPTED"; +export const INBOX_ISSUE_SNOOZED = "INBOX_ISSUE_SNOOZED"; +export const INBOX_ISSUE_REJECTED = "INBOX_ISSUE_REJECTED"; +// Importer Events +export const GITHUB_IMPORTER_CREATE = "GITHUB_IMPORTER_CREATE"; +export const GITHUB_IMPORTER_DELETE = "GITHUB_IMPORTER_DELETE"; +export const JIRA_IMPORTER_CREATE = "JIRA_IMPORTER_CREATE"; +export const JIRA_IMPORTER_DELETE = "JIRA_IMPORTER_DELETE"; +// Exporter Events +export const CSV_EXPORTER_CREATE = "CSV_EXPORTER_CREATE"; +// Analytics Events +export const WORKSPACE_SCOPE_AND_DEMAND_ANALYTICS = "WORKSPACE_SCOPE_AND_DEMAND_ANALYTICS"; +export const WORKSPACE_CUSTOM_ANALYTICS = "WORKSPACE_CUSTOM_ANALYTICS"; +export const WORKSPACE_ANALYTICS_EXPORT = "WORKSPACE_ANALYTICS_EXPORT"; +// Project Analytics +export const PROJECT_SCOPE_AND_DEMAND_ANALYTICS = "PROJECT_SCOPE_AND_DEMAND_ANALYTICS"; +export const PROJECT_CUSTOM_ANALYTICS = "PROJECT_CUSTOM_ANALYTICS"; +export const PROJECT_ANALYTICS_EXPORT = "PROJECT_ANALYTICS_EXPORT"; +// Cycle Custom +export const CYCLE_SCOPE_AND_DEMAND_ANALYTICS = "CYCLE_SCOPE_AND_DEMAND_ANALYTICS"; +export const CYCLE_CUSTOM_ANALYTICS = "CYCLE_CUSTOM_ANALYTICS"; +export const CYCLE_ANALYTICS_EXPORT = "CYCLE_ANALYTICS_EXPORT"; +// Module Custom +export const MODULE_SCOPE_AND_DEMAND_ANALYTICS = "MODULE_SCOPE_AND_DEMAND_ANALYTICS"; +export const MODULE_CUSTOM_ANALYTICS = "MODULE_CUSTOM_ANALYTICS"; +export const MODULE_ANALYTICS_EXPORT = "MODULE_ANALYTICS_EXPORT"; +// Reaction Events +export const ISSUE_REACTION_CREATE = "ISSUE_REACTION_CREATE"; +export const ISSUE_COMMENT_REACTION_CREATE = "ISSUE_COMMENT_REACTION_CREATE"; +export const ISSUE_REACTION_DELETE = "ISSUE_REACTION_DELETE"; +export const ISSUE_COMMENT_REACTION_DELETE = "ISSUE_COMMENT_REACTION_DELETE"; +// Leave Project/Workspace +export const PROJECT_MEMBER_LEAVE = "PROJECT_MEMBER_LEAVE"; +export const WORKSPACE_MEMBER_LEAVE = "WORKSPACE_MEMBER_LEAVE"; diff --git a/web/hooks/use-user.tsx b/web/hooks/use-user.tsx index f9b6e36d8..e2585ea5e 100644 --- a/web/hooks/use-user.tsx +++ b/web/hooks/use-user.tsx @@ -1,15 +1,17 @@ import { useEffect } from "react"; import { useRouter } from "next/router"; +import { usePostHog } from "posthog-js/react"; import useSWR from "swr"; // services import userService from "services/user.service"; // constants import { CURRENT_USER } from "constants/fetch-keys"; // types -import type { ICurrentUserResponse, IUser } from "types"; +import type { ICurrentUserResponse } from "types"; export default function useUser({ redirectTo = "", redirectIfFound = false, options = {} } = {}) { const router = useRouter(); + const posthog = usePostHog(); // API to fetch user information const { data, isLoading, error, mutate } = useSWR( CURRENT_USER, diff --git a/web/lib/app-providers.tsx b/web/lib/app-providers.tsx new file mode 100644 index 000000000..dfd6f5ba0 --- /dev/null +++ b/web/lib/app-providers.tsx @@ -0,0 +1,84 @@ +import { useEffect } from "react"; +import dynamic from "next/dynamic"; +import Router, { useRouter } from "next/router"; +import { ThemeProvider } from "next-themes"; +import NProgress from "nprogress"; +import posthog from "posthog-js"; +import { PostHogProvider } from "posthog-js/react"; +// mobx store provider +import { MobxStoreProvider } from "lib/mobx/store-provider"; +import MobxStoreInit from "lib/mobx/store-init"; +// constants +import { THEMES } from "constants/themes"; +// contexts +import { ToastContextProvider } from "contexts/toast.context"; +import useUser from "hooks/use-user"; + +const CrispWithNoSSR = dynamic(() => import("constants/crisp"), { ssr: false }); + +// nprogress +NProgress.configure({ showSpinner: false }); +Router.events.on("routeChangeStart", NProgress.start); +Router.events.on("routeChangeError", NProgress.done); +Router.events.on("routeChangeComplete", NProgress.done); + +// Check that PostHog is client-side (used to handle Next.js SSR) +if (typeof window !== "undefined") { + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY || "", { + api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://app.posthog.com", + // Enable debug mode in development + loaded: (posthog) => { + if (process.env.NODE_ENV === "development") posthog.debug(); + }, + autocapture: false, + capture_pageview: false, // Disable automatic pageview capture, as we capture manually + }); +} + +const AppProviders = (props: any) => { + const router = useRouter(); + const { user } = useUser(); + + useEffect(() => { + if (user) { + // Identify sends an event, so you want may want to limit how often you call it + posthog?.identify(user.email, { + email: user.email, + first_name: user.first_name, + last_name: user.last_name, + id: user.id, + }); + } + }, [user?.id]); + + const RemainingProviders = ( + + + + + + {props.children} + + + + ); + + useEffect(() => { + // Track page views + const handleRouteChange = () => { + posthog?.capture("$pageview"); + }; + router.events.on("routeChangeComplete", handleRouteChange); + + return () => { + router.events.off("routeChangeComplete", handleRouteChange); + }; + }, []); + + if (process.env.NEXT_PUBLIC_POSTHOG_KEY && process.env.NEXT_PUBLIC_POSTHOG_HOST) { + return {RemainingProviders}; + } + return <>{RemainingProviders}; +}; + +export default AppProviders; diff --git a/web/package.json b/web/package.json index 3da1e3089..88ab2b0b9 100644 --- a/web/package.json +++ b/web/package.json @@ -64,6 +64,7 @@ "next-pwa": "^5.6.0", "next-themes": "^0.2.1", "nprogress": "^0.2.0", + "posthog-js": "^1.78.4", "react": "18.2.0", "react-beautiful-dnd": "^13.1.1", "react-color": "^2.19.3", diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index b94e52d6b..e50442927 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -1,33 +1,16 @@ import Head from "next/head"; -import dynamic from "next/dynamic"; -import Router from "next/router"; -import { ThemeProvider } from "next-themes"; -import NProgress from "nprogress"; // styles import "styles/globals.css"; import "styles/editor.css"; import "styles/command-pallette.css"; import "styles/nprogress.css"; import "styles/react-datepicker.css"; -// contexts -import { ToastContextProvider } from "contexts/toast.context"; // types import type { AppProps } from "next/app"; // constants -import { THEMES } from "constants/themes"; -// constants import { SITE_TITLE } from "constants/seo-variables"; -// mobx store provider -import { MobxStoreProvider } from "lib/mobx/store-provider"; -import MobxStoreInit from "lib/mobx/store-init"; - -const CrispWithNoSSR = dynamic(() => import("constants/crisp"), { ssr: false }); - -// nprogress -NProgress.configure({ showSpinner: false }); -Router.events.on("routeChangeStart", NProgress.start); -Router.events.on("routeChangeError", NProgress.done); -Router.events.on("routeChangeComplete", NProgress.done); +//lib +import AppProviders from "lib/app-providers"; function MyApp({ Component, pageProps }: AppProps) { return ( @@ -35,15 +18,9 @@ function MyApp({ Component, pageProps }: AppProps) { {SITE_TITLE} - - - - - - - - - + + + ); } diff --git a/web/pages/_document.tsx b/web/pages/_document.tsx index 7bb7480c0..eb186be11 100644 --- a/web/pages/_document.tsx +++ b/web/pages/_document.tsx @@ -32,10 +32,6 @@ class MyDocument extends Document { - - -
- {process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN && ( )} + + +
+ ); diff --git a/web/store/issues.ts b/web/store/issues.ts index 286d98534..5400a1d8c 100644 --- a/web/store/issues.ts +++ b/web/store/issues.ts @@ -1,5 +1,7 @@ // mobx +import { ISSUE_CREATE } from "constants/posthog-events"; import { action, observable, runInAction, makeAutoObservable } from "mobx"; +import { usePostHog } from "posthog-js/react"; // services import issueService from "services/issues.service"; // types