From 7d07afd59c1ccf1447c907ccb47de236610db2ad Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:34:53 +0530 Subject: [PATCH 1/2] chore: cycle and module sidebar analytics improvement (#3559) * chore: cycle and module store update action updated * chore: cycle and module issue store actions updated * chore: cycle and module retrieve endpoints updated * fix: app sidebar z index and priority icon fix * chore: cycle and module sidebar and stats updated --- apiserver/plane/app/views/cycle.py | 24 +++++++-------- apiserver/plane/app/views/module.py | 12 ++++---- packages/ui/src/icons/priority-icon.tsx | 1 - .../core/sidebar/progress-chart.tsx | 19 +++++++----- web/components/cycles/sidebar.tsx | 30 ++++++++----------- web/components/modules/sidebar.tsx | 16 ++++------ web/layouts/app-layout/sidebar.tsx | 22 ++++++-------- web/store/cycle.store.ts | 1 + web/store/issue/cycle/issue.store.ts | 6 ++++ web/store/issue/module/issue.store.ts | 6 ++++ web/store/module.store.ts | 1 + 11 files changed, 70 insertions(+), 68 deletions(-) diff --git a/apiserver/plane/app/views/cycle.py b/apiserver/plane/app/views/cycle.py index 23a227fef..32f593e1e 100644 --- a/apiserver/plane/app/views/cycle.py +++ b/apiserver/plane/app/views/cycle.py @@ -242,13 +242,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .values("display_name", "assignee_id", "avatar") .annotate( total_issues=Count( - "assignee_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ), ) .annotate( completed_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -258,7 +258,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -281,13 +281,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ) ) .annotate( completed_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -297,7 +297,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -419,13 +419,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( total_issues=Count( - "assignee_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ), ) .annotate( completed_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -435,7 +435,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -459,13 +459,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ), ) .annotate( completed_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -475,7 +475,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, diff --git a/apiserver/plane/app/views/module.py b/apiserver/plane/app/views/module.py index 1f055129a..fafcfed4b 100644 --- a/apiserver/plane/app/views/module.py +++ b/apiserver/plane/app/views/module.py @@ -197,7 +197,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( total_issues=Count( - "assignee_id", + "id", filter=Q( archived_at__isnull=True, is_draft=False, @@ -206,7 +206,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( completed_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -216,7 +216,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -239,7 +239,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", + "id", filter=Q( archived_at__isnull=True, is_draft=False, @@ -248,7 +248,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( completed_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -258,7 +258,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, diff --git a/packages/ui/src/icons/priority-icon.tsx b/packages/ui/src/icons/priority-icon.tsx index caa821305..0b98b3e6b 100644 --- a/packages/ui/src/icons/priority-icon.tsx +++ b/packages/ui/src/icons/priority-icon.tsx @@ -47,7 +47,6 @@ export const PriorityIcon: React.FC = (props) => { > = ({ distribution, startDate, endDate, tota { id: "pending", color: "#3F76FF", - data: chartData.map((item, index) => ({ - index, - x: item.currentDate, - y: item.pending, - color: "#3F76FF", - })), + data: + chartData.length > 0 + ? chartData.map((item, index) => ({ + index, + x: item.currentDate, + y: item.pending, + color: "#3F76FF", + })) + : [], enableArea: true, }, { @@ -121,7 +124,9 @@ const ProgressChart: React.FC = ({ distribution, startDate, endDate, tota enableArea colors={(datum) => datum.color ?? "#3F76FF"} customYAxisTickValues={[0, totalIssues]} - gridXValues={chartData.map((item, index) => (index % 2 === 0 ? item.currentDate : ""))} + gridXValues={ + chartData.length > 0 ? chartData.map((item, index) => (index % 2 === 0 ? item.currentDate : "")) : undefined + } enableSlices="x" sliceTooltip={(datum) => (
diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 4bf76f91f..5fa1d2459 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -319,13 +319,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus); const issueCount = - 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 === 0 ? "0 Issue" : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; @@ -575,7 +569,9 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
- {isStartValid && isEndValid ? ( + {cycleDetails.distribution?.completion_chart && + cycleDetails.start_date && + cycleDetails.end_date ? (
@@ -589,16 +585,14 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
- {cycleDetails && cycleDetails.distribution && ( -
- -
- )} +
+ +
) : ( "" diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index f43326b6b..8b1aa28e8 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -262,13 +262,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); const issueCount = - moduleDetails.total_issues === 0 - ? "0 Issue" - : moduleDetails.total_issues === moduleDetails.completed_issues - ? moduleDetails.total_issues > 1 - ? `${moduleDetails.total_issues}` - : `${moduleDetails.total_issues}` - : `${moduleDetails.completed_issues}/${moduleDetails.total_issues}`; + moduleDetails.total_issues === 0 ? "0 Issue" : `${moduleDetails.completed_issues}/${moduleDetails.total_issues}`; const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; @@ -582,7 +576,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
- {isStartValid && isEndValid ? ( + {moduleDetails.start_date && moduleDetails.target_date ? (
@@ -598,9 +592,9 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
diff --git a/web/layouts/app-layout/sidebar.tsx b/web/layouts/app-layout/sidebar.tsx index d9ce3c3f0..e211e7884 100644 --- a/web/layouts/app-layout/sidebar.tsx +++ b/web/layouts/app-layout/sidebar.tsx @@ -12,7 +12,7 @@ import { ProjectSidebarList } from "components/project"; import { useApplication } from "hooks/store"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; -export interface IAppSidebar { } +export interface IAppSidebar {} export const AppSidebar: FC = observer(() => { // store hooks @@ -34,24 +34,23 @@ export const AppSidebar: FC = observer(() => { } }; handleResize(); - window.addEventListener('resize', handleResize); + window.addEventListener("resize", handleResize); return () => { - window.removeEventListener('resize', handleResize); + window.removeEventListener("resize", handleResize); }; }, [themStore]); return (
-
+ md:ml-0 ${themStore.sidebarCollapsed ? "w-[80px]" : "w-[280px]"} + lg:ml-0 ${themStore.sidebarCollapsed ? "w-[80px]" : "w-[280px]"} + `} + > +
@@ -61,6 +60,3 @@ export const AppSidebar: FC = observer(() => {
); }); - - - diff --git a/web/store/cycle.store.ts b/web/store/cycle.store.ts index ed5077385..bb6824c08 100644 --- a/web/store/cycle.store.ts +++ b/web/store/cycle.store.ts @@ -304,6 +304,7 @@ export class CycleStore implements ICycleStore { set(this.cycleMap, [cycleId], { ...this.cycleMap?.[cycleId], ...data }); }); const response = await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, data); + this.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; } catch (error) { console.log("Failed to patch cycle from cycle store"); diff --git a/web/store/issue/cycle/issue.store.ts b/web/store/issue/cycle/issue.store.ts index 286222e4a..71618d51c 100644 --- a/web/store/issue/cycle/issue.store.ts +++ b/web/store/issue/cycle/issue.store.ts @@ -152,6 +152,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { const params = this.rootIssueStore?.cycleIssuesFilter?.appliedFilters; const response = await this.cycleService.getCycleIssuesWithParams(workspaceSlug, projectId, cycleId, params); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); runInAction(() => { set( @@ -182,6 +183,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id]); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; } catch (error) { @@ -218,6 +220,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { if (!cycleId) throw new Error("Cycle Id is required"); const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); const issueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === issueId); if (issueIndex >= 0) @@ -246,6 +249,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { }); const response = await this.createIssue(workspaceSlug, projectId, data, cycleId); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); const quickAddIssueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === data.id); if (quickAddIssueIndex >= 0) @@ -273,6 +277,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { issueIds.forEach((issueId) => { this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId }); }); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return issueToCycle; } catch (error) { @@ -289,6 +294,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { this.rootStore.issues.updateIssue(issueId, { cycle_id: null }); const response = await this.issueService.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; } catch (error) { diff --git a/web/store/issue/module/issue.store.ts b/web/store/issue/module/issue.store.ts index e32b97df0..c9ed459a1 100644 --- a/web/store/issue/module/issue.store.ts +++ b/web/store/issue/module/issue.store.ts @@ -156,6 +156,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { const params = this.rootIssueStore?.moduleIssuesFilter?.appliedFilters; const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { set( @@ -187,6 +188,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id]); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { @@ -223,6 +225,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { if (!moduleId) throw new Error("Module Id is required"); const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); const issueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === issueId); if (issueIndex >= 0) @@ -251,6 +254,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { }); const response = await this.createIssue(workspaceSlug, projectId, data, moduleId); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); const quickAddIssueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === data.id); if (quickAddIssueIndex >= 0) @@ -284,6 +288,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { else return uniq(concat(issueModuleIds, [moduleId])); }); }); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return issueToModule; } catch (error) { @@ -314,6 +319,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { moduleId, issueIds ); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { diff --git a/web/store/module.store.ts b/web/store/module.store.ts index f0f576cbc..7ebccc23c 100644 --- a/web/store/module.store.ts +++ b/web/store/module.store.ts @@ -196,6 +196,7 @@ export class ModulesStore implements IModuleStore { set(this.moduleMap, [moduleId], { ...originalModuleDetails, ...data }); }); const response = await this.moduleService.patchModule(workspaceSlug, projectId, moduleId, data); + this.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { console.error("Failed to update module in module store", error); From 0165abab3e10c88e824b32a7f221ad5b63bb3210 Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:19:07 +0530 Subject: [PATCH 2/2] chore: posthog events improved (#3554) * chore: events naming convention changed * chore: track element added for project related events * chore: track element added for cycle related events * chore: track element added for module related events * chore: issue related events updated * refactor: event tracker store * refactor: event-tracker store * fix: posthog changes --------- Co-authored-by: sriram veeraghanta --- .../actions/project-actions.tsx | 9 +- .../command-palette/command-modal.tsx | 8 +- .../command-palette/command-palette.tsx | 7 +- web/components/cycles/cycles-board-card.tsx | 9 +- web/components/cycles/cycles-list-item.tsx | 9 +- web/components/cycles/delete-modal.tsx | 16 +- web/components/cycles/modal.tsx | 17 +- web/components/cycles/sidebar.tsx | 6 +- .../dashboard/project-empty-state.tsx | 11 +- .../dashboard/widgets/recent-projects.tsx | 4 +- web/components/headers/cycle-issues.tsx | 5 +- web/components/headers/cycles.tsx | 6 +- web/components/headers/module-issues.tsx | 5 +- web/components/headers/modules-list.tsx | 8 +- web/components/headers/project-issues.tsx | 15 +- .../headers/project-view-issues.tsx | 3 +- web/components/headers/projects.tsx | 10 +- web/components/inbox/inbox-issue-actions.tsx | 36 ++-- .../inbox/modals/create-issue-modal.tsx | 35 ++-- .../integration/jira/give-details.tsx | 10 +- web/components/issues/attachment/root.tsx | 33 +++- .../issues/issue-detail/inbox/root.tsx | 36 +++- web/components/issues/issue-detail/root.tsx | 116 ++++++++++++- .../calendar/quick-add-issue-form.tsx | 16 +- .../issue-layouts/empty-states/cycle.tsx | 6 +- .../empty-states/global-view.tsx | 8 +- .../issue-layouts/empty-states/module.tsx | 6 +- .../empty-states/project-issues.tsx | 10 +- .../empty-states/project-view.tsx | 10 +- .../gantt/quick-add-issue-form.tsx | 16 +- .../issue-layouts/kanban/base-kanban-root.tsx | 8 +- .../issues/issue-layouts/kanban/block.tsx | 5 +- .../kanban/headers/group-by-card.tsx | 26 ++- .../kanban/quick-add-issue-form.tsx | 16 +- .../issues/issue-layouts/list/block.tsx | 7 +- .../issues/issue-layouts/list/blocks-list.tsx | 2 +- .../list/headers/group-by-card.tsx | 23 ++- .../list/quick-add-issue-form.tsx | 18 +- .../properties/all-properties.tsx | 98 +++++++++-- .../quick-action-dropdowns/all-issue.tsx | 6 + .../quick-action-dropdowns/archived-issue.tsx | 14 +- .../quick-action-dropdowns/cycle-issue.tsx | 9 + .../quick-action-dropdowns/module-issue.tsx | 9 + .../quick-action-dropdowns/project-issue.tsx | 9 +- .../roots/all-issue-layout-root.tsx | 13 +- .../spreadsheet/columns/assignee-column.tsx | 13 +- .../spreadsheet/columns/due-date-column.tsx | 14 +- .../spreadsheet/columns/estimate-column.tsx | 6 +- .../spreadsheet/columns/label-column.tsx | 6 +- .../spreadsheet/columns/priority-column.tsx | 4 +- .../spreadsheet/columns/start-date-column.tsx | 14 +- .../spreadsheet/columns/state-column.tsx | 4 +- .../issue-layouts/spreadsheet/issue-row.tsx | 18 +- .../spreadsheet/quick-add-issue-form.tsx | 21 ++- .../issues/issue-modal/draft-issue-layout.tsx | 22 ++- web/components/issues/issue-modal/modal.tsx | 72 ++++---- web/components/issues/peek-overview/root.tsx | 113 ++++++++++++- web/components/issues/sub-issues/root.tsx | 66 +++++++- .../modules/delete-module-modal.tsx | 16 +- web/components/modules/modal.tsx | 29 ++-- web/components/modules/module-card-item.tsx | 5 +- web/components/modules/module-list-item.tsx | 5 +- web/components/modules/modules-list-view.tsx | 8 +- web/components/modules/sidebar.tsx | 11 +- web/components/onboarding/invitations.tsx | 11 +- web/components/onboarding/tour/root.tsx | 10 +- .../page-views/workspace-dashboard.tsx | 15 +- web/components/project/card-list.tsx | 10 +- .../project/create-project-modal.tsx | 31 ++-- .../project/delete-project-modal.tsx | 34 ++-- web/components/project/form.tsx | 34 ++-- .../project/leave-project-modal.tsx | 10 +- web/components/project/member-list.tsx | 6 +- .../project/send-project-invitation-modal.tsx | 14 +- .../project/settings/features-list.tsx | 8 +- web/components/project/sidebar-list-item.tsx | 45 ++--- web/components/project/sidebar-list.tsx | 7 +- .../states/create-update-state-inline.tsx | 14 +- web/components/states/delete-state-modal.tsx | 10 +- .../project-setting-state-list-item.tsx | 6 +- .../states/project-setting-state-list.tsx | 6 +- .../workspace/create-workspace-form.tsx | 12 +- .../workspace/delete-workspace-modal.tsx | 10 +- .../workspace/settings/workspace-details.tsx | 10 +- web/components/workspace/sidebar-dropdown.tsx | 31 ++-- .../workspace/sidebar-quick-action.tsx | 9 +- web/constants/event-tracker.ts | 105 ++++++++++++ web/constants/spreadsheet.ts | 6 +- web/hooks/store/index.ts | 1 + web/hooks/store/use-event-tracker.ts | 11 ++ web/layouts/auth-layout/project-wrapper.tsx | 7 +- web/lib/app-provider.tsx | 6 +- ...sthog-wrapper.tsx => posthog-provider.tsx} | 12 +- web/lib/redirect.ts | 16 -- web/pages/[workspaceSlug]/analytics.tsx | 6 +- .../projects/[projectId]/cycles/index.tsx | 4 +- .../[workspaceSlug]/settings/members.tsx | 10 +- web/pages/invitations/index.tsx | 8 +- web/pages/onboarding/index.tsx | 8 +- web/store/application/event-tracker.store.ts | 79 --------- web/store/application/index.ts | 5 +- web/store/event-tracker.store.ts | 155 ++++++++++++++++++ web/store/root.store.ts | 3 + 103 files changed, 1385 insertions(+), 596 deletions(-) create mode 100644 web/constants/event-tracker.ts create mode 100644 web/hooks/store/use-event-tracker.ts rename web/lib/{wrappers/posthog-wrapper.tsx => posthog-provider.tsx} (82%) delete mode 100644 web/lib/redirect.ts delete mode 100644 web/store/application/event-tracker.store.ts create mode 100644 web/store/event-tracker.store.ts diff --git a/web/components/command-palette/actions/project-actions.tsx b/web/components/command-palette/actions/project-actions.tsx index 44b5e6111..bdd08a0d8 100644 --- a/web/components/command-palette/actions/project-actions.tsx +++ b/web/components/command-palette/actions/project-actions.tsx @@ -1,7 +1,7 @@ import { Command } from "cmdk"; import { ContrastIcon, FileText } from "lucide-react"; // hooks -import { useApplication } from "hooks/store"; +import { useApplication, useEventTracker } from "hooks/store"; // ui import { DiceIcon, PhotoFilterIcon } from "@plane/ui"; @@ -14,8 +14,8 @@ export const CommandPaletteProjectActions: React.FC = (props) => { const { commandPalette: { toggleCreateCycleModal, toggleCreateModuleModal, toggleCreatePageModal, toggleCreateViewModal }, - eventTracker: { setTrackElement }, } = useApplication(); + const { setTrackElement } = useEventTracker(); return ( <> @@ -23,7 +23,7 @@ export const CommandPaletteProjectActions: React.FC = (props) => { { closePalette(); - setTrackElement("COMMAND_PALETTE"); + setTrackElement("Command palette"); toggleCreateCycleModal(true); }} className="focus:outline-none" @@ -39,6 +39,7 @@ export const CommandPaletteProjectActions: React.FC = (props) => { { closePalette(); + setTrackElement("Command palette"); toggleCreateModuleModal(true); }} className="focus:outline-none" @@ -54,6 +55,7 @@ export const CommandPaletteProjectActions: React.FC = (props) => { { closePalette(); + setTrackElement("Command palette"); toggleCreateViewModal(true); }} className="focus:outline-none" @@ -69,6 +71,7 @@ export const CommandPaletteProjectActions: React.FC = (props) => { { closePalette(); + setTrackElement("Command palette"); toggleCreatePageModal(true); }} className="focus:outline-none" diff --git a/web/components/command-palette/command-modal.tsx b/web/components/command-palette/command-modal.tsx index 342827825..dbf349f9d 100644 --- a/web/components/command-palette/command-modal.tsx +++ b/web/components/command-palette/command-modal.tsx @@ -6,7 +6,7 @@ import { Dialog, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; import { FolderPlus, Search, Settings } from "lucide-react"; // hooks -import { useApplication, useProject } from "hooks/store"; +import { useApplication, useEventTracker, useProject } from "hooks/store"; // services import { WorkspaceService } from "services/workspace.service"; import { IssueService } from "services/issue"; @@ -64,8 +64,8 @@ export const CommandModal: React.FC = observer(() => { toggleCreateIssueModal, toggleCreateProjectModal, }, - eventTracker: { setTrackElement }, } = useApplication(); + const { setTrackElement } = useEventTracker(); // router const router = useRouter(); @@ -278,7 +278,7 @@ export const CommandModal: React.FC = observer(() => { { closePalette(); - setTrackElement("COMMAND_PALETTE"); + setTrackElement("Command Palette"); toggleCreateIssueModal(true); }} className="focus:bg-custom-background-80" @@ -296,7 +296,7 @@ export const CommandModal: React.FC = observer(() => { { closePalette(); - setTrackElement("COMMAND_PALETTE"); + setTrackElement("Command palette"); toggleCreateProjectModal(true); }} className="focus:outline-none" diff --git a/web/components/command-palette/command-palette.tsx b/web/components/command-palette/command-palette.tsx index 213c35f8e..e6349e0b4 100644 --- a/web/components/command-palette/command-palette.tsx +++ b/web/components/command-palette/command-palette.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; // hooks -import { useApplication, useIssues, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useIssues, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { CommandModal, ShortcutsModal } from "components/command-palette"; @@ -32,8 +32,8 @@ export const CommandPalette: FC = observer(() => { const { commandPalette, theme: { toggleSidebar }, - eventTracker: { setTrackElement }, } = useApplication(); + const { setTrackElement } = useEventTracker(); const { currentUser } = useUser(); const { issues: { removeIssue }, @@ -118,11 +118,10 @@ export const CommandPalette: FC = observer(() => { toggleSidebar(); } } else if (!isAnyModalOpen) { + setTrackElement("Shortcut key"); 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 d2279aeb4..8da2be9ec 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -2,7 +2,7 @@ import { FC, MouseEvent, useState } from "react"; import { useRouter } from "next/router"; import Link from "next/link"; // hooks -import { useApplication, useCycle, useUser } from "hooks/store"; +import { useEventTracker, useCycle, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles"; @@ -33,9 +33,7 @@ export const CyclesBoardCard: FC = (props) => { // router const router = useRouter(); // store - const { - eventTracker: { setTrackElement }, - } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); @@ -117,14 +115,15 @@ export const CyclesBoardCard: FC = (props) => { const handleEditCycle = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); + setTrackElement("Cycles page board layout"); setUpdateModal(true); }; const handleDeleteCycle = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); + setTrackElement("Cycles page board layout"); setDeleteModal(true); - setTrackElement("CYCLE_PAGE_BOARD_LAYOUT"); }; const openCycleOverview = (e: MouseEvent) => { diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index e34f4b30b..86de8d0c0 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -2,7 +2,7 @@ import { FC, MouseEvent, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; // hooks -import { useApplication, useCycle, useUser } from "hooks/store"; +import { useEventTracker, useCycle, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles"; @@ -37,9 +37,7 @@ export const CyclesListItem: FC = (props) => { // router const router = useRouter(); // store hooks - const { - eventTracker: { setTrackElement }, - } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); @@ -90,14 +88,15 @@ export const CyclesListItem: FC = (props) => { const handleEditCycle = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); + setTrackElement("Cycles page list layout"); setUpdateModal(true); }; const handleDeleteCycle = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); + setTrackElement("Cycles page list layout"); setDeleteModal(true); - setTrackElement("CYCLE_PAGE_LIST_LAYOUT"); }; const openCycleOverview = (e: MouseEvent) => { diff --git a/web/components/cycles/delete-modal.tsx b/web/components/cycles/delete-modal.tsx index 44da175b4..32e067833 100644 --- a/web/components/cycles/delete-modal.tsx +++ b/web/components/cycles/delete-modal.tsx @@ -4,7 +4,7 @@ import { Dialog, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; import { AlertTriangle } from "lucide-react"; // hooks -import { useApplication, useCycle } from "hooks/store"; +import { useEventTracker, useCycle } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { Button } from "@plane/ui"; @@ -27,9 +27,7 @@ export const CycleDeleteModal: React.FC = observer((props) => { const router = useRouter(); const { cycleId, peekCycle } = router.query; // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureCycleEvent } = useEventTracker(); const { deleteCycle } = useCycle(); // toast alert const { setToastAlert } = useToast(); @@ -46,13 +44,15 @@ export const CycleDeleteModal: React.FC = observer((props) => { title: "Success!", message: "Cycle deleted successfully.", }); - postHogEventTracker("CYCLE_DELETE", { - state: "SUCCESS", + captureCycleEvent({ + eventName: "Cycle deleted", + payload: { ...cycle, state: "SUCCESS" }, }); }) .catch(() => { - postHogEventTracker("CYCLE_DELETE", { - state: "FAILED", + captureCycleEvent({ + eventName: "Cycle deleted", + payload: { ...cycle, state: "FAILED" }, }); }); diff --git a/web/components/cycles/modal.tsx b/web/components/cycles/modal.tsx index 8144feef7..7e17e55f1 100644 --- a/web/components/cycles/modal.tsx +++ b/web/components/cycles/modal.tsx @@ -3,7 +3,7 @@ import { Dialog, Transition } from "@headlessui/react"; // services import { CycleService } from "services/cycle.service"; // hooks -import { useApplication, useCycle, useProject } from "hooks/store"; +import { useEventTracker, useCycle, useProject } from "hooks/store"; import useToast from "hooks/use-toast"; import useLocalStorage from "hooks/use-local-storage"; // components @@ -27,9 +27,7 @@ export const CycleCreateUpdateModal: React.FC = (props) => { // states const [activeProject, setActiveProject] = useState(null); // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureCycleEvent } = useEventTracker(); const { workspaceProjectIds } = useProject(); const { createCycle, updateCycleDetails } = useCycle(); // toast alert @@ -48,9 +46,9 @@ export const CycleCreateUpdateModal: React.FC = (props) => { title: "Success!", message: "Cycle created successfully.", }); - postHogEventTracker("CYCLE_CREATE", { - ...res, - state: "SUCCESS", + captureCycleEvent({ + eventName: "Cycle created", + payload: { ...res, state: "SUCCESS" }, }); }) .catch((err) => { @@ -59,8 +57,9 @@ export const CycleCreateUpdateModal: React.FC = (props) => { title: "Error!", message: err.detail ?? "Error in creating cycle. Please try again.", }); - postHogEventTracker("CYCLE_CREATE", { - state: "FAILED", + captureCycleEvent({ + eventName: "Cycle created", + payload: { ...payload, state: "FAILED" }, }); }); }; diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 5fa1d2459..c61679304 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -6,7 +6,7 @@ import { Disclosure, Popover, Transition } from "@headlessui/react"; // services import { CycleService } from "services/cycle.service"; // hooks -import { useApplication, useCycle, useMember, useUser } from "hooks/store"; +import { useEventTracker, useCycle, useUser, useMember } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { SidebarProgressStats } from "components/core"; @@ -66,9 +66,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const router = useRouter(); const { workspaceSlug, projectId, peekCycle } = router.query; // store hooks - const { - eventTracker: { setTrackElement }, - } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); diff --git a/web/components/dashboard/project-empty-state.tsx b/web/components/dashboard/project-empty-state.tsx index c0ac90f34..bb7f82f34 100644 --- a/web/components/dashboard/project-empty-state.tsx +++ b/web/components/dashboard/project-empty-state.tsx @@ -1,7 +1,7 @@ import Image from "next/image"; import { observer } from "mobx-react-lite"; // hooks -import { useApplication, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useUser } from "hooks/store"; // ui import { Button } from "@plane/ui"; // assets @@ -14,6 +14,7 @@ export const DashboardProjectEmptyState = observer(() => { const { commandPalette: { toggleCreateProjectModal }, } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentWorkspaceRole }, } = useUser(); @@ -31,7 +32,13 @@ export const DashboardProjectEmptyState = observer(() => { Project empty state {canCreateProject && (
-
diff --git a/web/components/dashboard/widgets/recent-projects.tsx b/web/components/dashboard/widgets/recent-projects.tsx index aae8ff54b..eb7a5e4e5 100644 --- a/web/components/dashboard/widgets/recent-projects.tsx +++ b/web/components/dashboard/widgets/recent-projects.tsx @@ -3,7 +3,7 @@ import Link from "next/link"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; // hooks -import { useApplication, useDashboard, useProject, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useDashboard, useProject, useUser } from "hooks/store"; // components import { WidgetLoader, WidgetProps } from "components/dashboard/widgets"; // ui @@ -72,6 +72,7 @@ export const RecentProjectsWidget: React.FC = observer((props) => { const { commandPalette: { toggleCreateProjectModal }, } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentWorkspaceRole }, } = useUser(); @@ -105,6 +106,7 @@ export const RecentProjectsWidget: React.FC = observer((props) => { onClick={(e) => { e.preventDefault(); e.stopPropagation(); + setTrackElement("Sidebar"); toggleCreateProjectModal(true); }} > diff --git a/web/components/headers/cycle-issues.tsx b/web/components/headers/cycle-issues.tsx index 8e2ceab10..7cfc492f4 100644 --- a/web/components/headers/cycle-issues.tsx +++ b/web/components/headers/cycle-issues.tsx @@ -5,6 +5,7 @@ import Link from "next/link"; // hooks import { useApplication, + useEventTracker, useCycle, useLabel, useMember, @@ -70,8 +71,8 @@ export const CycleIssuesHeader: React.FC = observer(() => { const { currentProjectCycleIds, getCycleById } = useCycle(); const { commandPalette: { toggleCreateIssueModal }, - eventTracker: { setTrackElement }, } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); @@ -238,7 +239,7 @@ export const CycleIssuesHeader: React.FC = observer(() => { diff --git a/web/components/headers/project-issues.tsx b/web/components/headers/project-issues.tsx index 29ecb16c7..769d6c945 100644 --- a/web/components/headers/project-issues.tsx +++ b/web/components/headers/project-issues.tsx @@ -4,7 +4,16 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { ArrowLeft, Briefcase, Circle, ExternalLink, Plus } from "lucide-react"; // hooks -import { useApplication, useLabel, useProject, useProjectState, useUser, useInbox, useMember } from "hooks/store"; +import { + useApplication, + useEventTracker, + useLabel, + useProject, + useProjectState, + useUser, + useInbox, + useMember, +} from "hooks/store"; // components import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues"; import { ProjectAnalyticsModal } from "components/analytics"; @@ -36,8 +45,8 @@ export const ProjectIssuesHeader: React.FC = observer(() => { } = useIssues(EIssuesStoreType.PROJECT); const { commandPalette: { toggleCreateIssueModal }, - eventTracker: { setTrackElement }, } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); @@ -221,7 +230,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => { {isEditingAllowed && ( - setModuleDeleteModal(true)}> + { + setTrackElement("Module peek-overview"); + setModuleDeleteModal(true); + }} + > Delete module diff --git a/web/components/onboarding/invitations.tsx b/web/components/onboarding/invitations.tsx index c9ec19cc8..3315ff035 100644 --- a/web/components/onboarding/invitations.tsx +++ b/web/components/onboarding/invitations.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import useSWR, { mutate } from "swr"; // hooks -import { useApplication, useUser, useWorkspace } from "hooks/store"; +import { useEventTracker, useUser, useWorkspace } from "hooks/store"; // components import { Button } from "@plane/ui"; // helpers @@ -15,6 +15,7 @@ import { ROLE } from "constants/workspace"; import { IWorkspaceMemberInvitation } from "@plane/types"; // icons import { CheckCircle2, Search } from "lucide-react"; +import {} from "hooks/store/use-event-tracker"; type Props = { handleNextStep: () => void; @@ -28,9 +29,7 @@ export const Invitations: React.FC = (props) => { const [isJoiningWorkspaces, setIsJoiningWorkspaces] = useState(false); const [invitationsRespond, setInvitationsRespond] = useState([]); // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureEvent } = useEventTracker(); const { currentUser, updateCurrentUser } = useUser(); const { workspaces, fetchWorkspaces } = useWorkspace(); @@ -63,7 +62,7 @@ export const Invitations: React.FC = (props) => { await workspaceService .joinWorkspaces({ invitations: invitationsRespond }) .then(async (res) => { - postHogEventTracker("MEMBER_ACCEPTED", { ...res, state: "SUCCESS", accepted_from: "App" }); + captureEvent("Member accepted", { ...res, state: "SUCCESS", accepted_from: "App" }); await fetchWorkspaces(); await mutate(USER_WORKSPACES); await updateLastWorkspace(); @@ -72,7 +71,7 @@ export const Invitations: React.FC = (props) => { }) .catch((error) => { console.error(error); - postHogEventTracker("MEMBER_ACCEPTED", { state: "FAILED", accepted_from: "App" }); + captureEvent("Member accepted", { state: "FAILED", accepted_from: "App" }); }) .finally(() => setIsJoiningWorkspaces(false)); }; diff --git a/web/components/onboarding/tour/root.tsx b/web/components/onboarding/tour/root.tsx index 362c03ed5..6e1de15dd 100644 --- a/web/components/onboarding/tour/root.tsx +++ b/web/components/onboarding/tour/root.tsx @@ -3,7 +3,7 @@ import Image from "next/image"; import { observer } from "mobx-react-lite"; import { X } from "lucide-react"; // hooks -import { useApplication, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useUser } from "hooks/store"; // components import { TourSidebar } from "components/onboarding"; // ui @@ -78,10 +78,8 @@ export const TourRoot: React.FC = observer((props) => { // states const [step, setStep] = useState("welcome"); // store hooks - const { - commandPalette: commandPaletteStore, - eventTracker: { setTrackElement }, - } = useApplication(); + const { commandPalette: commandPaletteStore } = useApplication(); + const { setTrackElement } = useEventTracker(); const { currentUser } = useUser(); const currentStepIndex = TOUR_STEPS.findIndex((tourStep) => tourStep.key === step); @@ -159,7 +157,7 @@ export const TourRoot: React.FC = observer((props) => { variant="primary" onClick={() => { onComplete(); - setTrackElement("ONBOARDING_TOUR"); + setTrackElement("Onboarding tour"); commandPaletteStore.toggleCreateProjectModal(true); }} > diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index 3ae0b9e62..dc9c4de61 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { useTheme } from "next-themes"; import { observer } from "mobx-react-lite"; // hooks -import { useApplication, useDashboard, useProject, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useDashboard, useProject, useUser } from "hooks/store"; // components import { TourRoot } from "components/onboarding"; import { UserGreetingsView } from "components/user"; @@ -18,9 +18,9 @@ export const WorkspaceDashboardView = observer(() => { // theme const { resolvedTheme } = useTheme(); // store hooks + const { captureEvent, setTrackElement } = useEventTracker(); const { commandPalette: { toggleCreateProjectModal }, - eventTracker: { postHogEventTracker }, router: { workspaceSlug }, } = useApplication(); const { @@ -37,7 +37,7 @@ export const WorkspaceDashboardView = observer(() => { const handleTourCompleted = () => { updateTourCompleted() .then(() => { - postHogEventTracker("USER_TOUR_COMPLETE", { + captureEvent("User tour complete", { user_id: currentUser?.id, email: currentUser?.email, state: "SUCCESS", @@ -62,7 +62,7 @@ export const WorkspaceDashboardView = observer(() => { {homeDashboardId && joinedProjectIds ? ( <> {joinedProjectIds.length > 0 ? ( -
+
{currentUser && } {currentUser && !currentUser.is_tour_completed && ( @@ -81,7 +81,10 @@ export const WorkspaceDashboardView = observer(() => { progress." primaryButton={{ text: "Build your first project", - onClick: () => toggleCreateProjectModal(true), + onClick: () => { + setTrackElement("Dashboard"); + toggleCreateProjectModal(true); + }, }} comicBox={{ title: "Everything starts with a project in Plane", @@ -93,7 +96,7 @@ export const WorkspaceDashboardView = observer(() => { )} ) : ( -
+
)} diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 76e67e1b7..e7601ce35 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react-lite"; import { useTheme } from "next-themes"; // hooks -import { useApplication, useProject, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useProject, useUser } from "hooks/store"; // components import { ProjectCard } from "components/project"; import { Loader } from "@plane/ui"; @@ -13,10 +13,8 @@ export const ProjectCardList = observer(() => { // theme const { resolvedTheme } = useTheme(); // store hooks - const { - commandPalette: commandPaletteStore, - eventTracker: { setTrackElement }, - } = useApplication(); + const { commandPalette: commandPaletteStore } = useApplication(); + const { setTrackElement } = useEventTracker(); const { membership: { currentWorkspaceRole }, currentUser, @@ -66,7 +64,7 @@ export const ProjectCardList = observer(() => { primaryButton={{ text: "Start your first project", onClick: () => { - setTrackElement("PROJECTS_EMPTY_STATE"); + setTrackElement("Project empty state"); commandPaletteStore.toggleCreateProjectModal(true); }, }} diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index 1e6aa136a..377ef06c1 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -4,7 +4,7 @@ import { Dialog, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; import { X } from "lucide-react"; // hooks -import { useApplication, useProject, useUser, useWorkspace } from "hooks/store"; +import { useEventTracker, useProject, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button, CustomSelect, Input, TextArea } from "@plane/ui"; @@ -61,9 +61,7 @@ export interface ICreateProjectForm { export const CreateProjectModal: FC = observer((props) => { const { isOpen, onClose, setToFavorite = false, workspaceSlug } = props; // store - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureProjectEvent } = useEventTracker(); const { membership: { currentWorkspaceRole }, } = useUser(); @@ -135,10 +133,14 @@ export const CreateProjectModal: FC = observer((props) => { ...res, state: "SUCCESS", }; - postHogEventTracker("PROJECT_CREATED", newPayload, { - isGrouping: true, - groupType: "Workspace_metrics", - groupId: res.workspace, + captureProjectEvent({ + eventName: "Project created", + payload: newPayload, + group: { + isGrouping: true, + groupType: "Workspace_metrics", + groupId: res.workspace, + }, }); setToastAlert({ type: "success", @@ -157,17 +159,18 @@ export const CreateProjectModal: FC = observer((props) => { title: "Error!", message: err.data[key], }); - postHogEventTracker( - "PROJECT_CREATED", - { + captureProjectEvent({ + eventName: "Project created", + payload: { + ...payload, state: "FAILED", }, - { + group: { isGrouping: true, groupType: "Workspace_metrics", groupId: currentWorkspace?.id!, - } - ); + }, + }); }); }); }; diff --git a/web/components/project/delete-project-modal.tsx b/web/components/project/delete-project-modal.tsx index e2b0bc5f0..7e04a6ebd 100644 --- a/web/components/project/delete-project-modal.tsx +++ b/web/components/project/delete-project-modal.tsx @@ -4,7 +4,7 @@ import { Controller, useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; import { AlertTriangle } from "lucide-react"; // hooks -import { useApplication, useProject, useWorkspace } from "hooks/store"; +import { useEventTracker, useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button, Input } from "@plane/ui"; @@ -25,9 +25,7 @@ const defaultValues = { export const DeleteProjectModal: React.FC = (props) => { const { isOpen, project, onClose } = props; // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureProjectEvent } = useEventTracker(); const { currentWorkspace } = useWorkspace(); const { deleteProject } = useProject(); // router @@ -63,17 +61,15 @@ export const DeleteProjectModal: React.FC = (props) => { if (projectId && projectId.toString() === project.id) router.push(`/${workspaceSlug}/projects`); handleClose(); - postHogEventTracker( - "PROJECT_DELETED", - { - state: "SUCCESS", - }, - { + captureProjectEvent({ + eventName: "Project deleted", + payload: { ...project, state: "SUCCESS", element: "Project general settings" }, + group: { isGrouping: true, groupType: "Workspace_metrics", groupId: currentWorkspace?.id!, - } - ); + }, + }); setToastAlert({ type: "success", title: "Success!", @@ -81,17 +77,15 @@ export const DeleteProjectModal: React.FC = (props) => { }); }) .catch(() => { - postHogEventTracker( - "PROJECT_DELETED", - { - state: "FAILED", - }, - { + captureProjectEvent({ + eventName: "Project deleted", + payload: { ...project, state: "FAILED", element: "Project general settings" }, + group: { isGrouping: true, groupType: "Workspace_metrics", groupId: currentWorkspace?.id!, - } - ); + }, + }); setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/project/form.tsx b/web/components/project/form.tsx index 5be7033a4..f5efa7bc8 100644 --- a/web/components/project/form.tsx +++ b/web/components/project/form.tsx @@ -1,7 +1,7 @@ import { FC, useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; // hooks -import { useApplication, useProject, useWorkspace } from "hooks/store"; +import { useEventTracker, useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // components import EmojiIconPicker from "components/emoji-icon-picker"; @@ -32,9 +32,7 @@ export const ProjectDetailsForm: FC = (props) => { // states const [isLoading, setIsLoading] = useState(false); // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureProjectEvent } = useEventTracker(); const { currentWorkspace } = useWorkspace(); const { updateProject } = useProject(); // toast alert @@ -79,15 +77,15 @@ export const ProjectDetailsForm: FC = (props) => { return updateProject(workspaceSlug.toString(), project.id, payload) .then((res) => { - postHogEventTracker( - "PROJECT_UPDATED", - { ...res, state: "SUCCESS" }, - { + captureProjectEvent({ + eventName: "Project updated", + payload: { ...res, state: "SUCCESS", element: "Project general settings" }, + group: { isGrouping: true, groupType: "Workspace_metrics", groupId: res.workspace, - } - ); + }, + }); setToastAlert({ type: "success", title: "Success!", @@ -95,17 +93,15 @@ export const ProjectDetailsForm: FC = (props) => { }); }) .catch((error) => { - postHogEventTracker( - "PROJECT_UPDATED", - { - state: "FAILED", - }, - { + captureProjectEvent({ + eventName: "Project updated", + payload: { ...payload, state: "FAILED", element: "Project general settings" }, + group: { isGrouping: true, groupType: "Workspace_metrics", - groupId: currentWorkspace?.id!, - } - ); + groupId: currentWorkspace?.id, + }, + }); setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/project/leave-project-modal.tsx b/web/components/project/leave-project-modal.tsx index 96fc8df59..941bbbaa6 100644 --- a/web/components/project/leave-project-modal.tsx +++ b/web/components/project/leave-project-modal.tsx @@ -5,7 +5,7 @@ import { Dialog, Transition } from "@headlessui/react"; import { AlertTriangleIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; // hooks -import { useApplication, useUser } from "hooks/store"; +import { useEventTracker, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button, Input } from "@plane/ui"; @@ -34,9 +34,7 @@ export const LeaveProjectModal: FC = observer((props) => { const router = useRouter(); const { workspaceSlug } = router.query; // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureEvent } = useEventTracker(); const { membership: { leaveProject }, } = useUser(); @@ -65,7 +63,7 @@ export const LeaveProjectModal: FC = observer((props) => { .then(() => { handleClose(); router.push(`/${workspaceSlug}/projects`); - postHogEventTracker("PROJECT_MEMBER_LEAVE", { + captureEvent("Project member leave", { state: "SUCCESS", }); }) @@ -75,7 +73,7 @@ export const LeaveProjectModal: FC = observer((props) => { title: "Error!", message: "Something went wrong please try again later.", }); - postHogEventTracker("PROJECT_MEMBER_LEAVE", { + captureEvent("Project member leave", { state: "FAILED", }); }); diff --git a/web/components/project/member-list.tsx b/web/components/project/member-list.tsx index 899348dd2..fa8008c8f 100644 --- a/web/components/project/member-list.tsx +++ b/web/components/project/member-list.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { observer } from "mobx-react-lite"; import { Search } from "lucide-react"; // hooks -import { useApplication, useMember } from "hooks/store"; +import { useEventTracker, useMember } from "hooks/store"; // components import { ProjectMemberListItem, SendProjectInvitationModal } from "components/project"; // ui @@ -13,9 +13,7 @@ export const ProjectMemberList: React.FC = observer(() => { const [inviteModal, setInviteModal] = useState(false); const [searchQuery, setSearchQuery] = useState(""); // store hooks - const { - eventTracker: { setTrackElement }, - } = useApplication(); + const { setTrackElement } = useEventTracker(); const { project: { projectMemberIds, getProjectMemberDetails }, } = useMember(); diff --git a/web/components/project/send-project-invitation-modal.tsx b/web/components/project/send-project-invitation-modal.tsx index b165a40ae..39fb5c974 100644 --- a/web/components/project/send-project-invitation-modal.tsx +++ b/web/components/project/send-project-invitation-modal.tsx @@ -5,7 +5,7 @@ import { useForm, Controller, useFieldArray } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; import { ChevronDown, Plus, X } from "lucide-react"; // hooks -import { useApplication, useMember, useUser, useWorkspace } from "hooks/store"; +import { useEventTracker, useMember, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Avatar, Button, CustomSelect, CustomSearchSelect } from "@plane/ui"; @@ -45,9 +45,7 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { // toast alert const { setToastAlert } = useToast(); // store hooks - const { - eventTracker: { postHogEventTracker }, - } = useApplication(); + const { captureEvent } = useEventTracker(); const { membership: { currentProjectRole }, } = useUser(); @@ -89,8 +87,8 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { type: "success", message: "Members added successfully.", }); - postHogEventTracker( - "MEMBER_ADDED", + captureEvent( + "Member added", { ...res, state: "SUCCESS", @@ -104,8 +102,8 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { }) .catch((error) => { console.error(error); - postHogEventTracker( - "MEMBER_ADDED", + captureEvent( + "Member added", { state: "FAILED", }, diff --git a/web/components/project/settings/features-list.tsx b/web/components/project/settings/features-list.tsx index ed45792f2..34aec4db6 100644 --- a/web/components/project/settings/features-list.tsx +++ b/web/components/project/settings/features-list.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite"; import { ContrastIcon, FileText, Inbox, Layers } from "lucide-react"; import { DiceIcon, ToggleSwitch } from "@plane/ui"; // hooks -import { useApplication, useProject, useUser, useWorkspace } from "hooks/store"; +import { useEventTracker, useProject, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // types import { IProject } from "@plane/types"; @@ -51,9 +51,7 @@ export const ProjectFeaturesList: FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; // store hooks - const { - eventTracker: { setTrackElement, postHogEventTracker }, - } = useApplication(); + const { setTrackElement, captureEvent } = useEventTracker(); const { currentUser, membership: { currentProjectRole }, @@ -94,7 +92,7 @@ export const ProjectFeaturesList: FC = observer(() => { value={Boolean(currentProjectDetails?.[feature.property as keyof IProject])} onChange={() => { setTrackElement("PROJECT_SETTINGS_FEATURES_PAGE"); - postHogEventTracker(`TOGGLE_${feature.title.toUpperCase()}`, { + captureEvent(`Toggle ${feature.title.toLowerCase()}`, { workspace_id: currentWorkspace?.id, workspace_slug: currentWorkspace?.slug, project_id: currentProjectDetails?.id, diff --git a/web/components/project/sidebar-list-item.tsx b/web/components/project/sidebar-list-item.tsx index 8e4188d81..d9cd81d5f 100644 --- a/web/components/project/sidebar-list-item.tsx +++ b/web/components/project/sidebar-list-item.tsx @@ -18,7 +18,7 @@ import { MoreHorizontal, } from "lucide-react"; // hooks -import { useApplication, useProject } from "hooks/store"; +import { useApplication,useEventTracker, useProject } from "hooks/store"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; import useToast from "hooks/use-toast"; // helpers @@ -73,10 +73,8 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { projectId, provided, snapshot, handleCopyText, shortContextMenu = false } = props; // store hooks - const { - theme: themeStore, - eventTracker: { setTrackElement }, - } = useApplication(); + const { theme: themeStore } = useApplication(); + const { setTrackElement } = useEventTracker(); const { addProjectToFavorites, removeProjectFromFavorites, getProjectById } = useProject(); // states const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false); @@ -149,8 +147,9 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { {({ open }) => ( <>
{provided && ( = observer((props) => { >