Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[WEB-5681] refactor: add new event trackers#8293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
JayashTripathy wants to merge17 commits intopreview
base:preview
Choose a base branch
Loading
fromchore-event-updates

Conversation

@JayashTripathy
Copy link
Member

@JayashTripathyJayashTripathy commentedDec 10, 2025
edited by coderabbitaibot
Loading

Description

This PR adds new set of event trackers.

Type of Change

  • Improvement (change that would cause existing functionality to not work as expected)

Screenshots and Media (if applicable)

Test Scenarios

References

Summary by CodeRabbit

  • Chores
    • Migrated analytics to a centralized, role-aware tracking system across workspaces, projects, pages, work items, cycles and invitations; safer PostHog guards and group context added.
  • New Features
    • Exposes users' last login time in profile/me endpoints and public types (read-only).
  • Chores / Breaking
    • Small public prop change: workspace delete form props expanded (add onClose).

✏️ Tip: You can customize this high-level summary in your review settings.

sriramveeraghantaand others added10 commitsDecember 8, 2025 21:21
…ameter and update event tracking in workspace creation
…ameter and update event tracking in workspace creation
@coderabbitai
Copy link
Contributor

coderabbitaibot commentedDec 10, 2025
edited
Loading

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new user fieldlast_login_time, centralizes frontend analytics in a new event-tracker-v2 helper, and wires backend and frontend flows to emit structured PostHog tracking events for invites and workspace/project/page/cycle/work-item lifecycle actions.

Changes

