From 11d57a5bf0a32596298cd6996d3be28dacd77ff0 Mon Sep 17 00:00:00 2001 From: Ramesh Kumar Chandra <31303617+rameshkumarchandra@users.noreply.github.com> Date: Sat, 25 Nov 2023 21:26:26 +0530 Subject: [PATCH] fix: track events updated, extra parameters added, added events for issues, pages, states, cycles (#2875) * fix: event tracking method updated to store, chore: updated and added events for workspace, projects and create issue * fix: posthog auth event tracking --------- Co-authored-by: NarayanBavisetti --- apiserver/plane/app/views/authentication.py | 155 ++++++------------ apiserver/plane/app/views/oauth.py | 70 +++----- .../plane/bgtasks/event_tracking_task.py | 31 ++++ apiserver/plane/settings/common.py | 6 +- apiserver/requirements/base.txt | 1 + .../actions/project-actions.tsx | 2 + .../command-palette/command-modal.tsx | 3 + .../command-palette/command-pallette.tsx | 3 + web/components/cycles/cycles-board-card.tsx | 5 +- web/components/cycles/cycles-list-item.tsx | 3 +- web/components/cycles/cycles-list.tsx | 8 +- web/components/cycles/delete-modal.tsx | 26 ++- web/components/cycles/modal.tsx | 17 +- web/components/cycles/sidebar.tsx | 41 ++++- web/components/headers/cycle-issues.tsx | 8 +- web/components/headers/cycles.tsx | 8 +- web/components/headers/module-issues.tsx | 7 +- web/components/headers/project-issues.tsx | 7 +- web/components/headers/projects.tsx | 7 +- .../headers/workspace-dashboard.tsx | 2 + .../inbox/modals/create-issue-modal.tsx | 19 ++- .../integration/jira/give-details.tsx | 7 +- .../issue-layouts/empty-states/cycle.tsx | 7 +- .../empty-states/global-view.tsx | 12 +- .../issue-layouts/empty-states/module.tsx | 7 +- .../empty-states/project-view.tsx | 7 +- .../issue-layouts/empty-states/project.tsx | 7 +- web/components/issues/modal.tsx | 36 +++- web/components/onboarding/invitations.tsx | 21 ++- web/components/onboarding/invite-members.tsx | 3 +- web/components/onboarding/tour/root.tsx | 3 +- .../page-views/workspace-dashboard.tsx | 8 +- web/components/project/card-list.tsx | 7 +- .../project/create-project-modal.tsx | 24 ++- web/components/project/form.tsx | 15 +- .../project/leave-project-modal.tsx | 13 ++ web/components/project/member-list.tsx | 8 +- .../project/send-project-invitation-modal.tsx | 17 +- .../project/settings/features-list.tsx | 12 ++ web/components/project/sidebar-list-item.tsx | 3 +- web/components/project/sidebar-list.tsx | 13 +- .../states/create-update-state-inline.tsx | 41 +++-- web/components/states/delete-state-modal.tsx | 16 +- .../project-setting-state-list-item.tsx | 8 +- .../states/project-setting-state-list.tsx | 7 +- .../workspace/create-workspace-form.tsx | 31 ++-- .../workspace/delete-workspace-modal.tsx | 21 ++- .../workspace/settings/workspace-details.tsx | 26 ++- web/components/workspace/sidebar-dropdown.tsx | 2 + .../workspace/sidebar-quick-action.tsx | 46 +++--- web/pages/[workspaceSlug]/analytics.tsx | 11 +- .../[workspaceSlug]/settings/members.tsx | 13 +- web/store/event-tracker.store.ts | 80 +++++++++ web/store/root.ts | 6 + 54 files changed, 662 insertions(+), 305 deletions(-) create mode 100644 apiserver/plane/bgtasks/event_tracking_task.py create mode 100644 web/store/event-tracker.store.ts diff --git a/apiserver/plane/app/views/authentication.py b/apiserver/plane/app/views/authentication.py index 61a79b984..5ac9377d3 100644 --- a/apiserver/plane/app/views/authentication.py +++ b/apiserver/plane/app/views/authentication.py @@ -35,7 +35,7 @@ from plane.settings.redis import redis_instance from plane.bgtasks.magic_link_code_task import magic_link from plane.license.models import InstanceConfiguration from plane.license.utils.instance_value import get_configuration_value - +from plane.bgtasks.event_tracking_task import auth_events def get_tokens_for_user(user): refresh = RefreshToken.for_user(user) @@ -162,30 +162,17 @@ class SignUpEndpoint(BaseAPIView): workspace_member_invites.delete() project_member_invites.delete() - try: - # Send Analytics - if settings.ANALYTICS_BASE_API: - _ = requests.post( - settings.ANALYTICS_BASE_API, - headers={ - "Content-Type": "application/json", - "X-Auth-Token": settings.ANALYTICS_SECRET_KEY, - }, - json={ - "event_id": uuid.uuid4().hex, - "event_data": { - "medium": "email", - }, - "user": {"email": email, "id": str(user.id)}, - "device_ctx": { - "ip": request.META.get("REMOTE_ADDR"), - "user_agent": request.META.get("HTTP_USER_AGENT"), - }, - "event_type": "SIGN_UP", - }, - ) - except RequestException as e: - capture_exception(e) + # Send event + if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="EMAIL", + first_time=True + ) access_token, refresh_token = get_tokens_for_user(user) @@ -305,30 +292,17 @@ class SignInEndpoint(BaseAPIView): # Delete all the invites workspace_member_invites.delete() project_member_invites.delete() - try: - # Send Analytics - if settings.ANALYTICS_BASE_API: - _ = requests.post( - settings.ANALYTICS_BASE_API, - headers={ - "Content-Type": "application/json", - "X-Auth-Token": settings.ANALYTICS_SECRET_KEY, - }, - json={ - "event_id": uuid.uuid4().hex, - "event_data": { - "medium": "email", - }, - "user": {"email": email, "id": str(user.id)}, - "device_ctx": { - "ip": request.META.get("REMOTE_ADDR"), - "user_agent": request.META.get("HTTP_USER_AGENT"), - }, - "event_type": "SIGN_IN", - }, - ) - except RequestException as e: - capture_exception(e) + # Send event + if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="EMAIL", + first_time=False + ) access_token, refresh_token = get_tokens_for_user(user) data = { @@ -476,32 +450,25 @@ class MagicSignInEndpoint(BaseAPIView): if str(token) == str(user_token): if User.objects.filter(email=email).exists(): user = User.objects.get(email=email) - try: - # Send event to Jitsu for tracking - if settings.ANALYTICS_BASE_API: - _ = requests.post( - settings.ANALYTICS_BASE_API, - headers={ - "Content-Type": "application/json", - "X-Auth-Token": settings.ANALYTICS_SECRET_KEY, - }, - json={ - "event_id": uuid.uuid4().hex, - "event_data": { - "medium": "code", - }, - "user": {"email": email, "id": str(user.id)}, - "device_ctx": { - "ip": request.META.get("REMOTE_ADDR"), - "user_agent": request.META.get( - "HTTP_USER_AGENT" - ), - }, - "event_type": "SIGN_IN", - }, - ) - except RequestException as e: - capture_exception(e) + if not user.is_active: + return Response( + { + "error": "Your account has been deactivated. Please contact your site administrator." + }, + status=status.HTTP_403_FORBIDDEN, + ) + # Send event + if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="MAGIC_LINK", + first_time=False + ) + else: user = User.objects.create( email=email, @@ -509,32 +476,18 @@ class MagicSignInEndpoint(BaseAPIView): password=make_password(uuid.uuid4().hex), is_password_autoset=True, ) - try: - # Send event to Jitsu for tracking - if settings.ANALYTICS_BASE_API: - _ = requests.post( - settings.ANALYTICS_BASE_API, - headers={ - "Content-Type": "application/json", - "X-Auth-Token": settings.ANALYTICS_SECRET_KEY, - }, - json={ - "event_id": uuid.uuid4().hex, - "event_data": { - "medium": "code", - }, - "user": {"email": email, "id": str(user.id)}, - "device_ctx": { - "ip": request.META.get("REMOTE_ADDR"), - "user_agent": request.META.get( - "HTTP_USER_AGENT" - ), - }, - "event_type": "SIGN_UP", - }, - ) - except RequestException as e: - capture_exception(e) + + # Send event + if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium="MAGIC_LINK", + first_time=True + ) user.is_active = True user.last_active = timezone.now() diff --git a/apiserver/plane/app/views/oauth.py b/apiserver/plane/app/views/oauth.py index 04810fddd..bab502bd6 100644 --- a/apiserver/plane/app/views/oauth.py +++ b/apiserver/plane/app/views/oauth.py @@ -29,6 +29,7 @@ from plane.db.models import ( ProjectMemberInvite, ProjectMember, ) +from plane.bgtasks.event_tracking_task import auth_events from .base import BaseAPIView from plane.license.models import InstanceConfiguration from plane.license.utils.instance_value import get_configuration_value @@ -285,29 +286,18 @@ class OauthEndpoint(BaseAPIView): "last_login_at": timezone.now(), }, ) - try: - if settings.ANALYTICS_BASE_API: - _ = requests.post( - settings.ANALYTICS_BASE_API, - headers={ - "Content-Type": "application/json", - "X-Auth-Token": settings.ANALYTICS_SECRET_KEY, - }, - json={ - "event_id": uuid.uuid4().hex, - "event_data": { - "medium": f"oauth-{medium}", - }, - "user": {"email": email, "id": str(user.id)}, - "device_ctx": { - "ip": request.META.get("REMOTE_ADDR"), - "user_agent": request.META.get("HTTP_USER_AGENT"), - }, - "event_type": "SIGN_IN", - }, - ) - except RequestException as e: - capture_exception(e) + + # Send event + if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium=medium.upper(), + first_time=False + ) access_token, refresh_token = get_tokens_for_user(user) @@ -428,29 +418,17 @@ class OauthEndpoint(BaseAPIView): workspace_member_invites.delete() project_member_invites.delete() - try: - if settings.ANALYTICS_BASE_API: - _ = requests.post( - settings.ANALYTICS_BASE_API, - headers={ - "Content-Type": "application/json", - "X-Auth-Token": settings.ANALYTICS_SECRET_KEY, - }, - json={ - "event_id": uuid.uuid4().hex, - "event_data": { - "medium": f"oauth-{medium}", - }, - "user": {"email": email, "id": str(user.id)}, - "device_ctx": { - "ip": request.META.get("REMOTE_ADDR"), - "user_agent": request.META.get("HTTP_USER_AGENT"), - }, - "event_type": "SIGN_UP", - }, - ) - except RequestException as e: - capture_exception(e) + # Send event + if settings.POSTHOG_API_KEY and settings.POSTHOG_HOST: + auth_events.delay( + user=user.id, + email=email, + user_agent=request.META.get("HTTP_USER_AGENT"), + ip=request.META.get("REMOTE_ADDR"), + event_name="SIGN_IN", + medium=medium.upper(), + first_time=True + ) SocialLoginConnection.objects.update_or_create( medium=medium, diff --git a/apiserver/plane/bgtasks/event_tracking_task.py b/apiserver/plane/bgtasks/event_tracking_task.py new file mode 100644 index 000000000..e8b5b7831 --- /dev/null +++ b/apiserver/plane/bgtasks/event_tracking_task.py @@ -0,0 +1,31 @@ +import uuid + +from posthog import Posthog +from django.conf import settings + +#third party imports +from celery import shared_task +from sentry_sdk import capture_exception + + +@shared_task +def auth_events(user, email, user_agent, ip, event_name, medium, first_time): + print(user, email, user_agent, ip, event_name, medium, first_time) + try: + posthog = Posthog(settings.POSTHOG_API_KEY, host=settings.POSTHOG_HOST) + posthog.capture( + email, + event=event_name, + properties={ + "event_id": uuid.uuid4().hex, + "user": {"email": email, "id": str(user)}, + "device_ctx": { + "ip": ip, + "user_agent": user_agent, + }, + "medium": medium, + "first_time": first_time + } + ) + except Exception as e: + capture_exception(e) \ No newline at end of file diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index c76ec1340..b99f74c84 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -322,4 +322,8 @@ ANALYTICS_SECRET_KEY = os.environ.get("ANALYTICS_SECRET_KEY", False) ANALYTICS_BASE_API = os.environ.get("ANALYTICS_BASE_API", False) # Use Minio settings -USE_MINIO = int(os.environ.get("USE_MINIO", 0)) == 1 \ No newline at end of file +USE_MINIO = int(os.environ.get("USE_MINIO", 0)) == 1 + +# Posthog settings +POSTHOG_API_KEY = os.environ.get("POSTHOG_API_KEY", False) +POSTHOG_HOST = os.environ.get("POSTHOG_HOST", False) \ No newline at end of file diff --git a/apiserver/requirements/base.txt b/apiserver/requirements/base.txt index 7c5f1cf28..55329be73 100644 --- a/apiserver/requirements/base.txt +++ b/apiserver/requirements/base.txt @@ -36,3 +36,4 @@ scout-apm==2.26.1 openpyxl==3.1.2 beautifulsoup4==4.12.2 dj-database-url==2.1.0 +posthog==3.0.2 \ No newline at end of file diff --git a/web/components/command-palette/actions/project-actions.tsx b/web/components/command-palette/actions/project-actions.tsx index 5db9b2c4e..d7a196676 100644 --- a/web/components/command-palette/actions/project-actions.tsx +++ b/web/components/command-palette/actions/project-actions.tsx @@ -14,6 +14,7 @@ export const CommandPaletteProjectActions: React.FC = (props) => { const { commandPalette: { toggleCreateCycleModal, toggleCreateModuleModal, toggleCreatePageModal, toggleCreateViewModal }, + trackEvent: { setTrackElement } } = useMobxStore(); return ( @@ -22,6 +23,7 @@ export const CommandPaletteProjectActions: React.FC = (props) => { { closePalette(); + setTrackElement("COMMAND_PALETTE") toggleCreateCycleModal(true); }} className="focus:outline-none" diff --git a/web/components/command-palette/command-modal.tsx b/web/components/command-palette/command-modal.tsx index f43032cf2..7d1352983 100644 --- a/web/components/command-palette/command-modal.tsx +++ b/web/components/command-palette/command-modal.tsx @@ -62,6 +62,7 @@ export const CommandModal: React.FC = observer(() => { toggleCreateIssueModal, toggleCreateProjectModal, }, + trackEvent: { setTrackElement } } = useMobxStore(); // router @@ -273,6 +274,7 @@ export const CommandModal: React.FC = observer(() => { { closePalette(); + setTrackElement("COMMAND_PALETTE"); toggleCreateIssueModal(true); }} className="focus:bg-custom-background-80" @@ -290,6 +292,7 @@ export const CommandModal: React.FC = observer(() => { { closePalette(); + setTrackElement("COMMAND_PALETTE"); toggleCreateProjectModal(true); }} className="focus:outline-none" diff --git a/web/components/command-palette/command-pallette.tsx b/web/components/command-palette/command-pallette.tsx index 7530a15a6..f30c8eb4f 100644 --- a/web/components/command-palette/command-pallette.tsx +++ b/web/components/command-palette/command-pallette.tsx @@ -33,6 +33,7 @@ export const CommandPalette: FC = observer(() => { commandPalette, theme: { toggleSidebar }, user: { currentUser }, + trackEvent: { setTrackElement } } = useMobxStore(); const { toggleCommandPaletteModal, @@ -112,8 +113,10 @@ export const CommandPalette: FC = observer(() => { } } else { if (keyPressed === "c") { + setTrackElement("SHORTCUT_KEY"); toggleCreateIssueModal(true); } else if (keyPressed === "p") { + setTrackElement("SHORTCUT_KEY"); toggleCreateProjectModal(true); } else if (keyPressed === "h") { toggleShortcutModal(true); diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index 7de04172f..da8641f66 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -33,7 +33,7 @@ export interface ICyclesBoardCard { export const CyclesBoardCard: FC = (props) => { const { cycle, workspaceSlug, projectId } = props; // store - const { cycle: cycleStore } = useMobxStore(); + const { cycle: cycleStore, trackEvent: { setTrackElement } } = useMobxStore(); // toast const { setToastAlert } = useToast(); // states @@ -119,6 +119,7 @@ export const CyclesBoardCard: FC = (props) => { e.preventDefault(); e.stopPropagation(); setDeleteModal(true); + setTrackElement("CYCLE_PAGE_BOARD_LAYOUT"); }; const openCycleOverview = (e: MouseEvent) => { @@ -252,7 +253,7 @@ export const CyclesBoardCard: FC = (props) => { - Delete module + Delete cycle diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index f93e428e7..bf0d5c781 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -38,7 +38,7 @@ type TCyclesListItem = { export const CyclesListItem: FC = (props) => { const { cycle, workspaceSlug, projectId } = props; // store - const { cycle: cycleStore } = useMobxStore(); + const { cycle: cycleStore, trackEvent: { setTrackElement } } = useMobxStore(); // toast const { setToastAlert } = useToast(); // states @@ -119,6 +119,7 @@ export const CyclesListItem: FC = (props) => { e.preventDefault(); e.stopPropagation(); setDeleteModal(true); + setTrackElement("CYCLE_PAGE_LIST_LAYOUT"); }; const openCycleOverview = (e: MouseEvent) => { diff --git a/web/components/cycles/cycles-list.tsx b/web/components/cycles/cycles-list.tsx index 0cff682af..bf8445cde 100644 --- a/web/components/cycles/cycles-list.tsx +++ b/web/components/cycles/cycles-list.tsx @@ -19,7 +19,7 @@ export interface ICyclesList { export const CyclesList: FC = observer((props) => { const { cycles, filter, workspaceSlug, projectId } = props; - const { commandPalette: commandPaletteStore } = useMobxStore(); + const { commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore(); return ( <> @@ -57,7 +57,11 @@ export const CyclesList: FC = observer((props) => { diff --git a/web/components/cycles/delete-modal.tsx b/web/components/cycles/delete-modal.tsx index aa28ea153..a9d145447 100644 --- a/web/components/cycles/delete-modal.tsx +++ b/web/components/cycles/delete-modal.tsx @@ -24,7 +24,7 @@ interface ICycleDelete { export const CycleDeleteModal: React.FC = observer((props) => { const { isOpen, handleClose, cycle, workspaceSlug, projectId } = props; // store - const { cycle: cycleStore } = useMobxStore(); + const { cycle: cycleStore, trackEvent: { postHogEventTracker } } = useMobxStore(); // toast const { setToastAlert } = useToast(); // states @@ -36,11 +36,25 @@ export const CycleDeleteModal: React.FC = observer((props) => { setLoader(true); if (cycle?.id) try { - await cycleStore.removeCycle(workspaceSlug, projectId, cycle?.id); - setToastAlert({ - type: "success", - title: "Success!", - message: "Cycle deleted successfully.", + await cycleStore.removeCycle(workspaceSlug, projectId, cycle?.id).then((res) => { + setToastAlert({ + type: "success", + title: "Success!", + message: "Cycle deleted successfully.", + }); + postHogEventTracker( + "CYCLE_DELETE", + { + state: "SUCCESS" + } + ); + }).catch((error) => { + postHogEventTracker( + "CYCLE_DELETE", + { + state: "FAILED" + } + ); }); if (cycleId) router.replace(`/${workspaceSlug}/projects/${projectId}/cycles`); diff --git a/web/components/cycles/modal.tsx b/web/components/cycles/modal.tsx index 0db3a5ed1..652b66098 100644 --- a/web/components/cycles/modal.tsx +++ b/web/components/cycles/modal.tsx @@ -24,7 +24,7 @@ const cycleService = new CycleService(); export const CycleCreateUpdateModal: React.FC = (props) => { const { isOpen, handleClose, data, workspaceSlug, projectId } = props; // store - const { cycle: cycleStore } = useMobxStore(); + const { cycle: cycleStore, trackEvent: { postHogEventTracker } } = useMobxStore(); // states const [activeProject, setActiveProject] = useState(projectId); // toast @@ -35,12 +35,19 @@ export const CycleCreateUpdateModal: React.FC = (props) => { const selectedProjectId = payload.project ?? projectId.toString(); await cycleStore .createCycle(workspaceSlug, selectedProjectId, payload) - .then(() => { + .then((res) => { setToastAlert({ type: "success", title: "Success!", message: "Cycle created successfully.", }); + postHogEventTracker( + "CYCLE_CREATE", + { + ...res, + state: "SUCCESS", + } + ); }) .catch(() => { setToastAlert({ @@ -48,6 +55,12 @@ export const CycleCreateUpdateModal: React.FC = (props) => { title: "Error!", message: "Error in creating cycle. Please try again.", }); + postHogEventTracker( + "CYCLE_CREATE", + { + state: "FAILED", + } + ); }); }; diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index c41825c73..d21825344 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -52,7 +52,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const router = useRouter(); const { workspaceSlug, projectId, peekCycle } = router.query; - const { cycle: cycleDetailsStore } = useMobxStore(); + const { cycle: cycleDetailsStore, trackEvent: { setTrackElement, postHogEventTracker } } = useMobxStore(); const cycleDetails = cycleDetailsStore.cycle_details[cycleId] ?? undefined; @@ -74,8 +74,27 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { cycleService .patchCycle(workspaceSlug as string, projectId as string, cycleId as string, data) - .then(() => mutate(CYCLE_DETAILS(cycleId as string))) - .catch((e) => console.log(e)); + .then((res) => { + mutate(CYCLE_DETAILS(cycleId as string)); + postHogEventTracker( + "CYCLE_UPDATE", + { + ...res, + state: "SUCCESS" + } + ); + } + ) + .catch((e) => { + console.log(e); + postHogEventTracker( + "CYCLE_UPDATE", + { + state: "FAILED" + } + ); + } + ); }; const handleCopyText = () => { @@ -284,10 +303,10 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { cycleDetails.total_issues === 0 ? "0 Issue" : cycleDetails.total_issues === cycleDetails.completed_issues - ? cycleDetails.total_issues > 1 - ? `${cycleDetails.total_issues}` - : `${cycleDetails.total_issues}` - : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; + ? cycleDetails.total_issues > 1 + ? `${cycleDetails.total_issues}` + : `${cycleDetails.total_issues}` + : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; return ( <> @@ -317,7 +336,11 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { {!isCompleted && ( - setCycleDeleteModal(true)}> + { + setTrackElement("CYCLE_PAGE_SIDEBAR"); + setCycleDeleteModal(true) + } + }> Delete cycle @@ -367,6 +390,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { value={watch("start_date") ? watch("start_date") : cycleDetails?.start_date} onChange={(val) => { if (val) { + setTrackElement("CYCLE_PAGE_SIDEBAR_START_DATE_BUTTON"); handleStartDateChange(val); } }} @@ -402,6 +426,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { value={watch("end_date") ? watch("end_date") : cycleDetails?.end_date} onChange={(val) => { if (val) { + setTrackElement("CYCLE_PAGE_SIDEBAR_END_DATE_BUTTON"); handleEndDateChange(val); } }} diff --git a/web/components/headers/cycle-issues.tsx b/web/components/headers/cycle-issues.tsx index 8bff1c999..5353adbbe 100644 --- a/web/components/headers/cycle-issues.tsx +++ b/web/components/headers/cycle-issues.tsx @@ -40,7 +40,7 @@ export const CycleIssuesHeader: React.FC = observer(() => { projectLabel: { projectLabels }, projectState: projectStateStore, commandPalette: commandPaletteStore, - + trackEvent: { setTrackElement }, cycleIssuesFilter: { issueFilters, updateFilters }, } = useMobxStore(); @@ -190,7 +190,11 @@ export const CycleIssuesHeader: React.FC = observer(() => { - diff --git a/web/components/headers/module-issues.tsx b/web/components/headers/module-issues.tsx index 1c682a118..c488d7fdf 100644 --- a/web/components/headers/module-issues.tsx +++ b/web/components/headers/module-issues.tsx @@ -38,6 +38,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => { projectMember: { projectMembers }, projectState: projectStateStore, commandPalette: commandPaletteStore, + trackEvent: { setTrackElement }, projectLabel: { projectLabels }, moduleIssuesFilter: { issueFilters, updateFilters }, } = useMobxStore(); @@ -190,7 +191,11 @@ export const ModuleIssuesHeader: React.FC = observer(() => { - - diff --git a/web/components/headers/projects.tsx b/web/components/headers/projects.tsx index 0702ae22e..8b384e8cb 100644 --- a/web/components/headers/projects.tsx +++ b/web/components/headers/projects.tsx @@ -11,7 +11,7 @@ export const ProjectsHeader = observer(() => { const { workspaceSlug } = router.query; // store - const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); + const { project: projectStore, commandPalette: commandPaletteStore, trackEvent: {setTrackElement} } = useMobxStore(); const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : []; @@ -41,7 +41,10 @@ export const ProjectsHeader = observer(() => { )} - diff --git a/web/components/headers/workspace-dashboard.tsx b/web/components/headers/workspace-dashboard.tsx index 76bd6bc30..eeff05ba7 100644 --- a/web/components/headers/workspace-dashboard.tsx +++ b/web/components/headers/workspace-dashboard.tsx @@ -8,9 +8,11 @@ import githubWhiteImage from "/public/logos/github-white.png"; // components import { ProductUpdatesModal } from "components/common"; import { Breadcrumbs } from "@plane/ui"; +import { useMobxStore } from "lib/mobx/store-provider"; export const WorkspaceDashboardHeader = () => { const [isProductUpdatesModalOpen, setIsProductUpdatesModalOpen] = useState(false); + const { trackEvent: { postHogEventTracker } } = useMobxStore(); // theme const { resolvedTheme } = useTheme(); diff --git a/web/components/inbox/modals/create-issue-modal.tsx b/web/components/inbox/modals/create-issue-modal.tsx index 28bde21e4..5f8ac3fda 100644 --- a/web/components/inbox/modals/create-issue-modal.tsx +++ b/web/components/inbox/modals/create-issue-modal.tsx @@ -46,7 +46,7 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { const router = useRouter(); const { workspaceSlug, projectId, inboxId } = router.query; - const { inboxIssueDetails: inboxIssueDetailsStore } = useMobxStore(); + const { inboxIssueDetails: inboxIssueDetailsStore, trackEvent: { postHogEventTracker } } = useMobxStore(); const { control, @@ -70,6 +70,21 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { router.push(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${res.issue_inbox[0].id}`); handleClose(); } else reset(defaultValues); + postHogEventTracker( + "ISSUE_CREATE", + { + ...res, + state: "SUCCESS" + } + ); + }).catch((error) => { + console.log(error); + postHogEventTracker( + "ISSUE_CREATE", + { + state: "FAILED" + } + ); }); }; @@ -172,7 +187,7 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { onClick={() => setCreateMore((prevData) => !prevData)} > Create more - {}} size="md" /> + { }} size="md" />
diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index cb7aece1e..2e9ade28f 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -18,7 +18,7 @@ export interface IProjectCardList { export const ProjectCardList: FC = observer((props) => { const { workspaceSlug } = props; // store - const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); + const { project: projectStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore(); const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; @@ -57,7 +57,10 @@ export const ProjectCardList: FC = observer((props) => { primaryButton={{ icon: , text: "Start something new", - onClick: () => commandPaletteStore.toggleCreateProjectModal(true), + onClick: () => { + setTrackElement("PROJECTS_EMPTY_STATE"); + commandPaletteStore.toggleCreateProjectModal(true) + } }} /> )} diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index 5cf6c1bcd..14e54c208 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -20,8 +20,6 @@ import { getRandomEmoji, renderEmoji } from "helpers/emoji.helper"; import { IWorkspaceMember } from "types"; // constants import { NETWORK_CHOICES, PROJECT_UNSPLASH_COVERS } from "constants/project"; -// track events -import { trackEvent } from "helpers/event-tracker.helper"; type Props = { isOpen: boolean; @@ -68,6 +66,7 @@ export const CreateProjectModal: FC = observer((props) => { const { project: projectStore, workspaceMember: { workspaceMembers }, + trackEvent: { postHogEventTracker } } = useMobxStore(); // states const [isChangeInIdentifierRequired, setIsChangeInIdentifierRequired] = useState(true); @@ -132,11 +131,11 @@ export const CreateProjectModal: FC = observer((props) => { .createProject(workspaceSlug.toString(), payload) .then((res) => { const newPayload = { - ...payload, - id: res.id + ...res, + state: "SUCCESS" } - trackEvent( - "CREATE_PROJECT", + postHogEventTracker( + "PROJECT_CREATE", newPayload, ) setToastAlert({ @@ -150,12 +149,19 @@ export const CreateProjectModal: FC = observer((props) => { handleClose(); }) .catch((err) => { - Object.keys(err.data).map((key) => + Object.keys(err.data).map((key) => { setToastAlert({ type: "error", title: "Error!", message: err.data[key], - }) + }); + postHogEventTracker( + "PROJECT_CREATE", + { + state: "FAILED" + }, + ) + } ); }); }; @@ -380,7 +386,7 @@ export const CreateProjectModal: FC = observer((props) => { control={control} render={({ field: { value, onChange } }) => ( member.member.id ===value)[0]} + value={workspaceMembers?.filter((member: IWorkspaceMember) => member.member.id === value)[0]} onChange={onChange} options={workspaceMembers || []} placeholder="Select Lead" diff --git a/web/components/project/form.tsx b/web/components/project/form.tsx index 267b2506c..ecd1a923c 100644 --- a/web/components/project/form.tsx +++ b/web/components/project/form.tsx @@ -16,7 +16,6 @@ import { ProjectService } from "services/project"; // hooks import useToast from "hooks/use-toast"; import { useMobxStore } from "lib/mobx/store-provider"; -import { trackEvent } from "helpers/event-tracker.helper"; export interface IProjectDetailsForm { project: IProject; @@ -29,7 +28,7 @@ const projectService = new ProjectService(); export const ProjectDetailsForm: FC = (props) => { const { project, workspaceSlug, isAdmin } = props; // store - const { project: projectStore } = useMobxStore(); + const { project: projectStore, trackEvent: { postHogEventTracker } } = useMobxStore(); // toast const { setToastAlert } = useToast(); // form data @@ -63,7 +62,10 @@ export const ProjectDetailsForm: FC = (props) => { return projectStore .updateProject(workspaceSlug.toString(), project.id, payload) .then((res) => { - trackEvent("UPDATE_PROJECT", res); + postHogEventTracker( + 'PROJECT_UPDATE', + {...res, state: "SUCCESS"} + ); setToastAlert({ type: "success", title: "Success!", @@ -71,7 +73,12 @@ export const ProjectDetailsForm: FC = (props) => { }); }) .catch((error) => { - trackEvent("UPDATE_PROJECT/FAIL"); + postHogEventTracker( + 'PROJECT_UPDATE', + { + state: "FAILED" + } + ); setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/project/leave-project-modal.tsx b/web/components/project/leave-project-modal.tsx index 503c1df74..4344dd272 100644 --- a/web/components/project/leave-project-modal.tsx +++ b/web/components/project/leave-project-modal.tsx @@ -37,6 +37,7 @@ export const LeaveProjectModal: FC = observer((props) => { // store const { user: { leaveProject }, + trackEvent: { postHogEventTracker } } = useMobxStore(); // toast const { setToastAlert } = useToast(); @@ -63,6 +64,12 @@ export const LeaveProjectModal: FC = observer((props) => { .then(() => { handleClose(); router.push(`/${workspaceSlug}/projects`); + postHogEventTracker( + "PROJECT_MEMBER_LEAVE", + { + state: "SUCCESS" + } + ); }) .catch(() => { setToastAlert({ @@ -70,6 +77,12 @@ export const LeaveProjectModal: FC = observer((props) => { title: "Error!", message: "Something went wrong please try again later.", }); + postHogEventTracker( + "PROJECT_MEMBER_LEAVE", + { + state: "FAILED" + } + ); }); } else { setToastAlert({ diff --git a/web/components/project/member-list.tsx b/web/components/project/member-list.tsx index 5a9b2f78c..9f86032da 100644 --- a/web/components/project/member-list.tsx +++ b/web/components/project/member-list.tsx @@ -19,6 +19,7 @@ export const ProjectMemberList: React.FC = observer(() => { // store const { projectMember: { projectMembers, fetchProjectMembers }, + trackEvent: { setTrackElement } } = useMobxStore(); // states @@ -56,7 +57,12 @@ export const ProjectMemberList: React.FC = observer(() => { onChange={(e) => setSearchQuery(e.target.value)} /> - diff --git a/web/components/project/send-project-invitation-modal.tsx b/web/components/project/send-project-invitation-modal.tsx index cfcc25866..5e57e55ba 100644 --- a/web/components/project/send-project-invitation-modal.tsx +++ b/web/components/project/send-project-invitation-modal.tsx @@ -58,6 +58,7 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { const { user: { currentProjectRole }, workspaceMember: { workspaceMembers }, + trackEvent: { postHogEventTracker } } = useMobxStore(); const { @@ -85,18 +86,30 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { await projectMemberService .bulkAddMembersToProject(workspaceSlug.toString(), projectId.toString(), payload) - .then(() => { + .then((res) => { onSuccess(); onClose(); - trackEvent("PROJECT_MEMBER_INVITE"); setToastAlert({ title: "Success", type: "success", message: "Member added successfully", }); + postHogEventTracker( + 'PROJECT_MEMBER_INVITE', + { + ...res, + state: "SUCCESS" + } + ); }) .catch((error) => { console.log(error); + postHogEventTracker( + 'PROJECT_MEMBER_INVITE', + { + state: "FAILED", + } + ); }) .finally(() => { reset(defaultValues); diff --git a/web/components/project/settings/features-list.tsx b/web/components/project/settings/features-list.tsx index 105c1b2c7..d562f19f7 100644 --- a/web/components/project/settings/features-list.tsx +++ b/web/components/project/settings/features-list.tsx @@ -51,8 +51,10 @@ export const ProjectFeaturesList: FC = observer(() => { const { workspaceSlug, projectId } = router.query; // store const { + workspace: { currentWorkspace }, project: { currentProjectDetails, updateProject }, user: { currentUser, currentProjectRole }, + trackEvent: { setTrackElement, postHogEventTracker }, } = useMobxStore(); const isAdmin = currentProjectRole === 20; // hooks @@ -87,6 +89,16 @@ export const ProjectFeaturesList: FC = observer(() => { { + console.log(currentProjectDetails?.[feature.property as keyof IProject]); + setTrackElement("PROJECT_SETTINGS_FEATURES_PAGE"); + postHogEventTracker(`TOGGLE_${feature.title.toUpperCase()}`, { + workspace_id: currentWorkspace?.id, + workspace_slug: currentWorkspace?.slug, + project_id: currentProjectDetails?.id, + project_name: currentProjectDetails?.name, + project_identifier: currentProjectDetails?.identifier, + enabled: !currentProjectDetails?.[feature.property as keyof IProject] + }); handleSubmit({ [feature.property]: !currentProjectDetails?.[feature.property as keyof IProject], }); diff --git a/web/components/project/sidebar-list-item.tsx b/web/components/project/sidebar-list-item.tsx index 68b2787ae..961ad49cf 100644 --- a/web/components/project/sidebar-list-item.tsx +++ b/web/components/project/sidebar-list-item.tsx @@ -75,7 +75,7 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { project, provided, snapshot, handleCopyText, shortContextMenu = false } = props; // store - const { project: projectStore, theme: themeStore } = useMobxStore(); + const { project: projectStore, theme: themeStore, trackEvent: { setTrackElement } } = useMobxStore(); // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -118,6 +118,7 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { }; const handleLeaveProject = () => { + setTrackElement("APP_SIDEBAR_PROJECT_DROPDOWN"); setLeaveProjectModal(true); }; diff --git a/web/components/project/sidebar-list.tsx b/web/components/project/sidebar-list.tsx index b32fb4f42..81d10c7b0 100644 --- a/web/components/project/sidebar-list.tsx +++ b/web/components/project/sidebar-list.tsx @@ -30,6 +30,7 @@ export const ProjectSidebarList: FC = observer(() => { theme: { sidebarCollapsed }, project: { joinedProjects, favoriteProjects, orderProjectsWithSortOrder, updateProjectView }, commandPalette: { toggleCreateProjectModal }, + trackEvent: { setTrackElement } } = useMobxStore(); // router const router = useRouter(); @@ -111,9 +112,8 @@ export const ProjectSidebarList: FC = observer(() => { )}
@@ -140,6 +140,7 @@ export const ProjectSidebarList: FC = observer(() => { - diff --git a/web/components/states/delete-state-modal.tsx b/web/components/states/delete-state-modal.tsx index e3b15bf2e..b8d58de73 100644 --- a/web/components/states/delete-state-modal.tsx +++ b/web/components/states/delete-state-modal.tsx @@ -13,7 +13,6 @@ import useToast from "hooks/use-toast"; import { Button } from "@plane/ui"; // types import type { IState } from "types"; -import { trackEvent } from "helpers/event-tracker.helper"; type Props = { isOpen: boolean; @@ -29,7 +28,7 @@ export const DeleteStateModal: React.FC = observer((props) => { const { workspaceSlug } = router.query; // store - const { projectState: projectStateStore } = useMobxStore(); + const { projectState: projectStateStore, trackEvent: { postHogEventTracker } } = useMobxStore(); // states const [isDeleteLoading, setIsDeleteLoading] = useState(false); @@ -49,9 +48,12 @@ export const DeleteStateModal: React.FC = observer((props) => { await projectStateStore .deleteState(workspaceSlug.toString(), data.project, data.id) .then((res) => { - trackEvent( + postHogEventTracker( 'STATE_DELETE', - ) + { + state: "SUCCESS" + } + ); handleClose(); }) .catch((err) => { @@ -68,6 +70,12 @@ export const DeleteStateModal: React.FC = observer((props) => { title: "Error!", message: "State could not be deleted. Please try again.", }); + postHogEventTracker( + 'STATE_DELETE', + { + state: "FAILED" + } + ) }) .finally(() => { setIsDeleteLoading(false); diff --git a/web/components/states/project-setting-state-list-item.tsx b/web/components/states/project-setting-state-list-item.tsx index b264ad4bb..051751f7f 100644 --- a/web/components/states/project-setting-state-list-item.tsx +++ b/web/components/states/project-setting-state-list-item.tsx @@ -30,6 +30,7 @@ export const ProjectSettingListItem: React.FC = observer((props) => { // store const { projectState: { markStateAsDefault, moveStatePosition }, + trackEvent: { setTrackElement } } = useMobxStore(); // states @@ -107,7 +108,12 @@ export const ProjectSettingListItem: React.FC = observer((props) => { className={`group-hover:opacity-100 opacity-0 ${ state.default || groupLength === 1 ? "cursor-not-allowed" : "" } grid place-items-center`} - onClick={handleDeleteState} + onClick={ + () => { + setTrackElement("PROJECT_SETTINGS_STATE_PAGE"); + handleDeleteState() + } + } disabled={state.default || groupLength === 1} > {state.default ? ( diff --git a/web/components/states/project-setting-state-list.tsx b/web/components/states/project-setting-state-list.tsx index b5ac492ce..05316e828 100644 --- a/web/components/states/project-setting-state-list.tsx +++ b/web/components/states/project-setting-state-list.tsx @@ -23,6 +23,7 @@ export const ProjectSettingStateList: React.FC = observer(() => { // store const { projectState: { groupedProjectStates, projectStates, fetchProjectStates }, + trackEvent: { setTrackElement } } = useMobxStore(); // state const [activeGroup, setActiveGroup] = useState(null); @@ -58,7 +59,11 @@ export const ProjectSettingStateList: React.FC = observer(() => { diff --git a/web/components/workspace/create-workspace-form.tsx b/web/components/workspace/create-workspace-form.tsx index 02238887e..737458ef5 100644 --- a/web/components/workspace/create-workspace-form.tsx +++ b/web/components/workspace/create-workspace-form.tsx @@ -51,7 +51,7 @@ export const CreateWorkspaceForm: FC = observer((props) => { const router = useRouter(); - const { workspace: workspaceStore } = useMobxStore(); + const { workspace: workspaceStore, trackEvent: { postHogEventTracker } } = useMobxStore(); const { setToastAlert } = useToast(); @@ -73,15 +73,12 @@ export const CreateWorkspaceForm: FC = observer((props) => { await workspaceStore .createWorkspace(formData) .then(async (res) => { - const payload = { - name: formData.name, - slug: formData.slug, - workspace_url: formData.url, - organization_size: formData.organization_size - }; - trackEvent( - "CREATE_WORKSPACE", - payload + postHogEventTracker( + "WORKSPACE_CREATE", + { + ...res, + state: "SUCCESS" + }, ) setToastAlert({ type: "success", @@ -92,11 +89,19 @@ export const CreateWorkspaceForm: FC = observer((props) => { if (onSubmit) await onSubmit(res); }) .catch(() => + { setToastAlert({ type: "error", title: "Error!", message: "Workspace could not be created. Please try again.", }) + postHogEventTracker( + "WORKSPACE_CREATE", + { + state: "FAILED" + }, + ) + } ); } else setSlugError(true); }) @@ -106,6 +111,12 @@ export const CreateWorkspaceForm: FC = observer((props) => { title: "Error!", message: "Some error occurred while creating workspace. Please try again.", }); + postHogEventTracker( + "WORKSPACE_CREATE", + { + state: "FAILED" + }, + ) }); }; diff --git a/web/components/workspace/delete-workspace-modal.tsx b/web/components/workspace/delete-workspace-modal.tsx index 93b3b7351..79f32ec59 100644 --- a/web/components/workspace/delete-workspace-modal.tsx +++ b/web/components/workspace/delete-workspace-modal.tsx @@ -12,7 +12,6 @@ import useToast from "hooks/use-toast"; import { Button, Input } from "@plane/ui"; // types import type { IWorkspace } from "types"; -import { trackEvent } from "helpers/event-tracker.helper"; type Props = { isOpen: boolean; @@ -30,7 +29,7 @@ export const DeleteWorkspaceModal: React.FC = observer((props) => { const router = useRouter(); - const { workspace: workspaceStore } = useMobxStore(); + const { workspace: workspaceStore, trackEvent: { postHogEventTracker } } = useMobxStore(); const { setToastAlert } = useToast(); @@ -60,14 +59,16 @@ export const DeleteWorkspaceModal: React.FC = observer((props) => { .deleteWorkspace(data.slug) .then((res) => { handleClose(); - console.log('DELETE WORKPSACE', res); router.push("/"); const payload = { slug: data.slug }; - trackEvent( - 'DELETE_WORKSPACE', - payload + postHogEventTracker( + 'WORKSPACE_DELETE', + { + res, + state: "SUCCESS" + } ); setToastAlert({ type: "success", @@ -76,11 +77,19 @@ export const DeleteWorkspaceModal: React.FC = observer((props) => { }); }) .catch(() => + { setToastAlert({ type: "error", title: "Error!", message: "Something went wrong. Please try again later.", }) + postHogEventTracker( + 'WORKSPACE_DELETE', + { + state: "FAILED" + } + ); + } ); }; diff --git a/web/components/workspace/settings/workspace-details.tsx b/web/components/workspace/settings/workspace-details.tsx index d9650fa25..318cc9fe0 100644 --- a/web/components/workspace/settings/workspace-details.tsx +++ b/web/components/workspace/settings/workspace-details.tsx @@ -40,6 +40,7 @@ export const WorkspaceDetails: FC = observer(() => { const { workspace: { currentWorkspace, updateWorkspace }, user: { currentWorkspaceRole }, + trackEvent: { postHogEventTracker } } = useMobxStore(); const isAdmin = currentWorkspaceRole === 20; // hooks @@ -66,14 +67,28 @@ export const WorkspaceDetails: FC = observer(() => { await updateWorkspace(currentWorkspace.slug, payload) .then((res) => { - trackEvent("UPDATE_WORKSPACE", res); + postHogEventTracker( + 'WORKSPACE_UPDATE', + { + ...res, + state: "SUCCESS" + } + ) setToastAlert({ title: "Success", type: "success", message: "Workspace updated successfully", }); - }) - .catch((err) => console.error(err)); + }).catch((err) => { + postHogEventTracker( + 'WORKSPACE_UPDATE', + { + state: "FAILED" + } + ); + console.error(err) + } + ); }; const handleRemoveLogo = () => { @@ -262,10 +277,9 @@ export const WorkspaceDetails: FC = observer(() => { id="url" name="url" type="url" - value={`${ - typeof window !== "undefined" && + value={`${typeof window !== "undefined" && window.location.origin.replace("http://", "").replace("https://", "") - }/${currentWorkspace.slug}`} + }/${currentWorkspace.slug}`} onChange={onChange} ref={ref} hasError={Boolean(errors.url)} diff --git a/web/components/workspace/sidebar-dropdown.tsx b/web/components/workspace/sidebar-dropdown.tsx index c94589401..316e92935 100644 --- a/web/components/workspace/sidebar-dropdown.tsx +++ b/web/components/workspace/sidebar-dropdown.tsx @@ -56,6 +56,7 @@ export const WorkspaceSidebarDropdown = observer(() => { theme: { sidebarCollapsed }, workspace: { workspaces, currentWorkspace: activeWorkspace }, user: { currentUser, updateCurrentUser, isUserInstanceAdmin }, + trackEvent: { setTrackElement } } = useMobxStore(); // hooks const { setToastAlert } = useToast(); @@ -203,6 +204,7 @@ export const WorkspaceSidebarDropdown = observer(() => { as="button" type="button" onClick={() => { + setTrackElement("APP_SIEDEBAR_WORKSPACE_DROPDOWN"); router.push("/create-workspace"); }} className="flex w-full items-center gap-2 px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80" diff --git a/web/components/workspace/sidebar-quick-action.tsx b/web/components/workspace/sidebar-quick-action.tsx index 9ae9ccde8..1f16ad4c5 100644 --- a/web/components/workspace/sidebar-quick-action.tsx +++ b/web/components/workspace/sidebar-quick-action.tsx @@ -14,7 +14,7 @@ export const WorkspaceSidebarQuickAction = observer(() => { // states const [isDraftIssueModalOpen, setIsDraftIssueModalOpen] = useState(false); - const { theme: themeStore, commandPalette: commandPaletteStore } = useMobxStore(); + const { theme: themeStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore(); const { storedValue, clearValue } = useLocalStorage("draftedIssue", JSON.stringify({})); @@ -34,23 +34,24 @@ export const WorkspaceSidebarQuickAction = observer(() => { />
-
diff --git a/web/store/event-tracker.store.ts b/web/store/event-tracker.store.ts new file mode 100644 index 000000000..280701d37 --- /dev/null +++ b/web/store/event-tracker.store.ts @@ -0,0 +1,80 @@ +import { action, makeObservable, observable } from "mobx"; +import posthog from "posthog-js"; +import { RootStore } from "./root"; + +export interface ITrackEventStore { + trackElement: string; + setTrackElement: (element: string) => void; + postHogEventTracker: ( + eventName: string, + payload: object | [] | null + // group: { isGrouping: boolean; groupType: string; gorupId: string } | null + ) => void; +} + +export class TrackEventStore implements ITrackEventStore { + trackElement: string = ""; + rootStore; + constructor(_rootStore: RootStore) { + makeObservable(this, { + trackElement: observable, + setTrackElement: action, + postHogEventTracker: action, + }); + this.rootStore = _rootStore; + } + + setTrackElement = (element: string) => { + this.trackElement = element; + }; + + postHogEventTracker = ( + eventName: string, + payload: object | [] | null + // group: { isGrouping: boolean; groupType: string; gorupId: string } | null + ) => { + try { + console.log("POSTHOG_EVENT: ", eventName); + let extras: any = { + workspace_name: this.rootStore.workspace.currentWorkspace?.name ?? "", + workspace_id: this.rootStore.workspace.currentWorkspace?.id ?? "", + workspace_slug: this.rootStore.workspace.currentWorkspace?.slug ?? "", + project_name: this.rootStore.project.currentProjectDetails?.name ?? "", + project_id: this.rootStore.project.currentProjectDetails?.id ?? "", + project_identifier: this.rootStore.project.currentProjectDetails?.identifier ?? "", + }; + if (["PROJECT_CREATE", "PROJECT_UPDATE"].includes(eventName)) { + const project_details: any = payload as object; + extras = { + ...extras, + project_name: project_details?.name ?? "", + project_id: project_details?.id ?? "", + project_identifier: project_details?.identifier ?? "", + }; + } + + // if (group!.isGrouping === true) { + // posthog?.group(group!.groupType, group!.gorupId, { + // name: "PostHog", + // subscription: "subscription", + // date_joined: "2020-01-23T00:00:00.000Z", + // }); + // console.log("END OF GROUPING"); + // posthog?.capture(eventName, { + // ...payload, + // element: this.trackElement ?? "", + // }); + // } else { + posthog?.capture(eventName, { + ...payload, + extras: extras, + element: this.trackElement ?? "", + }); + // } + console.log(payload); + } catch (error) { + console.log(error); + } + this.setTrackElement(""); + }; +} diff --git a/web/store/root.ts b/web/store/root.ts index fd2aaf2aa..2c2adb69f 100644 --- a/web/store/root.ts +++ b/web/store/root.ts @@ -160,6 +160,8 @@ import { ModuleIssueFiltersStore, IModuleIssueFiltersStore } from "store/module- import { IMentionsStore, MentionsStore } from "store/editor"; // pages import { PageStore, IPageStore } from "store/page.store"; +// event tracking +import { TrackEventStore, ITrackEventStore } from "./event-tracker.store"; enableStaticRendering(typeof window === "undefined"); @@ -261,6 +263,8 @@ export class RootStore { page: IPageStore; + trackEvent: ITrackEventStore; + constructor() { this.instance = new InstanceStore(this); @@ -357,5 +361,7 @@ export class RootStore { this.moduleIssueFilters = new ModuleIssueFiltersStore(this); this.page = new PageStore(this); + + this.trackEvent = new TrackEventStore(this); } }