chore: added issue open related events

This commit is contained in:
LAKHAN BAHETI 2024-02-29 19:55:32 +05:30
parent b7aeb0f70d
commit d263335a29
21 changed files with 98 additions and 83 deletions

View File

@ -2,7 +2,7 @@ import { useRouter } from "next/router";
import { useEffect } from "react";
import { observer } from "mobx-react-lite";
// store hooks
import { useEstimate, useLabel } from "hooks/store";
import { useEstimate, useEventTracker, useLabel } from "hooks/store";
// icons
import { Tooltip, BlockedIcon, BlockerIcon, RelatedIcon, LayersIcon, DiceIcon } from "@plane/ui";
import {
@ -25,10 +25,14 @@ import { renderFormattedDate } from "helpers/date-time.helper";
import { capitalizeFirstLetter } from "helpers/string.helper";
// types
import { IIssueActivity } from "@plane/types";
// constants
import { ISSUE_OPENED, elementFromPath } from "constants/event-tracker";
export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
// store hooks
const { captureEvent } = useEventTracker();
return (
<Tooltip tooltipContent={activity?.issue_detail ? activity.issue_detail.name : "This issue has been deleted"}>
@ -38,6 +42,13 @@ export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
href={`${`/${workspaceSlug ?? activity.workspace_detail?.slug}/projects/${activity.project}/issues/${
activity.issue
}`}`}
onClick={() => {
captureEvent(ISSUE_OPENED, {
...elementFromPath(router.asPath),
element_id: "activity",
mode: "detail",
});
}}
target={activity.issue === null ? "_self" : "_blank"}
rel={activity.issue === null ? "" : "noopener noreferrer"}
className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline"

View File

@ -1,6 +1,6 @@
import Link from "next/link";
// hooks
import { useIssueDetail } from "hooks/store";
import { useEventTracker, useIssueDetail } from "hooks/store";
// components
import {
AssignedCompletedIssueListItem,
@ -20,6 +20,8 @@ import { cn } from "helpers/common.helper";
import { getRedirectionFilters } from "helpers/dashboard.helper";
// types
import { TAssignedIssuesWidgetResponse, TCreatedIssuesWidgetResponse, TIssue, TIssuesListTypes } from "@plane/types";
// constants
import { ISSUE_OPENED } from "constants/event-tracker";
export type WidgetIssuesListProps = {
isLoading: boolean;
@ -33,9 +35,19 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
const { isLoading, tab, type, widgetStats, workspaceSlug } = props;
// store hooks
const { setPeekIssue } = useIssueDetail();
const { captureEvent } = useEventTracker();
const handleIssuePeekOverview = (issue: TIssue) =>
const handleIssuePeekOverview = (issue: TIssue) => {
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
captureEvent(ISSUE_OPENED, {
element: "Dashboard",
element_id: tab,
mode: "peek",
filters: {
due_date: issue.target_date,
},
});
};
const filterParams = getRedirectionFilters(tab);

View File

@ -111,8 +111,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, cycleId).then(() =>
captureEvent(LAYOUT_CHANGED, {
layout: layout,
element: elementFromPath(router.asPath),
element_id: cycleId,
...elementFromPath(router.asPath),
})
);
},
@ -142,7 +141,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
payload: {
path: router.asPath,
filters: issueFilters,
element_id: cycleId,
filter_property: value,
filter_type: key,
},
@ -164,7 +162,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
property: Object.values(updatedDisplayFilter)?.[0],
path: router.asPath,
filters: issueFilters,
element_id: cycleId,
},
})
);
@ -182,7 +179,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
display_property: Object.keys(property).join(","),
path: router.asPath,
filters: issueFilters,
element_id: cycleId,
},
})
);
@ -293,7 +289,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
path: router.asPath,
current_filters: issueFilters?.filters,
layout: issueFilters?.displayFilters?.layout,
element_id: cycleId,
},
})
}

View File