Cohort / File(s)Change Summary
User model & serializers
packages/types/src/users.ts,apps/api/plane/app/serializers/user.py
Added `last_login_time: string
Backend event tracking task
apps/api/plane/bgtasks/event_tracking_task.py
Replacedauth_events() withtrack_event(user_id, event_name, slug, event_properties); addedpreprocess_data_properties() and PostHog guard; includes workspace group context and normalized payloads.
Backend invite / join integrations
apps/api/plane/app/views/workspace/invite.py,apps/api/plane/authentication/utils/workspace_project_join.py
Emittrack_event.delay() foruser_invited_to_workspace anduser_joined_workspace during invitation creation, acceptance, and bulk-join flows; removed previous standalone workspace invite event emission.
Frontend centralized tracker (new)
apps/web/ce/helpers/event-tracker-v2.helper.ts,apps/web/ee/helpers/event-tracker-v2.helper.ts
New helper exposing role mapping,identifyUser,joinWorkspaceGroup, generictrackEvent, and domain trackers (trackWorkspaceCreated/Deleted,trackProjectCreated,trackWorkItemCreated,trackCycleCreated,trackPageCreated); EE re-exports CE helper.
Frontend PostHog provider
apps/web/core/lib/posthog-provider.tsx
Switched to usingidentifyUser() andjoinWorkspaceGroup() helpers; simplified identification and group join logic; removed inline role derivation.
Frontend: workspace lifecycle components
apps/web/core/components/workspace/*,apps/web/core/components/onboarding/*
ReplacedcaptureSuccess() with conditionaltrackWorkspaceCreated() /trackWorkspaceDeleted(); added user/workspace hooks and role derivation;CreateWorkspace Props expanded;DeleteWorkspaceForm Props gainedonClose.
Frontend: project/page/cycle/work-item flows
apps/web/ce/components/projects/create/root.tsx,apps/web/core/components/pages/*,apps/web/core/components/cycles/modal.tsx,apps/web/core/components/inbox/*,apps/web/core/components/issues/*
Replaced legacycaptureSuccess() calls with conditional v2 tracker calls (trackProjectCreated,trackPageCreated,trackCycleCreated,trackWorkItemCreated); migrated some handlers to async/await and added user/workspace guards.
Frontend invitations
apps/web/app/(all)/invitations/page.tsx
ReplacedjoinEventGroup(...) withjoinWorkspaceGroup(redirectWorkspace) helper.

Sequence Diagram(s)

sequenceDiagram  participant User  participant Frontend  participant EventHelper as Event-Tracker-V2  participant PostHog  User->>Frontend: create project / page / work item / cycle  Frontend->>EventHelper: compute role, call trackXCreated(payload, role)  EventHelper->>PostHog: identify/join group (if needed) + capture event  PostHog-->>EventHelper: ack  EventHelper-->>Frontend: (no-op/async)
Loading
sequenceDiagram  participant Client  participant BackendAPI  participant CeleryTask as track_event.delay  participant BGTask as event_tracking_task.track_event  participant WorkspaceModel as Workspace  participant PostHog  Client->>BackendAPI: accept/join workspace or send invite  BackendAPI->>CeleryTask: enqueue track_event.delay(user_id, event, slug, props)  CeleryTask->>BGTask: run track_event(user_id, event, slug, props)  BGTask->>WorkspaceModel: load workspace (for role/ownership)  BGTask->>PostHog: preprocess props, include groups, capture event  PostHog-->>BGTask: ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay special attention to:
    • apps/api/plane/bgtasks/event_tracking_task.py — payload preprocessing, PostHog guard, and groups shape.
    • apps/api/plane/app/views/workspace/invite.py and workspace_project_join.py — correctness of enqueued event properties and timing.
    • New frontend helper (apps/web/ce/helpers/event-tracker-v2.helper.ts) — role mapping, null guards, and exported API consistency.
    • Cross-cutting replacements of captureSuccess — ensure no remaining legacy telemetry and consistent event properties.
    • Type propagation forlast_login_time across serializers, API outputs, and frontend types.
    • Prop changes to CreateWorkspace and DeleteWorkspaceForm — verify all call sites updated.

Poem

🐰
I hopped through code with tiny feet,
I tracked each invite, page, and feat,
From last login time to workspace cheer,
PostHog sings when rabbits near,
Hooray — events are tidy and neat.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 10.00% which is insufficient. The required threshold is 80.00%.You can run@coderabbitai generate docstrings to improve docstring coverage.
Description check❓ InconclusiveThe PR description is minimal and lacks detail. It provides only 'This PR adds new set of event trackers' with no substantive explanation of the scope, specific changes, or implementation details required for thorough review.Expand the description to explain what event trackers were added, which components/modules were affected, why the changes improve functionality, and include test scenarios demonstrating the new tracking behavior.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe PR title 'refactor: add new event trackers' directly and clearly describes the main change—adding new event tracking functionality across multiple components and modules.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branchchore-event-updates

Thanks for usingCodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment@coderabbitai help to get the list of available commands and usage tips.

@JayashTripathyJayashTripathy changed the titlerefactor: event trackersrefactor: Added new event trackersDec 15, 2025
@JayashTripathyJayashTripathy changed the titlerefactor: Added new event trackersrefactor: add new event trackersDec 15, 2025
@JayashTripathyJayashTripathy marked this pull request as ready for reviewDecember 15, 2025 17:34
CopilotAI review requested due to automatic review settingsDecember 15, 2025 17:34
Copy link
Contributor

CopilotAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Pull request overview

This PR refactors event tracking by introducing a new v2 event tracking system. The changes replace the old event tracking implementation with a more structured approach using PostHog, adding user identification, workspace group tracking, and standardized event tracking functions.

Key changes:

  • New event tracker v2 helper module with standardized tracking functions for lifecycle and product activation events
  • Updated event tracking calls across the codebase to use the new v2 tracking system
  • Addedlast_login_time field to user types and serializers
  • Backend changes to support the new event tracking infrastructure with a generictrack_event task

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 7 comments.

Show a summary per file
FileDescription
packages/types/src/users.tsAddedlast_login_time field to IUser interface
apps/web/ee/helpers/event-tracker-v2.helper.tsCreated EE export for event tracker v2 helper
apps/web/core/lib/posthog-provider.tsxRefactored PostHog provider to use new v2 identification and workspace group tracking
apps/web/core/components/workspace/delete-workspace-form.tsxUpdated workspace deletion to use v2 event tracking
apps/web/core/components/workspace/create-workspace-form.tsxUpdated workspace creation to use v2 event tracking
apps/web/core/components/pages/pages-list-main-content.tsxUpdated page creation to use v2 event tracking
apps/web/core/components/pages/modals/create-page-modal.tsxUpdated page modal creation to use v2 event tracking
apps/web/core/components/onboarding/steps/workspace/create.tsxUpdated onboarding workspace creation to use v2 event tracking
apps/web/core/components/onboarding/create-workspace.tsxUpdated onboarding workspace creation to use v2 event tracking
apps/web/core/components/issues/issue-modal/base.tsxUpdated issue creation/update to use v2 event tracking
apps/web/core/components/issues/issue-layouts/quick-add/root.tsxUpdated quick-add issue creation to use v2 event tracking
apps/web/core/components/inbox/modals/create-modal/create-root.tsxUpdated inbox issue creation to use v2 event tracking
apps/web/core/components/cycles/modal.tsxUpdated cycle creation to use v2 event tracking
apps/web/ce/helpers/event-tracker-v2.helper.tsCreated new event tracker v2 helper with standardized tracking functions
apps/web/ce/components/projects/create/root.tsxUpdated project creation to use v2 event tracking
apps/web/app/(all)/invitations/page.tsxUpdated invitation acceptance to use v2 workspace group tracking
apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsxUpdated page header creation to use v2 event tracking
apps/api/plane/bgtasks/event_tracking_task.pyRefactored backend event tracking with new generic track_event task
apps/api/plane/authentication/utils/workspace_project_join.pyAdded event tracking for workspace joins
apps/api/plane/app/views/workspace/invite.pyAdded event tracking for workspace invitations and acceptances
apps/api/plane/app/serializers/user.pyAddedlast_login_time to user serializer

💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.

Comment on lines +38 to +42
workspace=Workspace.objects.get(slug=slug)
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The function usesWorkspace.objects.get(slug=slug) without error handling. If the workspace doesn't exist, this will raise aWorkspace.DoesNotExist exception that will be caught by the generic exception handler intrack_event, causing the event tracking to fail silently. Add proper error handling or document that the workspace must exist.

Suggested change
workspace=Workspace.objects.get(slug=slug)
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"
try:
workspace=Workspace.objects.get(slug=slug)
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"
exceptWorkspace.DoesNotExist:
logger.error(f"Workspace with slug '{slug}' does not exist when processing event '{event_name}'.")
data_properties["role"]="unknown"

Copilot uses AI. Check for mistakes.
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The logic assumes that if a user is not the workspace owner, they must be an admin. However, the invitee could have any role (guest, member, admin). The actual role should come fromdata_properties['invitee_role'] which is passed in the event properties from the workspace invite view.

Suggested change
data_properties["role"]="admin"
data_properties["role"]=data_properties.get("invitee_role")

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (18)
apps/web/app/(all)/invitations/page.tsx (1)

32-32:Workspace group tracking wiring is correct; consider simplifying the null-check

UsingjoinWorkspaceGroup(redirectWorkspace) here is the right move and aligns this page with the new v2 tracker.

You can simplify the nested condition without changing behavior:

-        const redirectWorkspace = invitations?.find((i) => i.id === firstInviteId)?.workspace;-        if (redirectWorkspace?.id) {-          if (redirectWorkspace) {-            joinWorkspaceGroup(redirectWorkspace);-          }-        }+        const redirectWorkspace = invitations?.find((i) => i.id === firstInviteId)?.workspace;+        if (redirectWorkspace?.id) {+          joinWorkspaceGroup(redirectWorkspace);+        }

Also applies to: 83-87

apps/api/plane/app/serializers/user.py (1)

27-52:last_login_time exposure and read-only behavior look correct

Adding"last_login_time" toread_only_fields and toUserMeSerializer.Meta.fields matches the new user type and keeps the field server-controlled; no functional issues here.

Minor cleanup (non-blocking):UserMeSerializer.Meta.fields lists"is_email_verified" twice; you can safely drop one occurrence.

Also applies to: 59-83

apps/api/plane/app/views/workspace/invite.py (1)

24-26:Event tracking for workspace invites and joins is correctly integrated; consider ISO timestamps

The newtrack_event.delay(...) calls on:

  • invite creation (user_invited_to_workspace), and
  • workspace join flows (user_joined_workspace for single and bulk joins)

are wired with coherent properties (user_id,workspace_id,workspace_slug, role, and invite/join timestamps) and match thetrack_event task signature.

For slightly cleaner payloads, you might prefer ISO-formatted timestamps overstr(timezone.now()):

- "invited_at": str(timezone.now()),+ "invited_at": timezone.now().isoformat(),@@- "joined_at": str(timezone.now()),+ "joined_at": timezone.now().isoformat(),

This is non-blocking but improves downstream parsing.

Also applies to: 124-136, 202-213, 270-282

apps/web/core/components/workspace/create-workspace-form.tsx (1)

18-24:Unused import and inconsistent import ordering.

ThecaptureSuccess import on line 18 appears to be unused after this refactor. Additionally, the new imports at lines 23-24 should follow the established grouping pattern in this file (helpers before hooks, with proper comment sections).

-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";+import { captureError } from "@/helpers/event-tracker.helper";+import { getUserRoleString, trackWorkspaceCreated } from "@/plane-web/helpers/event-tracker-v2.helper"; import { useWorkspace } from "@/hooks/store/use-workspace"; import { useAppRouter } from "@/hooks/use-app-router";+import { useUser, useUserPermissions } from "@/hooks/store/user"; // services import { WorkspaceService } from "@/plane-web/services";-import { getUserRoleString, trackWorkspaceCreated } from "@/plane-web/helpers/event-tracker-v2.helper";-import { useUser, useUserPermissions } from "@/hooks/store/user";
apps/web/core/components/issues/issue-modal/base.tsx (1)

31-33:Import ordering inconsistency.

The new imports are placed after local component imports. They should be grouped with other helpers and hooks earlier in the import section to maintain consistency.

 // hooks import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";+import { getUserRoleString, trackWorkItemCreated } from "@/plane-web/helpers/event-tracker-v2.helper"; import { useIssueModal } from "@/hooks/context/use-issue-modal"; import { useCycle } from "@/hooks/store/use-cycle"; import { useIssueDetail } from "@/hooks/store/use-issue-detail"; import { useIssues } from "@/hooks/store/use-issues"; import { useModule } from "@/hooks/store/use-module"; import { useProject } from "@/hooks/store/use-project";+import { useWorkspace } from "@/hooks/store/use-workspace";+import { useUser, useUserPermissions } from "@/hooks/store/user"; import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; import { useIssuesActions } from "@/hooks/use-issues-actions"; // ...-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";
apps/web/core/components/pages/modals/create-page-modal.tsx (2)

17-19:Import ordering inconsistency.

The new imports should be grouped with related imports. The toast import at line 11 and the helper/hook imports at lines 17-19 are scattered.


96-100:Hardcoded English strings should use i18n.

The error toast uses hardcoded English strings"Error!" and"Page could not be created...". Other components in this PR uset() translations for user-facing text. Consider using the translation function for consistency.

       setToast({         type: TOAST_TYPE.ERROR,-        title: "Error!",-        message: error?.data?.error || "Page could not be created. Please try again.",+        title: t("error"),+        message: error?.data?.error || t("page_creation_failed"),       });
apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (2)

15-22:Unused importcaptureSuccess.

ThecaptureSuccess import on line 15 appears to be unused after this refactor.

-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";+import { captureError } from "@/helpers/event-tracker.helper";

139-158:Using synthetic timestamp instead of response data.

Line 145 usesnew Date().toISOString() forcreated_at instead ofquickAddRes.created_at. Other components in this PR (e.g.,base.tsx) use the actualresponse.created_at from the API. Using a client-side timestamp may introduce slight discrepancies between the tracked event and the actual server-recorded creation time.

       try {         if (currentWorkspace && currentUser && quickAddRes) {           const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);           trackWorkItemCreated(-            { id: quickAddRes.id, created_at: new Date().toISOString() },+            { id: quickAddRes.id, created_at: quickAddRes.created_at },             { id: projectId.toString() },             currentWorkspace,             currentUser,             getUserRoleString(role)           );         }
apps/web/core/components/pages/pages-list-main-content.tsx (2)

18-24:Unused import and inconsistent import ordering.

ThecaptureSuccess import on line 18 appears to be unused after this refactor. Additionally, the new imports at lines 23-24 are placed inconsistently between existing import groups.

-import { captureClick, captureError, captureSuccess } from "@/helpers/event-tracker.helper";+import { captureClick, captureError } from "@/helpers/event-tracker.helper";+import { getUserRoleString, trackPageCreated } from "@/plane-web/helpers/event-tracker-v2.helper"; import { useProject } from "@/hooks/store/use-project"; import { useUser, useUserPermissions } from "@/hooks/store/user";+import { useWorkspace } from "@/hooks/store/use-workspace"; // 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";

67-76:Using synthetic timestamp instead of response data.

Line 70 usesnew Date().toISOString() forcreated_at instead ofres.created_at. This is inconsistent with other components in this PR that use the actual response timestamp. Using server-provided timestamps ensures accurate tracking data.

       .then((res) => {         if (currentWorkspace && currentUser && res?.id) {           const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);           trackPageCreated(-            { id: res.id, created_at: new Date().toISOString() },+            { id: res.id, created_at: res.created_at ?? new Date().toISOString() },             currentWorkspace,             currentUser,             "project",             getUserRoleString(role)           );         }
apps/web/core/components/onboarding/steps/workspace/create.tsx (1)

28-28:Import placement inconsistency.

This import is placed after the local component import, breaking the established import order. It should be grouped with the other@/plane-web/ imports near line 24-25.

-// local components-import { CommonOnboardingHeader } from "../common";-import { getUserRoleString, trackWorkspaceCreated } from "@/plane-web/helpers/event-tracker-v2.helper";+import { getUserRoleString, trackWorkspaceCreated } from "@/plane-web/helpers/event-tracker-v2.helper";+// local components+import { CommonOnboardingHeader } from "../common";
apps/web/core/components/inbox/modals/create-modal/create-root.tsx (3)

32-33:Import placement inconsistency.

These imports are placed after local component imports (lines 29-31). Group them with their respective categories:@/plane-web/ imports near line 23-25 and@/hooks/ imports near lines 16-21.

 import { useProject } from "@/hooks/store/use-project"; import { useProjectInbox } from "@/hooks/store/use-project-inbox"; import { useWorkspace } from "@/hooks/store/use-workspace";+import { useUser, useUserPermissions } from "@/hooks/store/user"; import { useAppRouter } from "@/hooks/use-app-router"; ... import { useDebouncedDuplicateIssues } from "@/plane-web/hooks/use-debounced-duplicate-issues";+import { getUserRoleString, trackWorkItemCreated } from "@/plane-web/helpers/event-tracker-v2.helper"; // services

70-74:Consolidate duplicateuseWorkspace calls.

TheuseWorkspace hook is called twice. Consolidate into a single destructure:

-const { getWorkspaceBySlug } = useWorkspace();+const { getWorkspaceBySlug, currentWorkspace } = useWorkspace(); const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id; const { isMobile } = usePlatformOS(); const { getProjectById } = useProject();-const { currentWorkspace } = useWorkspace();

165-189:Redundant optional chaining after guard.

After the guard on line 165,res.issue is guaranteed to exist. The subsequent optional chaining (res.issue?.id,res?.issue?.id) is redundant. Consider using non-null assertion or removing the chaining for clarity:

-if (!res?.issue) return;+if (!res.issue) return; if (uploadedAssetIds.length > 0) {-  await fileService.updateBulkProjectAssetsUploadStatus(workspaceSlug, projectId, res.issue?.id ?? "", {+  await fileService.updateBulkProjectAssetsUploadStatus(workspaceSlug, projectId, res.issue.id, {     asset_ids: uploadedAssetIds,   });   ... } if (!createMore) {-  router.push(`...inboxIssueId=${res.issue?.id}`);+  router.push(`...inboxIssueId=${res.issue.id}`);   ... } if (currentWorkspace && currentUser) {   ...   trackWorkItemCreated(-    { id: res?.issue?.id ?? "", created_at: res.issue?.created_at ?? "" },+    { id: res.issue.id, created_at: res.issue.created_at ?? "" },     ...   ); }
apps/web/ce/components/projects/create/root.tsx (1)

1-24:Import organization needs cleanup.

The imports are disorganized with mixed ordering patterns. Consider grouping by category:

+import { useState } from "react";+import { observer } from "mobx-react";+import { FormProvider, useForm } from "react-hook-form";+// plane imports import { PROJECT_TRACKER_EVENTS } from "@plane/constants"; import { useTranslation } from "@plane/i18n";-import { observer } from "mobx-react";-import { useState } from "react";-import { FormProvider, useForm } from "react-hook-form";-// ui import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { EFileAssetType } from "@plane/types";-// constants+// components import ProjectCommonAttributes from "@/components/project/create/common-attributes"; ... // hooks-import { getCoverImageType, uploadCoverImage } from "@/helpers/cover-image.helper";-import { captureError } from "@/helpers/event-tracker.helper"; import { useProject } from "@/hooks/store/use-project";-import { usePlatformOS } from "@/hooks/use-platform-os";-// plane web types import { useWorkspace } from "@/hooks/store/use-workspace"; import { useUser, useUserPermissions } from "@/hooks/store/user";+import { usePlatformOS } from "@/hooks/use-platform-os";+// helpers+import { getCoverImageType, uploadCoverImage } from "@/helpers/cover-image.helper";+import { captureError } from "@/helpers/event-tracker.helper";+// plane-web imports import { getUserRoleString, trackProjectCreated } from "@/plane-web/helpers/event-tracker-v2.helper";
apps/web/core/components/onboarding/create-workspace.tsx (1)

46-48:UnusedcurrentWorkspace variable.

currentWorkspace is destructured fromuseWorkspace() but never used in this component.

-  const { createWorkspace, fetchWorkspaces, currentWorkspace } = useWorkspace();+  const { createWorkspace, fetchWorkspaces } = useWorkspace();
apps/web/ce/helpers/event-tracker-v2.helper.ts (1)

5-5:ExportTUserRole type for external consumers.

TUserRole is used as a parameter type in exported functions (e.g.,trackEvent,trackWorkspaceCreated). Consumers may need to reference this type when calling these functions.

-type TUserRole = "guest" | "member" | "admin" | "unknown";+export type TUserRole = "guest" | "member" | "admin" | "unknown";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between20510bb and9941459.

📒 Files selected for processing (21)
  • apps/api/plane/app/serializers/user.py (1 hunks)
  • apps/api/plane/app/views/workspace/invite.py (4 hunks)
  • apps/api/plane/authentication/utils/workspace_project_join.py (2 hunks)
  • apps/api/plane/bgtasks/event_tracking_task.py (2 hunks)
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx (3 hunks)
  • apps/web/app/(all)/invitations/page.tsx (2 hunks)
  • apps/web/ce/components/projects/create/root.tsx (4 hunks)
  • apps/web/ce/helpers/event-tracker-v2.helper.ts (1 hunks)
  • apps/web/core/components/cycles/modal.tsx (3 hunks)
  • apps/web/core/components/inbox/modals/create-modal/create-root.tsx (3 hunks)
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (3 hunks)
  • apps/web/core/components/issues/issue-modal/base.tsx (4 hunks)
  • apps/web/core/components/onboarding/create-workspace.tsx (3 hunks)
  • apps/web/core/components/onboarding/steps/workspace/create.tsx (3 hunks)
  • apps/web/core/components/pages/modals/create-page-modal.tsx (4 hunks)
  • apps/web/core/components/pages/pages-list-main-content.tsx (3 hunks)
  • apps/web/core/components/workspace/create-workspace-form.tsx (3 hunks)
  • apps/web/core/components/workspace/delete-workspace-form.tsx (3 hunks)
  • apps/web/core/lib/posthog-provider.tsx (2 hunks)
  • apps/web/ee/helpers/event-tracker-v2.helper.ts (1 hunks)
  • packages/types/src/users.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Useconst type parameters for more precise literal inference in TypeScript 5.0+
Use thesatisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicitis return types in filter/check functions
UseNoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing inswitch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacyexperimentalDecorators
Useusing declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Usewith { type: "json" } for import attributes; avoid deprecatedassert syntax (TypeScript 5.3/5.8+)
Useimport type explicitly when importing types to ensure they are erased during compilation, respectingverbatimModuleSyntax flag
Use.ts,.mts,.cts extensions inimport type statements (TypeScript 5.2+)
Useimport type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize newSet methods likeunion,intersection, etc., when available (TypeScript 5.5+)
UseObject.groupBy /Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
UsePromise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted,toSpliced,with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields viasuper in classes (TypeScript 5....

Files:

  • apps/web/app/(all)/invitations/page.tsx
  • packages/types/src/users.ts
  • apps/web/core/components/workspace/create-workspace-form.tsx
  • apps/web/ce/components/projects/create/root.tsx
  • apps/web/core/components/inbox/modals/create-modal/create-root.tsx
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/cycles/modal.tsx
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
  • apps/web/core/components/issues/issue-modal/base.tsx
  • apps/web/ee/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
  • apps/web/core/lib/posthog-provider.tsx
  • apps/web/ce/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/onboarding/create-workspace.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/app/(all)/invitations/page.tsx
  • packages/types/src/users.ts
  • apps/web/core/components/workspace/create-workspace-form.tsx
  • apps/web/ce/components/projects/create/root.tsx
  • apps/web/core/components/inbox/modals/create-modal/create-root.tsx
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/cycles/modal.tsx
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
  • apps/web/core/components/issues/issue-modal/base.tsx
  • apps/web/ee/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
  • apps/web/core/lib/posthog-provider.tsx
  • apps/web/ce/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/onboarding/create-workspace.tsx
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, runpnpm fix:format

Files:

  • apps/web/app/(all)/invitations/page.tsx
  • packages/types/src/users.ts
  • apps/web/core/components/workspace/create-workspace-form.tsx
  • apps/web/ce/components/projects/create/root.tsx
  • apps/web/core/components/inbox/modals/create-modal/create-root.tsx
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/cycles/modal.tsx
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
  • apps/web/core/components/issues/issue-modal/base.tsx
  • apps/web/ee/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
  • apps/web/core/lib/posthog-provider.tsx
  • apps/web/ce/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/onboarding/create-workspace.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/app/(all)/invitations/page.tsx
  • packages/types/src/users.ts
  • apps/web/core/components/workspace/create-workspace-form.tsx
  • apps/web/ce/components/projects/create/root.tsx
  • apps/web/core/components/inbox/modals/create-modal/create-root.tsx
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/cycles/modal.tsx
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
  • apps/web/core/components/issues/issue-modal/base.tsx
  • apps/web/ee/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
  • apps/web/core/lib/posthog-provider.tsx
  • apps/web/ce/helpers/event-tracker-v2.helper.ts
  • apps/web/core/components/onboarding/create-workspace.tsx
🧠 Learnings (8)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7989File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46Timestamp: 2025-10-21T17:22:05.204ZLearning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/workspace/create-workspace-form.tsx
  • apps/web/ce/components/projects/create/root.tsx
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
  • apps/web/core/lib/posthog-provider.tsx
📚 Learning: 2025-09-02T08:14:49.260Z
Learnt from: sriramveeraghantaRepo: makeplane/plane PR: 7697File: apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx:12-13Timestamp: 2025-09-02T08:14:49.260ZLearning: The star-us-link.tsx file in apps/web/app/(all)/[workspaceSlug]/(projects)/ already has "use client" directive at the top, making it a proper Client Component for hook usage.

Applied to files:

  • apps/web/core/components/workspace/create-workspace-form.tsx
  • apps/web/core/components/inbox/modals/create-modal/create-root.tsx
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
  • apps/web/core/components/onboarding/create-workspace.tsx
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisettiRepo: makeplane/plane PR: 7460File: apps/api/plane/app/serializers/draft.py:112-122Timestamp: 2025-07-23T18:18:06.875ZLearning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.

Applied to files:

  • apps/api/plane/authentication/utils/workspace_project_join.py
  • apps/web/ce/components/projects/create/root.tsx
  • apps/api/plane/app/views/workspace/invite.py
  • apps/web/core/components/issues/issue-modal/base.tsx
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7922File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19Timestamp: 2025-10-09T20:42:31.843ZLearning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • apps/web/ce/components/projects/create/root.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
  • apps/web/core/lib/posthog-provider.tsx
📚 Learning: 2025-10-09T22:12:26.424Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7922File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19Timestamp: 2025-10-09T22:12:26.424ZLearning: When `types/react` is installed in a TypeScript project (which is standard for React + TypeScript codebases), React types (React.FC, React.ReactNode, React.ComponentProps, etc.) are globally available by design. These type annotations can and should be used without explicitly importing the React namespace. This is a TypeScript/DefinitelyTyped feature, not codebase-specific configuration.

Applied to files:

  • apps/web/core/components/pages/modals/create-page-modal.tsx
📚 Learning: 2025-12-12T15:20:36.519Z
Learnt from: CRRepo: makeplane/plane PR: 0File: AGENTS.md:0-0Timestamp: 2025-12-12T15:20:36.519ZLearning: Applies to packages/shared-state/**/*.{ts,tsx} : Maintain MobX stores in `packages/shared-state` using reactive patterns

Applied to files:

  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx
📚 Learning: 2025-10-10T13:25:14.810Z
Learnt from: gakshitaRepo: makeplane/plane PR: 7949File: apps/web/core/components/issues/issue-modal/form.tsx:183-189Timestamp: 2025-10-10T13:25:14.810ZLearning: In `apps/web/core/components/issues/issue-modal/form.tsx`, the form reset effect uses a `dataResetProperties` dependency array prop (default: []) to give parent components explicit control over when the form resets. Do not suggest adding the `data` prop itself to the dependency array, as this would cause unwanted resets on every render when the data object reference changes, disrupting user input. The current pattern is intentional and allows the parent to trigger resets only when specific conditions are met.

Applied to files:

  • apps/web/core/components/issues/issue-modal/base.tsx
📚 Learning: 2025-12-12T15:20:36.519Z
Learnt from: CRRepo: makeplane/plane PR: 0File: AGENTS.md:0-0Timestamp: 2025-12-12T15:20:36.519ZLearning: Applies to **/package.json : Use `workspace:*` for internal packages and `catalog:` for external dependencies in imports

Applied to files:

  • apps/web/core/components/workspace/delete-workspace-form.tsx
🧬 Code graph analysis (14)
apps/web/app/(all)/invitations/page.tsx (1)
apps/web/ce/helpers/event-tracker-v2.helper.ts (1)
  • joinWorkspaceGroup (83-96)
apps/api/plane/authentication/utils/workspace_project_join.py (2)
apps/api/plane/db/models/workspace.py (2)
  • WorkspaceMember (194-224)
  • WorkspaceMemberInvite (227-251)
apps/api/plane/bgtasks/event_tracking_task.py (1)
  • track_event (48-67)
apps/web/ce/components/projects/create/root.tsx (3)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackProjectCreated (174-191)
  • getUserRoleString (19-33)
apps/web/core/components/inbox/modals/create-modal/create-root.tsx (3)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkItemCreated (196-216)
  • getUserRoleString (19-33)
apps/web/core/components/onboarding/steps/workspace/create.tsx (2)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkspaceCreated (134-147)
  • getUserRoleString (19-33)
apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (4)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkItemCreated (196-216)
  • getUserRoleString (19-33)
packages/constants/src/event-tracker/core.ts (1)
  • WORK_ITEM_TRACKER_EVENTS (127-148)
apps/web/core/components/pages/modals/create-page-modal.tsx (4)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackPageCreated (246-266)
  • getUserRoleString (19-33)
packages/propel/src/toast/toast.tsx (1)
  • setToast (202-222)
apps/api/plane/bgtasks/event_tracking_task.py (2)
apps/api/plane/db/models/workspace.py (1)
  • Workspace (115-178)
apps/api/plane/utils/exception_logger.py (1)
  • log_exception (9-20)
apps/web/core/components/cycles/modal.tsx (3)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackCycleCreated (221-241)
  • getUserRoleString (19-33)
apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx (6)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackPageCreated (246-266)
  • getUserRoleString (19-33)
apps/space/core/store/publish/publish.store.ts (1)
  • workspaceSlug (93-95)
apps/web/core/store/project/project.store.ts (1)
  • currentProjectDetails (214-217)
packages/constants/src/event-tracker/core.ts (1)
  • PROJECT_PAGE_TRACKER_EVENTS (205-218)
apps/web/core/components/pages/pages-list-main-content.tsx (3)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackPageCreated (246-266)
  • getUserRoleString (19-33)
apps/web/core/components/workspace/delete-workspace-form.tsx (1)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkspaceDeleted (152-163)
  • getUserRoleString (19-33)
apps/web/ce/helpers/event-tracker-v2.helper.ts (3)
packages/types/src/users.ts (2)
  • IUser (29-48)
  • TUserProfile (57-84)
packages/services/src/user/user.service.ts (1)
  • profile (52-58)
packages/types/src/workspace.ts (1)
  • IWorkspace (15-31)
apps/web/core/components/onboarding/create-workspace.tsx (2)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkspaceCreated (134-147)
  • getUserRoleString (19-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build packages
🔇 Additional comments (11)
packages/types/src/users.ts (1)

29-48:IUser:last_login_time addition looks consistent

last_login_time: string | null aligns with existing date fields (e.g.,date_joined: string) and the serializer exposure; no issues from a typing standpoint.

apps/web/ee/helpers/event-tracker-v2.helper.ts (1)

1-1:Thin re-export correctly mirrors CE event tracker helper

Re-exporting from"ce/helpers/event-tracker-v2.helper" gives EE code the same API surface as CE without duplication; this is a good pattern for keeping the helpers in sync.

apps/web/core/lib/posthog-provider.tsx (1)

4-6:PostHog identity & workspace grouping are wired correctly with v2 helpers

The new effects that:

  • callidentifyUser(user, profile) whenuser && profile && hydrated && is_posthog_enabled, and
  • calljoinWorkspaceGroup(currentWorkspace) whencurrentWorkspace && hydrated && is_posthog_enabled

are correctly gated on both hydration and telemetry configuration, so they won’t fire before PostHog is initialized. The use ofimport type { ReactNode } here also matches the TypeScript type-only import guideline.

Also applies to: 8-13, 23-28, 36-47

apps/web/core/components/issues/issue-modal/base.tsx (1)

249-258:LGTM!

The work item creation tracking is well-guarded with proper null checks forcurrentWorkspace,currentUser, andresponse. Usingresponse.created_at from the actual API response is the correct approach for accurate tracking.

apps/web/ce/components/projects/create/root.tsx (1)

106-126:LGTM!

The event tracking integration is well-guarded with proper null checks forcurrentUser,currentWorkspace, andres. The tracking call is correctly positioned after successful project creation and before the success toast.

apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx (1)

40-81:Good refactor to async/await pattern.

The conversion from promise chaining to async/await with try/catch/finally is clean. Thefinally block properly ensures the loading state is reset regardless of success or failure.

apps/web/core/components/onboarding/create-workspace.tsx (1)

82-89:Role lookup on newly created workspace may returnundefined.

getWorkspaceRoleByWorkspaceSlug(workspaceResponse.slug) is called immediately after workspace creation, but user permissions for the new workspace may not yet be in the store. This would result inrole beingundefined, causinggetUserRoleString to return"unknown".

Since the creator is always the owner of a newly created workspace, consider passing a hardcoded owner/admin role for workspace creation events:

              if (currentUser) {-               const role = getWorkspaceRoleByWorkspaceSlug(workspaceResponse.slug);                trackWorkspaceCreated(                  workspaceResponse,                  currentUser,-                 getUserRoleString(role),+                 "admin", // Creator is always the workspace owner/admin                );              }

Alternatively, verify thatfetchWorkspaces() (or a permissions refresh) is called before the role lookup, and thatgetWorkspaceRoleByWorkspaceSlug returns a valid role for newly created workspaces.

apps/web/ce/helpers/event-tracker-v2.helper.ts (4)

49-68:Verify PII handling compliance for user email in analytics.

identifyUser sendsuser.email to PostHog (line 54). Ensure this aligns with your privacy policy and data handling requirements (GDPR, CCPA). If email tracking is intentional for product analytics, this is acceptable; otherwise, consider using a hashed identifier or omitting it.


83-96:LGTM on workspace group tracking.

Good defensive checks forworkspace.slug and handling of optionalcreated_at date conversion. The fallbackworkspace.owner?.id || workspace.created_by reasonably handles cases where the full owner object isn't populated.


113-122:LGTM on generic event tracking.

Clean implementation with proper property spreading and role inclusion. The default"unknown" fallback for role handles edge cases appropriately.


134-147:Well-structured lifecycle and activation event trackers.

The tracking functions follow a consistent pattern with proper date serialization and null handling. Good separation of concerns with dedicated functions for each entity type.

Also applies to: 152-163, 174-191, 196-216, 221-241, 246-266

Comment on lines +1 to +3
# Django imports
fromdjango.utilsimporttimezone

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

⚠️ Potential issue |🟠 Major

Align event name with other join events and avoid list comprehension for side effects

Two things here:

  1. Event name inconsistency

Other join flows useevent_name="user_joined_workspace" (singular), but this path uses"user_joined_workspaces":

track_event.delay(user_id=user.id,event_name="user_joined_workspaces",# plural    ...)

Unless you explicitly want adifferent event type for this code path, this will fragment analytics. To keep queries consistent, consider:

-            event_name="user_joined_workspaces",+            event_name="user_joined_workspace",
  1. Avoid list comprehension for side effects

The comprehension:

[track_event.delay(...)forworkspace_member_inviteinworkspace_member_invites]

creates an unused list purely for side effects. A plain loop is clearer and avoids unnecessary allocation:

-    [-        track_event.delay(-            user_id=user.id,-            event_name="user_joined_workspace",-            slug=workspace_member_invite.workspace.slug,-            event_properties={-                "user_id": user.id,-                "workspace_id": workspace_member_invite.workspace.id,-                "workspace_slug": workspace_member_invite.workspace.slug,-                "role": workspace_member_invite.role,-                "joined_at": str(timezone.now()),-            },-        )-        for workspace_member_invite in workspace_member_invites-    ]+    for workspace_member_invite in workspace_member_invites:+        track_event.delay(+            user_id=user.id,+            event_name="user_joined_workspace",+            slug=workspace_member_invite.workspace.slug,+            event_properties={+                "user_id": user.id,+                "workspace_id": workspace_member_invite.workspace.id,+                "workspace_slug": workspace_member_invite.workspace.slug,+                "role": workspace_member_invite.role,+                "joined_at": timezone.now().isoformat(),+            },+        )

(The.isoformat() change is optional but recommended for consistent timestamps.)

Also applies to: 12-12, 33-47

🤖 Prompt for AI Agents
In apps/api/plane/authentication/utils/workspace_project_join.py lines 1-3 (andalso apply to occurrences at 12-12 and 33-47), change the event_name from"user_joined_workspaces" to "user_joined_workspace" to match other join events,and replace the list comprehension used solely for side effects with an explicitfor-loop that calls track_event.delay(...) for each workspace_member_invite;optionally ensure any timestamp passed is converted with .isoformat() forconsistency.

Comment on lines +33 to +44
defpreprocess_data_properties(
user_id:uuid.UUID,event_name:str,slug:str,data_properties:Dict[str,Any]
)->Dict[str,Any]:
ifevent_name=="user_invited_to_workspace":
# Check if the current user is the workspace owner
workspace=Workspace.objects.get(slug=slug)
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"

returndata_properties
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

⚠️ Potential issue |🟠 Major

UnhandledWorkspace.DoesNotExist exception can break event tracking.

Workspace.objects.get(slug=slug) will raiseWorkspace.DoesNotExist if the workspace doesn't exist (e.g., deleted or invalid slug). This exception propagates up totrack_event, where it's caught but causes the entire event to fail silently.

Consider usingfilter().first() with a null check:

 def preprocess_data_properties(     user_id: uuid.UUID, event_name: str, slug: str, data_properties: Dict[str, Any] ) -> Dict[str, Any]:     if event_name == "user_invited_to_workspace":-        # Check if the current user is the workspace owner-        workspace = Workspace.objects.get(slug=slug)-        if str(workspace.owner_id) == str(user_id):-            data_properties["role"] = "owner"-        else:-            data_properties["role"] = "admin"+        workspace = Workspace.objects.filter(slug=slug).first()+        if workspace:+            if str(workspace.owner_id) == str(user_id):+                data_properties["role"] = "owner"+            else:+                data_properties["role"] = "admin"+        else:+            data_properties["role"] = "unknown"      return data_properties
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
defpreprocess_data_properties(
user_id:uuid.UUID,event_name:str,slug:str,data_properties:Dict[str,Any]
)->Dict[str,Any]:
ifevent_name=="user_invited_to_workspace":
# Check if the current user is the workspace owner
workspace=Workspace.objects.get(slug=slug)
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"
returndata_properties
defpreprocess_data_properties(
user_id:uuid.UUID,event_name:str,slug:str,data_properties:Dict[str,Any]
)->Dict[str,Any]:
ifevent_name=="user_invited_to_workspace":
workspace=Workspace.objects.filter(slug=slug).first()
ifworkspace:
ifstr(workspace.owner_id)==str(user_id):
data_properties["role"]="owner"
else:
data_properties["role"]="admin"
else:
data_properties["role"]="unknown"
returndata_properties
🤖 Prompt for AI Agents
In apps/api/plane/bgtasks/event_tracking_task.py around lines 33-44, replace thedirect Workspace.objects.get(slug=slug) call withWorkspace.objects.filter(slug=slug).first() and add a null check so the codedoes not raise Workspace.DoesNotExist; if workspace is None, leavedata_properties unchanged (or set data_properties["role"]="unknown" if a role isrequired), otherwise perform the owner-id comparison and set "role" accordingly;this prevents an exception from propagating while preserving the intended roleassignment when the workspace exists.

Comment on lines 47 to +67
@shared_task
defauth_events(user,email,user_agent,ip,event_name,medium,first_time):
try:
POSTHOG_API_KEY,POSTHOG_HOST=posthogConfiguration()
deftrack_event(user_id:uuid.UUID,event_name:str,slug:str,event_properties:Dict[str,Any]):
POSTHOG_API_KEY,POSTHOG_HOST=posthogConfiguration()

ifPOSTHOG_API_KEYandPOSTHOG_HOST:
posthog=Posthog(POSTHOG_API_KEY,host=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,
},
)
ifnot (POSTHOG_API_KEYandPOSTHOG_HOST):
logger.warning("Event tracking is not configured")
return

try:
# preprocess the data properties for massaging the payload
# in the correct format for posthog
data_properties=preprocess_data_properties(user_id,event_name,slug,event_properties)
groups= {
"workspace":slug,
}
# track the event using posthog
posthog=Posthog(POSTHOG_API_KEY,host=POSTHOG_HOST)
posthog.capture(distinct_id=user_id,event=event_name,properties=data_properties,groups=groups)
exceptExceptionase:
log_exception(e)
return
returnFalse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

⚠️ Potential issue |🟡 Minor

Type mismatch and inconsistent return value.

Two issues in thetrack_event function:

  1. UUID type:user_id is typed asuuid.UUID, butposthog.capture(distinct_id=...) typically expects a string. Ensure explicit conversion to avoid serialization issues.

  2. Inconsistent return: ReturnsFalse on exception (line 67) but returnsNone implicitly on success or when tracking is disabled. Consider consistent return behavior.

 @shared_task-def track_event(user_id: uuid.UUID, event_name: str, slug: str, event_properties: Dict[str, Any]):+def track_event(user_id: uuid.UUID, event_name: str, slug: str, event_properties: Dict[str, Any]) -> bool:     POSTHOG_API_KEY, POSTHOG_HOST = posthogConfiguration()      if not (POSTHOG_API_KEY and POSTHOG_HOST):         logger.warning("Event tracking is not configured")-        return+        return False      try:         data_properties = preprocess_data_properties(user_id, event_name, slug, event_properties)         groups = {             "workspace": slug,         }         posthog = Posthog(POSTHOG_API_KEY, host=POSTHOG_HOST)-        posthog.capture(distinct_id=user_id, event=event_name, properties=data_properties, groups=groups)+        posthog.capture(distinct_id=str(user_id), event=event_name, properties=data_properties, groups=groups)+        return True     except Exception as e:         log_exception(e)         return False
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@shared_task
defauth_events(user,email,user_agent,ip,event_name,medium,first_time):
try:
POSTHOG_API_KEY,POSTHOG_HOST=posthogConfiguration()
deftrack_event(user_id:uuid.UUID,event_name:str,slug:str,event_properties:Dict[str,Any]):
POSTHOG_API_KEY,POSTHOG_HOST=posthogConfiguration()
ifPOSTHOG_API_KEYandPOSTHOG_HOST:
posthog=Posthog(POSTHOG_API_KEY,host=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,
},
)
ifnot (POSTHOG_API_KEYandPOSTHOG_HOST):
logger.warning("Event tracking is not configured")
return
try:
# preprocess the data properties for massaging the payload
# in the correct format for posthog
data_properties=preprocess_data_properties(user_id,event_name,slug,event_properties)
groups= {
"workspace":slug,
}
# track the event using posthog
posthog=Posthog(POSTHOG_API_KEY,host=POSTHOG_HOST)
posthog.capture(distinct_id=user_id,event=event_name,properties=data_properties,groups=groups)
exceptExceptionase:
log_exception(e)
return
returnFalse
@shared_task
deftrack_event(user_id:uuid.UUID,event_name:str,slug:str,event_properties:Dict[str,Any])->bool:
POSTHOG_API_KEY,POSTHOG_HOST=posthogConfiguration()
ifnot (POSTHOG_API_KEYandPOSTHOG_HOST):
logger.warning("Event tracking is not configured")
returnFalse
try:
# preprocess the data properties for massaging the payload
# in the correct format for posthog
data_properties=preprocess_data_properties(user_id,event_name,slug,event_properties)
groups= {
"workspace":slug,
}
# track the event using posthog
posthog=Posthog(POSTHOG_API_KEY,host=POSTHOG_HOST)
posthog.capture(distinct_id=str(user_id),event=event_name,properties=data_properties,groups=groups)
returnTrue
exceptExceptionase:
log_exception(e)
returnFalse
🤖 Prompt for AI Agents
In apps/api/plane/bgtasks/event_tracking_task.py around lines 47 to 67, convertuser_id to a string before passing it to posthog.capture (e.g.,distinct_id=str(user_id)) to avoid UUID serialization issues, and make thefunction return consistently (return True on successful capture and return Falseboth when tracking is disabled and when an exception occurs) so callers receivea boolean success indicator.

…nd issue creation, and reorganize imports in workspace components
Copy link
Contributor

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
apps/web/core/components/workspace/delete-workspace-form.tsx (1)

70-75:Potential timing issue: role lookup after workspace deletion.

getWorkspaceRoleByWorkspaceSlug(data.slug) is called after the workspace is deleted. The user's permissions for this workspace may already be cleared from the store, causing the role to beundefined and tracked as"unknown".

Capture the role before deletion:

 const onSubmit = async () => {   if (!data || !canDelete) return;+  // Capture role before deletion as permissions will be cleared+  const role = getWorkspaceRoleByWorkspaceSlug(data.slug);+   await deleteWorkspace(data.slug)     .then(async () => {       await fetchCurrentUserSettings();       handleClose();       router.push(getWorkspaceRedirectionUrl());-      const role = getWorkspaceRoleByWorkspaceSlug(data.slug);-       if (currentUser) {         trackWorkspaceDeleted(data, currentUser, getUserRoleString(role));       }
apps/web/core/components/pages/modals/create-page-modal.tsx (1)

74-87:Empty string fallbacks for tracking data remain unaddressed.

The previous review comment flaggingpageData.id ?? "" andpageData.created_at ?? "" as potentially sending invalid tracking data still applies. Consider guarding withpageData.id check and using a meaningful fallback forcreated_at.

apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (1)

145-145:Consider using a valid fallback timestamp instead of empty string.

Using an empty string as the fallback forcreated_at will result in an invalid timestamp being tracked. A client-side timestamp would be more appropriate as a fallback.

-            { id: quickAddRes.id, created_at: quickAddRes.created_at ?? "" },+            { id: quickAddRes.id, created_at: quickAddRes.created_at ?? new Date().toISOString() },
apps/web/core/components/cycles/modal.tsx (1)

72-81:UseselectedProjectId in tracking to match the actual cycle's project.

The tracking call usesprojectId from props, but if the user selects a different project in the form, the cycle will be created inselectedProjectId (line 54) while analytics records the originalprojectId. This creates incorrect analytics data.

Apply this diff:

         if (currentWorkspace && currentUser) {           const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);           trackCycleCreated(             { id: res.id, created_at: res?.created_at ?? "" },-            { id: projectId },+            { id: selectedProjectId },             currentWorkspace,             currentUser,             getUserRoleString(role)           );         }
🧹 Nitpick comments (1)
apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (1)

14-22:Import ordering is inconsistent.

The@/plane-web/helpers/... and@/hooks/store/... imports on lines 20-22 are placed after the local relative import on line 19. Consider grouping aliased path imports (@/...) before local relative imports (./...) for consistency.

 // helpers import { captureError } from "@/helpers/event-tracker.helper";+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"; // plane web imports 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";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between9941459 and1f89fcd.

📒 Files selected for processing (5)
  • apps/web/core/components/cycles/modal.tsx (3 hunks)
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (3 hunks)
  • apps/web/core/components/onboarding/steps/workspace/create.tsx (4 hunks)
  • apps/web/core/components/pages/modals/create-page-modal.tsx (4 hunks)
  • apps/web/core/components/workspace/delete-workspace-form.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/core/components/onboarding/steps/workspace/create.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Useconst type parameters for more precise literal inference in TypeScript 5.0+
Use thesatisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicitis return types in filter/check functions
UseNoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing inswitch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacyexperimentalDecorators
Useusing declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Usewith { type: "json" } for import attributes; avoid deprecatedassert syntax (TypeScript 5.3/5.8+)
Useimport type explicitly when importing types to ensure they are erased during compilation, respectingverbatimModuleSyntax flag
Use.ts,.mts,.cts extensions inimport type statements (TypeScript 5.2+)
Useimport type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize newSet methods likeunion,intersection, etc., when available (TypeScript 5.5+)
UseObject.groupBy /Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
UsePromise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted,toSpliced,with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields viasuper in classes (TypeScript 5....

Files:

  • apps/web/core/components/cycles/modal.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/core/components/cycles/modal.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, runpnpm fix:format

Files:

  • apps/web/core/components/cycles/modal.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/core/components/cycles/modal.tsx
  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/issues/issue-layouts/quick-add/root.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
🧠 Learnings (8)
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisettiRepo: makeplane/plane PR: 7460File: apps/api/plane/app/serializers/draft.py:112-122Timestamp: 2025-07-23T18:18:06.875ZLearning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.

Applied to files:

  • apps/web/core/components/cycles/modal.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
📚 Learning: 2025-06-16T07:23:39.497Z
Learnt from: vamsikrishnamathalaRepo: makeplane/plane PR: 7214File: web/core/store/issue/helpers/base-issues.store.ts:117-117Timestamp: 2025-06-16T07:23:39.497ZLearning: In the updateIssueDates method of BaseIssuesStore (web/core/store/issue/helpers/base-issues.store.ts), the projectId parameter is intentionally made optional to support override implementations in subclasses. The base implementation requires projectId and includes an early return check, but making it optional allows derived classes to override the method with different parameter requirements.

Applied to files:

  • apps/web/core/components/cycles/modal.tsx
📚 Learning: 2025-06-18T09:46:08.566Z
Learnt from: prateekshourya29Repo: makeplane/plane PR: 7188File: web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/header.tsx:40-45Timestamp: 2025-06-18T09:46:08.566ZLearning: When reviewing breadcrumb components that accept projectId or similar props, check if empty strings are being passed during loading states, which can result in invalid URLs. The preferred approach is to handle these loading states internally within the component rather than requiring each consumer to manage the loading logic.

Applied to files:

  • apps/web/core/components/pages/modals/create-page-modal.tsx
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7922File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19Timestamp: 2025-10-09T20:42:31.843ZLearning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7989File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46Timestamp: 2025-10-21T17:22:05.204ZLearning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/pages/modals/create-page-modal.tsx
  • apps/web/core/components/workspace/delete-workspace-form.tsx
📚 Learning: 2025-10-09T22:12:26.424Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7922File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19Timestamp: 2025-10-09T22:12:26.424ZLearning: When `types/react` is installed in a TypeScript project (which is standard for React + TypeScript codebases), React types (React.FC, React.ReactNode, React.ComponentProps, etc.) are globally available by design. These type annotations can and should be used without explicitly importing the React namespace. This is a TypeScript/DefinitelyTyped feature, not codebase-specific configuration.

Applied to files:

  • apps/web/core/components/pages/modals/create-page-modal.tsx
📚 Learning: 2025-11-25T10:18:05.172Z
Learnt from: CRRepo: makeplane/plane PR: 0File: .github/instructions/typescript.instructions.md:0-0Timestamp: 2025-11-25T10:18:05.172ZLearning: Applies to **/*.{ts,tsx,mts,cts} : Use `using` declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)

Applied to files:

  • apps/web/core/components/workspace/delete-workspace-form.tsx
📚 Learning: 2025-09-02T08:14:49.260Z
Learnt from: sriramveeraghantaRepo: makeplane/plane PR: 7697File: apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx:12-13Timestamp: 2025-09-02T08:14:49.260ZLearning: The star-us-link.tsx file in apps/web/app/(all)/[workspaceSlug]/(projects)/ already has "use client" directive at the top, making it a proper Client Component for hook usage.

Applied to files:

  • apps/web/core/components/workspace/delete-workspace-form.tsx
🧬 Code graph analysis (4)
apps/web/core/components/cycles/modal.tsx (3)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackCycleCreated (221-241)
  • getUserRoleString (19-33)
apps/web/core/components/pages/modals/create-page-modal.tsx (4)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackPageCreated (246-266)
  • getUserRoleString (19-33)
packages/propel/src/toast/toast.tsx (1)
  • setToast (202-222)
apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (4)
apps/admin/core/hooks/store/use-workspace.tsx (1)
  • useWorkspace (6-10)
apps/web/core/store/workspace/index.ts (1)
  • currentWorkspace (125-130)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkItemCreated (196-216)
  • getUserRoleString (19-33)
packages/constants/src/event-tracker/core.ts (1)
  • WORK_ITEM_TRACKER_EVENTS (127-148)
apps/web/core/components/workspace/delete-workspace-form.tsx (1)
apps/web/ce/helpers/event-tracker-v2.helper.ts (2)
  • trackWorkspaceDeleted (152-163)
  • getUserRoleString (19-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: check:types
  • GitHub Check: check:lint
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (7)
apps/web/core/components/workspace/delete-workspace-form.tsx (2)

1-18:LGTM!

Import changes are clean. The unusedcaptureSuccess import has been removed, and all new imports (useUser,useUserPermissions,getUserRoleString,trackWorkspaceDeleted,cn) are properly utilized in the component.


39-41:LGTM!

Hook declarations are properly added for the new tracking functionality.

apps/web/core/components/pages/modals/create-page-modal.tsx (2)

51-53:LGTM!

The hook additions foruseUserPermissions,useUser, anduseWorkspace are correctly placed and provide the necessary context for role-aware event tracking.


96-100:Good addition of user feedback for error cases.

The toast notification provides clear user feedback when page creation fails, with a sensible fallback message.

apps/web/core/components/issues/issue-layouts/quick-add/root.tsx (1)

73-77:LGTM!

The store hooks are correctly placed at the component's top level and properly destructured for use in the event tracking logic.

apps/web/core/components/cycles/modal.tsx (2)

21-23:LGTM - Tracking imports are properly structured.

The new imports for user context, workspace context, and tracking utilities are appropriate for implementing the event tracking functionality.


45-47:LGTM - Hook usage follows React patterns.

The workspace, user, and permissions hooks are correctly initialized to provide context for tracking.

@JayashTripathyJayashTripathy changed the titlerefactor: add new event trackers[WEB-5681] refactor: add new event trackersDec 16, 2025
@makeplane
Copy link

Linked to Plane Work Item(s)

This comment was auto-generated byPlane

Copy link
Contributor

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/core/components/pages/pages-list-main-content.tsx (1)

67-76:Wrap tracking call in try-catch to isolate it from the success flow.

ThetrackPageCreated call can interrupt the page creation success flow if it throws. WhilegetUserRoleString safely handles undefined roles andtrackPageCreated is implemented defensively, wrapping the tracking logic in a try-catch prevents any potential errors from blocking navigation to the newly created page.

 if (currentWorkspace && currentUser && res?.id) {-  const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);-  trackPageCreated(-    { id: res.id, created_at: res.created_at ?? "" },-    currentWorkspace,-    currentUser,-    "project",-    getUserRoleString(role)-  );+  try {+    const role = getWorkspaceRoleByWorkspaceSlug(currentWorkspace.slug);+    trackPageCreated(+      { id: res.id, created_at: res.created_at ?? "" },+      currentWorkspace,+      currentUser,+      "project",+      getUserRoleString(role)+    );+  } catch (error) {+    console.error("Failed to track page creation:", error);+  } }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between1f89fcd and5e2f83f.

📒 Files selected for processing (1)
  • apps/web/core/components/pages/pages-list-main-content.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Useconst type parameters for more precise literal inference in TypeScript 5.0+
Use thesatisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicitis return types in filter/check functions
UseNoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing inswitch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacyexperimentalDecorators
Useusing declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Usewith { type: "json" } for import attributes; avoid deprecatedassert syntax (TypeScript 5.3/5.8+)
Useimport type explicitly when importing types to ensure they are erased during compilation, respectingverbatimModuleSyntax flag
Use.ts,.mts,.cts extensions inimport type statements (TypeScript 5.2+)
Useimport type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize newSet methods likeunion,intersection, etc., when available (TypeScript 5.5+)
UseObject.groupBy /Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
UsePromise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted,toSpliced,with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields viasuper in classes (TypeScript 5....

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, runpnpm fix:format

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontentRepo: makeplane/plane PR: 7989File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46Timestamp: 2025-10-21T17:22:05.204ZLearning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
📚 Learning: 2025-09-02T08:14:49.260Z
Learnt from: sriramveeraghantaRepo: makeplane/plane PR: 7697File: apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx:12-13Timestamp: 2025-09-02T08:14:49.260ZLearning: The star-us-link.tsx file in apps/web/app/(all)/[workspaceSlug]/(projects)/ already has "use client" directive at the top, making it a proper Client Component for hook usage.

Applied to files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: check:lint
  • GitHub Check: check:types
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/web/core/components/pages/pages-list-main-content.tsx (2)

20-24:LGTM! Clean import additions.

The new imports for user context, permissions, and event tracking helpers are properly structured and align with the PR's objective to add event tracking capabilities.


40-43:LGTM! Proper hook usage.

The hooks are correctly called at the top level and follow standard React patterns. The retrieved user and workspace context will enable conditional event tracking.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@coderabbitaicoderabbitai[bot]coderabbitai[bot] left review comments

Copilot code reviewCopilotCopilot left review comments

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@JayashTripathy@sriramveeraghanta@pablohashescobar

[8]ページ先頭

©2009-2025 Movatter.jp