mirror of
https://github.com/makeplane/plane.git
synced 2025-12-16 20:07:56 +01:00
feat: integrate enhanced new set of event trackers
This commit is contained in:
@@ -18,6 +18,9 @@ import { useProject } from "@/hooks/store/use-project";
|
||||
// plane web imports
|
||||
import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common";
|
||||
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { getUserRoleString, trackPageCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
|
||||
export const PagesListHeader = observer(function PagesListHeader() {
|
||||
// states
|
||||
@@ -28,6 +31,9 @@ export const PagesListHeader = observer(function PagesListHeader() {
|
||||
const searchParams = useSearchParams();
|
||||
const pageType = searchParams.get("type");
|
||||
// store hooks
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { data: currentUser } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { canCurrentUserCreatePage, createPage } = usePageStore(EPageStoreType.PROJECT);
|
||||
// handle page create
|
||||
@@ -40,13 +46,16 @@ export const PagesListHeader = observer(function PagesListHeader() {
|
||||
|
||||
await createPage(payload)
|
||||
.then((res) => {
|
||||
captureSuccess({
|
||||
eventName: PROJECT_PAGE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res?.id,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
if (currentWorkspace && currentUser) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackPageCreated(
|
||||
{ id: res?.id ?? "", created_at: new Date().toISOString() },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
"project",
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
const pageId = `/${workspaceSlug}/projects/${currentProjectDetails?.id}/pages/${res?.id}`;
|
||||
router.push(pageId);
|
||||
})
|
||||
|
||||
@@ -29,6 +29,7 @@ import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
|
||||
// plane web services
|
||||
import { WorkspaceService } from "@/plane-web/services";
|
||||
import { joinWorkspaceGroup } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
@@ -80,10 +81,9 @@ function UserInvitationsPage() {
|
||||
const invitation = invitations?.find((i) => i.id === firstInviteId);
|
||||
const redirectWorkspace = invitations?.find((i) => i.id === firstInviteId)?.workspace;
|
||||
if (redirectWorkspace?.id) {
|
||||
joinEventGroup(GROUP_WORKSPACE_TRACKER_EVENT, redirectWorkspace?.id, {
|
||||
date: new Date().toDateString(),
|
||||
workspace_id: redirectWorkspace?.id,
|
||||
});
|
||||
if (redirectWorkspace) {
|
||||
joinWorkspaceGroup(redirectWorkspace);
|
||||
}
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: MEMBER_TRACKER_EVENTS.accept,
|
||||
|
||||
@@ -20,6 +20,9 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import type { TProject } from "@/plane-web/types/projects";
|
||||
import ProjectAttributes from "./attributes";
|
||||
import { getProjectFormValues } from "./utils";
|
||||
import { getUserRoleString, trackProjectCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
|
||||
export type TCreateProjectFormProps = {
|
||||
setToFavorite?: boolean;
|
||||
@@ -36,6 +39,9 @@ export const CreateProjectForm = observer(function CreateProjectForm(props: TCre
|
||||
// store
|
||||
const { t } = useTranslation();
|
||||
const { addProjectToFavorites, createProject, updateProject } = useProject();
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { data: currentUser } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
// states
|
||||
const [isChangeInIdentifierRequired, setIsChangeInIdentifierRequired] = useState(true);
|
||||
// form info
|
||||
@@ -98,12 +104,15 @@ export const CreateProjectForm = observer(function CreateProjectForm(props: TCre
|
||||
await updateCoverImageStatus(res.id, coverImage);
|
||||
await updateProject(workspaceSlug.toString(), res.id, { cover_image_url: coverImage });
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: PROJECT_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
identifier: formData.identifier,
|
||||
},
|
||||
});
|
||||
if (currentUser && currentWorkspace && res) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackProjectCreated(
|
||||
{ id: res.id, created_at: res.created_at instanceof Date ? res.created_at : new Date() },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("success"),
|
||||
|
||||
266
apps/web/ce/helpers/event-tracker-v2.helper.ts
Normal file
266
apps/web/ce/helpers/event-tracker-v2.helper.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
import posthog from "posthog-js";
|
||||
import { EUserPermissions } from "@plane/types";
|
||||
import type { EUserProjectRoles, EUserWorkspaceRoles, IUser, IWorkspace, TUserProfile } from "@plane/types";
|
||||
|
||||
type TUserRole = "guest" | "member" | "admin" | "unknown";
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* Utilities
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the user role string from the user role enum
|
||||
* @param role - The user role enum
|
||||
* @returns The user role string
|
||||
*/
|
||||
|
||||
export const getUserRoleString = (
|
||||
role: EUserPermissions | EUserWorkspaceRoles | EUserProjectRoles | undefined
|
||||
): TUserRole => {
|
||||
if (!role) return "unknown";
|
||||
switch (role) {
|
||||
case EUserPermissions.GUEST:
|
||||
return "guest";
|
||||
case EUserPermissions.MEMBER:
|
||||
return "member";
|
||||
case EUserPermissions.ADMIN:
|
||||
return "admin";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* USER IDENTIFICATION
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Identify a user in PostHog with all required person properties
|
||||
* Call this after signup, login, or whenever session becomes authenticated
|
||||
*
|
||||
* @param user - User object from the store
|
||||
* @param profile - Optional user profile object (for onboarding status, role, use_case)
|
||||
*/
|
||||
|
||||
export const identifyUser = (user: IUser, profile?: TUserProfile) => {
|
||||
if (!posthog || !user) return;
|
||||
|
||||
posthog.identify(user.id, {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
display_name: user.display_name,
|
||||
date_joined: user.date_joined,
|
||||
last_login_medium: user.last_login_medium || "EMAIL",
|
||||
timezone: user.user_timezone,
|
||||
is_email_verified: user.is_email_verified,
|
||||
is_onboarded: profile?.is_onboarded || false,
|
||||
role: profile?.role || null,
|
||||
use_case: profile?.use_case || null,
|
||||
last_workspace_id: user.last_workspace_id || null,
|
||||
language: profile?.language || null,
|
||||
last_login_time: user.last_login_time || null,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* WORKSPACE GROUP TRACKING
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Join workspace group properties in PostHog
|
||||
* Call this whenever a user views a workspace (e.g., on workspace switch)
|
||||
*
|
||||
* @param workspace - Workspace object
|
||||
*/
|
||||
export const joinWorkspaceGroup = (workspace: Partial<IWorkspace>) => {
|
||||
if (!posthog || !workspace.slug) return;
|
||||
|
||||
posthog.group("workspace", workspace.slug, {
|
||||
workspace_id: workspace.id,
|
||||
workspace_name: workspace.name,
|
||||
workspace_slug: workspace.slug,
|
||||
workspace_size: workspace.organization_size,
|
||||
created_at: workspace.created_at instanceof Date ? workspace.created_at.toISOString() : workspace.created_at,
|
||||
owner_user_id: workspace.owner?.id || workspace.created_by,
|
||||
is_deleted: false,
|
||||
deleted_at: null,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* GENERIC EVENT TRACKING
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic event tracking function with workspace context
|
||||
* All workspace events must include workspace_id, role, and groups
|
||||
*
|
||||
* @param eventName - Event name in snake_case (e.g., "workspace_created")
|
||||
* @param properties - Event-specific properties
|
||||
* @param workspaceSlug - Workspace slug for group association
|
||||
* @param role - User's role in the workspace
|
||||
*/
|
||||
export const trackEvent = (eventName: string, properties: Record<string, unknown>, role: TUserRole) => {
|
||||
if (!posthog) return;
|
||||
|
||||
const eventProperties = {
|
||||
...properties,
|
||||
role: role || "unknown",
|
||||
};
|
||||
|
||||
posthog.capture(eventName, eventProperties);
|
||||
};
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* LIFECYCLE EVENTS
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Track workspace creation
|
||||
* Call this immediately after a workspace is created
|
||||
*/
|
||||
export const trackWorkspaceCreated = (workspace: IWorkspace, user: IUser, role: TUserRole) => {
|
||||
joinWorkspaceGroup(workspace);
|
||||
trackEvent(
|
||||
"workspace_created",
|
||||
{
|
||||
id: user.id,
|
||||
workspace_id: workspace.id,
|
||||
workspace_slug: workspace.slug,
|
||||
workspace_name: workspace.name,
|
||||
created_at: workspace.created_at instanceof Date ? workspace.created_at.toISOString() : workspace.created_at,
|
||||
},
|
||||
role
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Track workspace deletion
|
||||
*/
|
||||
export const trackWorkspaceDeleted = (workspace: IWorkspace, user: IUser, role: TUserRole) => {
|
||||
trackEvent(
|
||||
"workspace_deleted",
|
||||
{
|
||||
id: user.id,
|
||||
workspace_id: workspace.id,
|
||||
workspace_slug: workspace.slug,
|
||||
deleted_at: new Date().toISOString(),
|
||||
},
|
||||
role
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* PRODUCT ACTIVATION EVENTS
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Track project creation
|
||||
*/
|
||||
export const trackProjectCreated = (
|
||||
project: { id: string; created_at: string | Date },
|
||||
workspace: IWorkspace,
|
||||
user: IUser,
|
||||
role: TUserRole
|
||||
) => {
|
||||
trackEvent(
|
||||
"project_created",
|
||||
{
|
||||
id: user.id,
|
||||
workspace_id: workspace.id,
|
||||
workspace_slug: workspace.slug,
|
||||
project_id: project.id,
|
||||
created_at: project.created_at instanceof Date ? project.created_at.toISOString() : project.created_at,
|
||||
},
|
||||
role
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Track work item creation
|
||||
*/
|
||||
export const trackWorkItemCreated = (
|
||||
workItem: { id: string; type?: string; created_at: string | Date },
|
||||
project: { id: string },
|
||||
workspace: IWorkspace,
|
||||
user: IUser,
|
||||
role: TUserRole
|
||||
) => {
|
||||
trackEvent(
|
||||
"work_item_created",
|
||||
{
|
||||
id: user.id,
|
||||
workspace_id: workspace.id,
|
||||
workspace_slug: workspace.slug,
|
||||
project_id: project.id,
|
||||
work_item_id: workItem.id,
|
||||
work_item_type: workItem.type,
|
||||
created_at: workItem.created_at instanceof Date ? workItem.created_at.toISOString() : workItem.created_at,
|
||||
},
|
||||
role
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Track cycle creation
|
||||
*/
|
||||
export const trackCycleCreated = (
|
||||
cycle: { id: string; length_days?: number; created_at: string | Date },
|
||||
project: { id: string },
|
||||
workspace: IWorkspace,
|
||||
user: IUser,
|
||||
role: TUserRole
|
||||
) => {
|
||||
trackEvent(
|
||||
"cycle_created",
|
||||
{
|
||||
id: user.id,
|
||||
workspace_id: workspace.id,
|
||||
workspace_slug: workspace.slug,
|
||||
project_id: project.id,
|
||||
cycle_id: cycle.id,
|
||||
cycle_length_days: cycle.length_days || null,
|
||||
created_at: cycle.created_at instanceof Date ? cycle.created_at.toISOString() : cycle.created_at,
|
||||
},
|
||||
role
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Track page creation
|
||||
*/
|
||||
export const trackPageCreated = (
|
||||
page: { id: string; created_at: string | Date; project_id?: string | null },
|
||||
workspace: IWorkspace,
|
||||
user: IUser,
|
||||
location: "project" | "wiki" | "teamspace" | "workitem",
|
||||
role: TUserRole
|
||||
) => {
|
||||
trackEvent(
|
||||
"page_created",
|
||||
{
|
||||
id: user.id,
|
||||
workspace_id: workspace.id,
|
||||
workspace_slug: workspace.slug,
|
||||
page_id: page.id,
|
||||
location,
|
||||
project_id: page.project_id || null,
|
||||
created_at: page.created_at instanceof Date ? page.created_at.toISOString() : page.created_at,
|
||||
},
|
||||
role
|
||||
);
|
||||
};
|
||||
@@ -18,6 +18,9 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { CycleService } from "@/services/cycle.service";
|
||||
// local imports
|
||||
import { CycleForm } from "./form";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { getUserRoleString, trackCycleCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
|
||||
type CycleModalProps = {
|
||||
isOpen: boolean;
|
||||
@@ -39,6 +42,10 @@ export function CycleCreateUpdateModal(props: CycleModalProps) {
|
||||
const { createCycle, updateCycleDetails } = useCycle();
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { data: currentUser } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const { setValue: setCycleTab } = useLocalStorage<TCycleTabOptions>("cycle_tab", "active");
|
||||
|
||||
const handleCreateCycle = async (payload: Partial<ICycle>) => {
|
||||
@@ -62,12 +69,16 @@ export function CycleCreateUpdateModal(props: CycleModalProps) {
|
||||
title: "Success!",
|
||||
message: "Cycle created successfully.",
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res.id,
|
||||
},
|
||||
});
|
||||
if (currentWorkspace && currentUser) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackCycleCreated(
|
||||
{ id: res.id, created_at: new Date().toISOString() },
|
||||
{ id: projectId },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setToast({
|
||||
|
||||
@@ -29,6 +29,8 @@ import { FileService } from "@/services/file.service";
|
||||
import { InboxIssueDescription } from "./issue-description";
|
||||
import { InboxIssueProperties } from "./issue-properties";
|
||||
import { InboxIssueTitle } from "./issue-title";
|
||||
import { getUserRoleString, trackWorkItemCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
|
||||
const fileService = new FileService();
|
||||
|
||||
@@ -69,6 +71,9 @@ export const InboxIssueCreateRoot = observer(function InboxIssueCreateRoot(props
|
||||
const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id;
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { getProjectById } = useProject();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data: currentUser } = useUser();
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { t } = useTranslation();
|
||||
// states
|
||||
const [createMore, setCreateMore] = useState<boolean>(false);
|
||||
@@ -170,12 +175,16 @@ export const InboxIssueCreateRoot = observer(function InboxIssueCreateRoot(props
|
||||
descriptionEditorRef?.current?.clearEditor();
|
||||
setFormData(defaultIssueData);
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res?.issue?.id,
|
||||
},
|
||||
});
|
||||
if (currentWorkspace && currentUser) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackWorkItemCreated(
|
||||
{ id: res?.issue?.id ?? "", created_at: new Date().toISOString() },
|
||||
{ id: projectId },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: `Success!`,
|
||||
|
||||
@@ -17,6 +17,9 @@ import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { QuickAddIssueFormRoot } from "@/plane-web/components/issues/quick-add";
|
||||
// local imports
|
||||
import { CreateIssueToastActionItems } from "../../create-issue-toast-action-items";
|
||||
import { getUserRoleString, trackWorkItemCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
|
||||
export type TQuickAddIssueForm = {
|
||||
ref: React.RefObject<HTMLFormElement>;
|
||||
@@ -67,6 +70,12 @@ export const QuickAddIssueRoot = observer(function QuickAddIssueRoot(props: TQui
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// states
|
||||
const [isOpen, setIsOpen] = useState(isQuickAddOpen ?? false);
|
||||
|
||||
// store hooks
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data: currentUser } = useUser();
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
|
||||
// form info
|
||||
const {
|
||||
reset,
|
||||
@@ -127,20 +136,26 @@ export const QuickAddIssueRoot = observer(function QuickAddIssueRoot(props: TQui
|
||||
},
|
||||
});
|
||||
|
||||
await quickAddPromise
|
||||
.then((res) => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: res?.id },
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: payload.id },
|
||||
error: error as Error,
|
||||
});
|
||||
const quickAddRes = await quickAddPromise;
|
||||
|
||||
try {
|
||||
if (currentWorkspace && currentUser && quickAddRes) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackWorkItemCreated(
|
||||
{ id: quickAddRes.id, created_at: new Date().toISOString() },
|
||||
{ id: projectId.toString() },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: payload.id },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ import { DraftIssueLayout } from "./draft-issue-layout";
|
||||
import { IssueFormRoot } from "./form";
|
||||
import type { IssueFormProps } from "./form";
|
||||
import type { IssuesModalProps } from "./modal";
|
||||
import { getUserRoleString, trackWorkItemCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
|
||||
export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueModalBase(props: IssuesModalProps) {
|
||||
const {
|
||||
@@ -72,6 +75,9 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
const { fetchIssue } = useIssueDetail();
|
||||
const { allowedProjectIds, handleCreateUpdatePropertyValues, handleCreateSubWorkItem } = useIssueModal();
|
||||
const { getProjectByIdentifier } = useProject();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data: currentUser } = useUser();
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
// current store details
|
||||
const { createIssue, updateIssue } = useIssuesActions(storeType);
|
||||
// derived values
|
||||
@@ -240,10 +246,16 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
/>
|
||||
),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: response.id },
|
||||
});
|
||||
if (currentWorkspace && currentUser && response) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackWorkItemCreated(
|
||||
{ id: response.id, created_at: new Date().toISOString() },
|
||||
{ id: payload.project_id },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
if (!createMore) handleClose();
|
||||
if (createMore && issueTitleRef) issueTitleRef?.current?.focus();
|
||||
setDescription("<p></p>");
|
||||
@@ -319,10 +331,17 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
title: t("success"),
|
||||
message: t("issue_updated_successfully"),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: data.id },
|
||||
});
|
||||
|
||||
if (currentWorkspace && currentUser) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackWorkItemCreated(
|
||||
{ id: data.id, created_at: new Date().toISOString() },
|
||||
{ id: payload.project_id },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
handleClose();
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
|
||||
@@ -14,6 +14,9 @@ import type { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
import { usePageStore } from "@/plane-web/hooks/store";
|
||||
// local imports
|
||||
import { PageForm } from "./page-form";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { getUserRoleString, trackPageCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
@@ -45,6 +48,10 @@ export function CreatePageModal(props: Props) {
|
||||
const router = useAppRouter();
|
||||
// store hooks
|
||||
const { createPage } = usePageStore(storeType);
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { data: currentUser } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const handlePageFormData = <T extends keyof TPage>(key: T, value: TPage[T]) =>
|
||||
setPageFormData((prev) => ({ ...prev, [key]: value }));
|
||||
|
||||
@@ -64,12 +71,16 @@ export function CreatePageModal(props: Props) {
|
||||
try {
|
||||
const pageData = await createPage(pageFormData);
|
||||
if (pageData) {
|
||||
captureSuccess({
|
||||
eventName: PROJECT_PAGE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: pageData.id,
|
||||
},
|
||||
});
|
||||
if (currentWorkspace && currentUser) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackPageCreated(
|
||||
{ id: pageData.id ?? "", created_at: new Date().toISOString() },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
"project",
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
handleStateClear();
|
||||
if (redirectionEnabled) router.push(`/${workspaceSlug}/projects/${projectId}/pages/${pageData.id}`);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ import { EUserProjectRoles } from "@plane/types";
|
||||
import { PageLoader } from "@/components/pages/loaders/page-loader";
|
||||
import { captureClick, captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
// plane web hooks
|
||||
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { getUserRoleString, trackPageCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
@@ -35,8 +37,10 @@ export const PagesListMainContent = observer(function PagesListMainContent(props
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { isAnyPageAvailable, getCurrentProjectFilteredPageIdsByTab, getCurrentProjectPageIdsByTab, loader } =
|
||||
usePageStore(storeType);
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { allowPermissions, getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { createPage } = usePageStore(EPageStoreType.PROJECT);
|
||||
const { data: currentUser } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
// states
|
||||
const [isCreatingPage, setIsCreatingPage] = useState(false);
|
||||
// router
|
||||
@@ -60,13 +64,16 @@ export const PagesListMainContent = observer(function PagesListMainContent(props
|
||||
|
||||
await createPage(payload)
|
||||
.then((res) => {
|
||||
captureSuccess({
|
||||
eventName: PROJECT_PAGE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res?.id,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
if (currentWorkspace && currentUser && res?.id) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);
|
||||
trackPageCreated(
|
||||
{ id: res.id, created_at: new Date().toISOString() },
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
"project",
|
||||
getUserRoleString(role)
|
||||
);
|
||||
}
|
||||
const pageId = `/${workspaceSlug}/projects/${currentProjectDetails?.id}/pages/${res?.id}`;
|
||||
router.push(pageId);
|
||||
})
|
||||
|
||||
@@ -14,8 +14,9 @@ import { Input } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUserSettings } from "@/hooks/store/user";
|
||||
import { useUser, useUserPermissions, useUserSettings } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { getUserRoleString, trackWorkspaceDeleted } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
|
||||
type Props = {
|
||||
data: IWorkspace | null;
|
||||
@@ -36,6 +37,9 @@ export const DeleteWorkspaceForm = observer(function DeleteWorkspaceForm(props:
|
||||
const { t } = useTranslation();
|
||||
const { getWorkspaceRedirectionUrl } = useWorkspace();
|
||||
const { fetchCurrentUserSettings } = useUserSettings();
|
||||
const { getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
|
||||
const { data: currentUser } = useUser();
|
||||
|
||||
// form info
|
||||
const {
|
||||
control,
|
||||
@@ -64,10 +68,12 @@ export const DeleteWorkspaceForm = observer(function DeleteWorkspaceForm(props:
|
||||
await fetchCurrentUserSettings();
|
||||
handleClose();
|
||||
router.push(getWorkspaceRedirectionUrl());
|
||||
captureSuccess({
|
||||
eventName: WORKSPACE_TRACKER_EVENTS.delete,
|
||||
payload: { slug: data.slug },
|
||||
});
|
||||
|
||||
if (currentUser && data) {
|
||||
const role = getWorkspaceRoleByWorkspaceSlug(data.slug);
|
||||
trackWorkspaceDeleted(data, currentUser, getUserRoleString(role));
|
||||
}
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("workspace_settings.settings.general.delete_modal.success_title"),
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { lazy, Suspense, useEffect, useCallback, useRef, useState } from "react";
|
||||
import { PostHogProvider as PHProvider } from "@posthog/react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import type { ReactNode } from "react";
|
||||
import { lazy, Suspense, useCallback, useEffect, useRef, useState } from "react";
|
||||
// constants
|
||||
import { GROUP_WORKSPACE_TRACKER_EVENT } from "@plane/constants";
|
||||
// hooks
|
||||
import { captureClick, joinEventGroup } from "@/helpers/event-tracker.helper";
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { identifyUser, joinWorkspaceGroup } from "@/plane-web/helpers/event-tracker-v2.helper";
|
||||
import { useInstance } from "@/hooks/store/use-instance";
|
||||
import { useUser, useUserProfile } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUser, useUserPermissions, useUserProfile } from "@/hooks/store/user";
|
||||
// dynamic imports
|
||||
const PostHogPageView = lazy(function PostHogPageView() {
|
||||
return import("@/lib/posthog-view");
|
||||
@@ -24,44 +23,27 @@ const PostHogProvider = observer(function PostHogProvider(props: IPosthogWrapper
|
||||
const { children } = props;
|
||||
const { data: user } = useUser();
|
||||
const { data: profile } = useUserProfile();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { instance } = useInstance();
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
const { getWorkspaceRoleByWorkspaceSlug, getProjectRoleByWorkspaceSlugAndProjectId } = useUserPermissions();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
// refs
|
||||
const isInitializedRef = useRef(false);
|
||||
// states
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
// derived values
|
||||
const currentProjectRole = getProjectRoleByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString(),
|
||||
projectId?.toString()
|
||||
);
|
||||
const currentWorkspaceRole = getWorkspaceRoleByWorkspaceSlug(workspaceSlug?.toString());
|
||||
|
||||
const is_telemetry_enabled = instance?.is_telemetry_enabled || false;
|
||||
const is_posthog_enabled = process.env.VITE_POSTHOG_KEY && process.env.VITE_POSTHOG_HOST && is_telemetry_enabled;
|
||||
|
||||
useEffect(() => {
|
||||
if (user && profile && hydrated) {
|
||||
// Identify sends an event, so you want may want to limit how often you call it
|
||||
posthog?.identify(user.email, {
|
||||
id: user.id,
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
email: user.email,
|
||||
display_name: user.display_name,
|
||||
date_joined: user.date_joined,
|
||||
last_login_medium: user.last_login_medium,
|
||||
is_email_verified: user.is_email_verified,
|
||||
timezone: user.user_timezone,
|
||||
is_onboarded: profile.is_onboarded,
|
||||
role: profile.role,
|
||||
use_case: profile.use_case,
|
||||
language: profile.language,
|
||||
last_workspace_id: profile.last_workspace_id,
|
||||
});
|
||||
if (user && profile && hydrated && is_posthog_enabled) {
|
||||
identifyUser(user, profile);
|
||||
}
|
||||
}, [user, profile, hydrated]);
|
||||
}, [user, profile, hydrated, is_posthog_enabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentWorkspace && hydrated && is_posthog_enabled) {
|
||||
joinWorkspaceGroup(currentWorkspace);
|
||||
}
|
||||
}, [currentWorkspace, hydrated, is_posthog_enabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitializedRef.current) return; // prevent multiple initializations
|
||||
|
||||
3
apps/web/ee/helpers/event-tracker-v2.helper.ts
Normal file
3
apps/web/ee/helpers/event-tracker-v2.helper.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "ce/helpers/event-tracker-v2.helper";
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface IUser extends IUserLite {
|
||||
user_timezone: string;
|
||||
username: string;
|
||||
last_login_medium: TLoginMediums;
|
||||
last_login_time: string | null;
|
||||
theme: IUserTheme;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user