@ -88,7 +88,6 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
payload: {
path: router.asPath,
filters: issueFilters,
element_id: globalViewId,
filter_property: value,
filter_type: key,
},
@ -115,7 +114,6 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
property: Object.values(updatedDisplayFilter)?.[0],
path: router.asPath,
filters: issueFilters,
element_id: globalViewId,
},
})
);
@ -139,7 +137,6 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
display_property: Object.keys(property).join(","),
path: router.asPath,
filters: issueFilters,
element_id: globalViewId,
},
})
);
@ -212,7 +209,6 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
path: router.asPath,
current_filters: issueFilters?.filters,
layout: issueFilters?.displayFilters?.layout,
element_id: globalViewId,
},
})
}

View File

@ -114,8 +114,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, moduleId).then(() =>
captureEvent(LAYOUT_CHANGED, {
layout: layout,
element: elementFromPath(router.asPath),
element_id: moduleId,
...elementFromPath(router.asPath),
})
);
},
@ -145,7 +144,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
payload: {
path: router.asPath,
filters: issueFilters,
element_id: moduleId,
filter_property: value,
filter_type: key,
},
@ -167,7 +165,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
property: Object.values(updatedDisplayFilter)?.[0],
path: router.asPath,
filters: issueFilters,
element_id: moduleId,
},
})
);
@ -185,7 +182,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
display_property: Object.keys(property).join(","),
path: router.asPath,
filters: issueFilters,
element_id: moduleId,
},
})
);
@ -297,7 +293,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
path: router.asPath,
current_filters: issueFilters?.filters,
layout: issueFilters?.displayFilters?.layout,
element_id: moduleId,
},
})
}

View File

@ -89,7 +89,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
payload: {
path: router.asPath,
filters: issueFilters,
element_id: projectId,
filter_property: value,
filter_type: key,
},
@ -105,8 +104,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }).then(() =>
captureEvent(LAYOUT_CHANGED, {
layout: layout,
element: elementFromPath(router.asPath),
element_id: projectId,
...elementFromPath(router.asPath),
})
);
},
@ -124,7 +122,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
property: Object.values(updatedDisplayFilter)?.[0],
path: router.asPath,
filters: issueFilters,
element_id: projectId,
},
})
);
@ -142,7 +139,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
display_property: Object.keys(property).join(","),
path: router.asPath,
filters: issueFilters,
element_id: projectId,
},
});
});
@ -242,7 +238,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
path: router.asPath,
current_filters: issueFilters?.filters,
layout: issueFilters?.displayFilters?.layout,
element_id: projectId,
},
})
}

View File

@ -76,8 +76,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, viewId).then(() =>
captureEvent(LAYOUT_CHANGED, {
layout: layout,
element: elementFromPath(router.asPath),
element_id: viewId,
...elementFromPath(router.asPath),
})
);
},
@ -107,7 +106,6 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
payload: {
path: router.asPath,
filters: issueFilters,
element_id: viewId,
filter_property: value,
filter_type: key,
},
@ -128,7 +126,6 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
property: Object.values(updatedDisplayFilter)?.[0],
path: router.asPath,
filters: issueFilters,
element_id: viewId,
},
})
);
@ -146,7 +143,6 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
display_property: Object.keys(property).join(","),
path: router.asPath,
filters: issueFilters,
element_id: viewId,
},
})
);
@ -257,7 +253,6 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
path: router.asPath,
current_filters: issueFilters?.filters,
layout: issueFilters?.displayFilters?.layout,
element_id: projectId,
},
})
}

View File

@ -106,7 +106,6 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
await fetchFilters(workspaceSlug.toString(), globalViewId.toString());
captureIssuesListOpenedEvent({
path: router.asPath,
element_id: globalViewId,
});
await fetchIssues(workspaceSlug.toString(), globalViewId.toString(), issueIds ? "mutation" : "init-loader");
routerFilterParams();
@ -247,7 +246,7 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
viewId={globalViewId}
/>
{/* peek overview */}
<IssuePeekOverview />
<IssuePeekOverview issuesFilter={issueFilters} />
</Fragment>
)}
</div>

View File

@ -31,7 +31,6 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
captureIssuesListOpenedEvent({
path: router.asPath,
filters: issuesFilter?.issueFilters?.filters,
element_id: projectId,
});
await issues?.fetchIssues(
workspaceSlug.toString(),
@ -61,7 +60,7 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
<div className="relative h-full w-full overflow-auto">
<ArchivedIssueListLayout />
</div>
<IssuePeekOverview is_archived />
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters} is_archived />
</Fragment>
)}
</div>

View File

@ -44,7 +44,6 @@ export const CycleLayoutRoot: React.FC = observer(() => {
captureIssuesListOpenedEvent({
path: router.asPath,
filters: issuesFilter?.issueFilters?.filters,
element_id: cycleId,
});
await issues?.fetchIssues(
workspaceSlug.toString(),
@ -132,7 +131,7 @@ export const CycleLayoutRoot: React.FC = observer(() => {
) : null}
</div>
{/* peek overview */}
<IssuePeekOverview />
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters}/>
</Fragment>
)}
</div>

View File

@ -31,7 +31,6 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => {
captureIssuesListOpenedEvent({
path: router.asPath,
filters: issuesFilter?.issueFilters?.filters,
element_id: projectId,
});
await issues?.fetchIssues(
workspaceSlug.toString(),
@ -67,7 +66,7 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => {
<DraftKanBanLayout />
) : null}
{/* issue peek overview */}
<IssuePeekOverview is_draft />
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters} is_draft />
</div>
)}
</div>

View File

@ -40,7 +40,6 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
captureIssuesListOpenedEvent({
path: router.asPath,
filters: issuesFilter?.issueFilters?.filters,
element_id: moduleId,
})
await issues?.fetchIssues(
workspaceSlug.toString(),
@ -117,7 +116,7 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
) : null}
</div>
{/* peek overview */}
<IssuePeekOverview />
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters}/>
</Fragment>
)}
</div>

View File

@ -38,7 +38,6 @@ export const ProjectLayoutRoot: FC = observer(() => {
captureIssuesListOpenedEvent({
path: router.asPath,
filters: issuesFilter?.issueFilters?.filters,
element_id: projectId,
});
await issues?.fetchIssues(
workspaceSlug.toString(),
@ -87,7 +86,7 @@ export const ProjectLayoutRoot: FC = observer(() => {
</div>
{/* peek overview */}
<IssuePeekOverview />
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters} />
</Fragment>
)}
</div>

View File

@ -38,7 +38,6 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
captureIssuesListOpenedEvent({
path: router.asPath,
filters: issuesFilter?.issueFilters?.filters,
element_id: viewId,
});
await issues?.fetchIssues(
workspaceSlug.toString(),
@ -100,7 +99,7 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
</div>
{/* peek overview */}
<IssuePeekOverview />
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters}/>
</Fragment>
)}
</div>

View File

@ -9,12 +9,14 @@ import { copyUrlToClipboard } from "helpers/string.helper";
// hooks
import useToast from "hooks/use-toast";
// store hooks
import { useIssueDetail, useProjectState, useUser } from "hooks/store";
import { useEventTracker, useIssueDetail, useProjectState, useUser } from "hooks/store";
// helpers
import { cn } from "helpers/common.helper";
// components
import { IssueSubscription, IssueUpdateStatus } from "components/issues";
import { STATE_GROUPS } from "constants/state";
// constants
import { ISSUE_OPENED, elementFromPath } from "constants/event-tracker";
export type TPeekModes = "side-peek" | "modal" | "full-screen";
@ -74,6 +76,7 @@ export const IssuePeekOverviewHeader: FC<PeekOverviewHeaderProps> = observer((pr
issue: { getIssueById },
} = useIssueDetail();
const { getStateById } = useProjectState();
const { captureEvent } = useEventTracker();
// hooks
const { setToastAlert } = useToast();
// derived values
@ -97,6 +100,11 @@ export const IssuePeekOverviewHeader: FC<PeekOverviewHeaderProps> = observer((pr
const redirectToIssueDetail = () => {
router.push({ pathname: `/${issueLink}` });
removeRoutePeekId();
captureEvent(ISSUE_OPENED, {
...elementFromPath(router.asPath),
element: "peek",
mode: "detail",
});
};
// auth
const isArchivingAllowed = !isArchived && !disabled;

View File

@ -7,15 +7,23 @@ import { useEventTracker, useIssueDetail, useIssues, useUser } from "hooks/store
// components
import { IssueView } from "components/issues";
// types
import { TIssue } from "@plane/types";
import { IIssueFilters, TIssue } from "@plane/types";
// constants
import { EUserProjectRoles } from "constants/project";
import { EIssuesStoreType } from "constants/issue";
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED, ISSUE_RESTORED } from "constants/event-tracker";
import {
ISSUE_UPDATED,
ISSUE_DELETED,
ISSUE_ARCHIVED,
ISSUE_RESTORED,
elementFromPath,
ISSUE_OPENED,
} from "constants/event-tracker";
interface IIssuePeekOverview {
is_archived?: boolean;
is_draft?: boolean;
issuesFilter?: IIssueFilters;
}
export type TIssuePeekOperations = {
@ -48,11 +56,12 @@ export type TIssuePeekOperations = {
};
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const { is_archived = false, is_draft = false } = props;
const { is_archived = false, is_draft = false, issuesFilter } = props;
// hooks
const { setToastAlert } = useToast();
// router
const router = useRouter();
const { userId } = router.query;
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
@ -68,7 +77,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
} = useIssueDetail();
const { addIssueToCycle, removeIssueFromCycle, addModulesToIssue, removeIssueFromModule, removeModulesFromIssue } =
useIssueDetail();
const { captureIssueEvent } = useEventTracker();
const { captureEvent, captureIssueEvent } = useEventTracker();
// state
const [loader, setLoader] = useState(false);
@ -387,6 +396,19 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
}
}, [peekIssue, issueOperations]);
useEffect(() => {
if (peekIssue && issuesFilter) {
captureEvent(ISSUE_OPENED, {
layout: issuesFilter?.displayFilters?.layout,
display_properties: issuesFilter?.displayFilters,
filters: issuesFilter?.filters,
mode: "peek",
profile_id: userId,
...elementFromPath(router.asPath),
});
}
}, [peekIssue, userId, captureEvent, router.asPath]);
if (!peekIssue?.workspaceSlug || !peekIssue?.projectId || !peekIssue?.issueId) return <></>;
const issue = getIssueById(peekIssue.issueId) || undefined;

View File

@ -23,6 +23,8 @@ type NotificationCardProps = {
selectedTab: NotificationType;
notification: IUserNotification;
isSnoozedTabOpen: boolean;
isArchivedTabOpen: boolean;
isUnreadTabOpen: boolean;
closePopover: () => void;
markNotificationReadStatus: (notificationId: string) => Promise<void>;
markNotificationReadStatusToggle: (notificationId: string) => Promise<void>;
@ -36,6 +38,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
selectedTab,
notification,
isSnoozedTabOpen,
isArchivedTabOpen,
isUnreadTabOpen,
closePopover,
markNotificationReadStatus,
markNotificationReadStatusToggle,
@ -127,7 +131,14 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
markNotificationReadStatus(notification.id);
captureEvent(ISSUE_OPENED, {
issue_id: notification.data.issue.id,
element: "notification",
element: "Notification",
element_id: isArchivedTabOpen
? "archived"
: isSnoozedTabOpen
? "snoozed"
: isUnreadTabOpen
? "unread"
: selectedTab,
});
closePopover();
}}

View File

@ -132,6 +132,8 @@ export const NotificationPopover = observer(() => {
selectedTab={selectedTab}
key={notification.id}
isSnoozedTabOpen={snoozed}
isArchivedTabOpen={archived}
isUnreadTabOpen={readNotification}
closePopover={() => setIsActive(false)}
notification={notification}
markNotificationArchivedStatus={markNotificationArchivedStatus}

View File

@ -88,7 +88,7 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
) : null}
</div>
{/* peek overview */}
<IssuePeekOverview />
<IssuePeekOverview issuesFilter={issueFilters}/>
</>
);
});

View File

@ -54,21 +54,6 @@ export const GlobalViewsHeader: React.FC = observer(() => {
// bring the active view to the centre of the header
useEffect(() => {
<<<<<<< HEAD
if (!globalViewId) return;
captureEvent(GLOBAL_VIEW_OPENED, {
view_id: globalViewId,
view_type: ["all-issues", "assigned", "created", "subscribed"].includes(globalViewId.toString())
? "Default"
: "Custom",
});
const activeTabElement = document.querySelector(`#global-view-${globalViewId.toString()}`);
if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" });
}, [globalViewId, captureEvent]);
=======
if (globalViewId && currentWorkspaceViews) {
captureEvent(GLOBAL_VIEW_OPENED, {
view_id: globalViewId,
@ -85,7 +70,6 @@ export const GlobalViewsHeader: React.FC = observer(() => {
}
}
}, [globalViewId, currentWorkspaceViews, containerRef]);
>>>>>>> 62693abb0992a35f2cd57c7caf8b94304c2756f7
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;

View File

@ -124,8 +124,7 @@ export const getProjectStateEventPayload = (payload: any) => {
};
export const getIssuesListOpenedPayload = (payload: any) => ({
element: elementFromPath(payload.path),
element_id: payload.element_id,
...elementFromPath(payload?.path),
layout: payload?.displayFilters?.layout,
filters: payload?.filters,
display_properties: payload?.displayProperties,
@ -136,8 +135,7 @@ export const getIssuesFilterEventPayload = (payload: any) => ({
filter_property: payload?.filter_property,
layout: payload?.filters?.displayFilters?.layout,
current_filters: payload?.filters?.filters,
element: elementFromPath(payload.path),
element_id: payload.element_id,
...elementFromPath(payload?.path),
});
export const getIssuesDisplayFilterPayload = (payload: any) => {
@ -150,8 +148,7 @@ export const getIssuesDisplayFilterPayload = (payload: any) => {
return {
layout: payload?.filters?.displayFilters?.layout,
current_display_properties: payload?.filters?.displayProperties,
element: elementFromPath(payload.path),
element_id: payload.element_id,
...elementFromPath(payload?.path),
display_property: payload.display_property,
property: property,
property_type: payload.property_type,
@ -159,13 +156,10 @@ export const getIssuesDisplayFilterPayload = (payload: any) => {
};
export const elementFromPath = (path?: string) => {
if (!path)
return {
element: "Project",
element_id: path?.split("/").at(-2),
};
path = path?.split("?")?.[0];
if (!path) return;
let element = "Project";
let element = "Dashboard";
if (path.includes("workspace-views")) element = "Global view";
else if (path.includes("cycles")) element = "Cycle";
else if (path.includes("modules")) element = "Module";
@ -175,12 +169,11 @@ export const elementFromPath = (path?: string) => {
else if (path.includes("inbox")) element = "Inbox";
else if (path.includes("draft")) element = "Draft";
else if (path.includes("archived")) element = "Archive";
else if (path.includes("projects")) element = "Project";
return {
element: element,
element_id: ["Project", "Draft", "Archive"].includes(element)
? path.split("/").at(-2)
: path.split("/").at(-1),
element_id: ["Project", "Draft", "Archive"].includes(element) ? path.split("/").at(-2) : path.split("/").at(-1),
};
};
@ -217,6 +210,9 @@ export const VIEW_UNFAVORITED = "View unfavorited";
export const ISSUE_CREATED = "Issue created";
export const ISSUE_UPDATED = "Issue updated";
export const ISSUE_DELETED = "Issue deleted";
export const ISSUE_ARCHIVED = "Issue archived";
export const ISSUE_RESTORED = "Issue restored";
// Issue Checkout Events
export const ISSUES_LIST_OPENED = "Issues list opened";
export const ISSUE_OPENED = "Issue opened";