mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
issue open events added for project, cycle, module, archives, notification
This commit is contained in:
parent
46182584c9
commit
45abefcd00
@ -21,17 +21,22 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { IIssueActivity } from "@plane/types";
|
import { IIssueActivity } from "@plane/types";
|
||||||
import { Tooltip, BlockedIcon, BlockerIcon, RelatedIcon, LayersIcon, DiceIcon } from "@plane/ui";
|
import { Tooltip, BlockedIcon, BlockerIcon, RelatedIcon, LayersIcon, DiceIcon } from "@plane/ui";
|
||||||
|
// constants
|
||||||
|
import { ISSUE_OPENED } from "constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||||
|
import { getElementFromPath } from "@/helpers/event-tracker.helper";
|
||||||
import { capitalizeFirstLetter } from "@/helpers/string.helper";
|
import { capitalizeFirstLetter } from "@/helpers/string.helper";
|
||||||
import { useEstimate, useLabel } from "@/hooks/store";
|
import { useEstimate, useLabel, useEventTracker } from "@/hooks/store";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
// types
|
// types
|
||||||
|
|
||||||
export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
|
export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
// hooks
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -47,6 +52,13 @@ export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
|
|||||||
target={activity.issue === null ? "_self" : "_blank"}
|
target={activity.issue === null ? "_self" : "_blank"}
|
||||||
rel={activity.issue === null ? "" : "noopener noreferrer"}
|
rel={activity.issue === null ? "" : "noopener noreferrer"}
|
||||||
className="inline items-center gap-1 font-medium text-custom-text-100 hover:underline"
|
className="inline items-center gap-1 font-medium text-custom-text-100 hover:underline"
|
||||||
|
onClick={() =>
|
||||||
|
captureEvent(ISSUE_OPENED, {
|
||||||
|
element: getElementFromPath(router.asPath),
|
||||||
|
element_id: "activity",
|
||||||
|
mode: "detail",
|
||||||
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span className="whitespace-nowrap">{`${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}`}</span>{" "}
|
<span className="whitespace-nowrap">{`${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}`}</span>{" "}
|
||||||
<span className="font-normal break-all">{activity.issue_detail?.name}</span>
|
<span className="font-normal break-all">{activity.issue_detail?.name}</span>
|
||||||
|
@ -15,13 +15,14 @@ import { StateDropdown } from "@/components/dropdowns";
|
|||||||
import { EmptyState } from "@/components/empty-state";
|
import { EmptyState } from "@/components/empty-state";
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
|
import { ISSUE_OPENED, E_ACYCLE } from "@/constants/event-tracker";
|
||||||
import { CYCLE_ISSUES_WITH_PARAMS } from "@/constants/fetch-keys";
|
import { CYCLE_ISSUES_WITH_PARAMS } from "@/constants/fetch-keys";
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
// helper
|
// helper
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { renderFormattedDate, renderFormattedDateWithoutYear } from "@/helpers/date-time.helper";
|
import { renderFormattedDate, renderFormattedDateWithoutYear } from "@/helpers/date-time.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues, useProject } from "@/hooks/store";
|
import { useIssues, useProject, useEventTracker } from "@/hooks/store";
|
||||||
import useLocalStorage from "@/hooks/use-local-storage";
|
import useLocalStorage from "@/hooks/use-local-storage";
|
||||||
|
|
||||||
export type ActiveCycleStatsProps = {
|
export type ActiveCycleStatsProps = {
|
||||||
@ -50,6 +51,7 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||||||
const {
|
const {
|
||||||
issues: { fetchActiveCycleIssues },
|
issues: { fetchActiveCycleIssues },
|
||||||
} = useIssues(EIssuesStoreType.CYCLE);
|
} = useIssues(EIssuesStoreType.CYCLE);
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
|
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
|
|
||||||
@ -140,6 +142,13 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||||||
key={issue.id}
|
key={issue.id}
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
|
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
|
||||||
className="group flex cursor-pointer items-center justify-between gap-2 rounded-md hover:bg-custom-background-90 p-1"
|
className="group flex cursor-pointer items-center justify-between gap-2 rounded-md hover:bg-custom-background-90 p-1"
|
||||||
|
onClick={() =>
|
||||||
|
captureEvent(ISSUE_OPENED, {
|
||||||
|
element: E_ACYCLE,
|
||||||
|
element_id: tab?.toLowerCase(),
|
||||||
|
mode: "detail",
|
||||||
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5 flex-grow w-full min-w-24 truncate">
|
<div className="flex items-center gap-1.5 flex-grow w-full min-w-24 truncate">
|
||||||
<PriorityIcon priority={issue.priority} withContainer size={12} />
|
<PriorityIcon priority={issue.priority} withContainer size={12} />
|
||||||
|
@ -14,11 +14,12 @@ import {
|
|||||||
CreatedUpcomingIssueListItem,
|
CreatedUpcomingIssueListItem,
|
||||||
IssueListItemProps,
|
IssueListItemProps,
|
||||||
} from "@/components/dashboard/widgets";
|
} from "@/components/dashboard/widgets";
|
||||||
// ui
|
// constants
|
||||||
|
import { E_DASHBOARD, ISSUE_OPENED } from "constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { getRedirectionFilters } from "@/helpers/dashboard.helper";
|
import { getRedirectionFilters } from "@/helpers/dashboard.helper";
|
||||||
import { useIssueDetail } from "@/hooks/store";
|
import { useIssueDetail, useEventTracker } from "@/hooks/store";
|
||||||
// types
|
// types
|
||||||
|
|
||||||
export type WidgetIssuesListProps = {
|
export type WidgetIssuesListProps = {
|
||||||
@ -33,9 +34,19 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
|
|||||||
const { isLoading, tab, type, widgetStats, workspaceSlug } = props;
|
const { isLoading, tab, type, widgetStats, workspaceSlug } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { setPeekIssue } = useIssueDetail();
|
const { setPeekIssue } = useIssueDetail();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
|
|
||||||
const handleIssuePeekOverview = (issue: TIssue) =>
|
const handleIssuePeekOverview = (issue: TIssue) => {
|
||||||
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
|
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
|
||||||
|
captureEvent(ISSUE_OPENED, {
|
||||||
|
element: E_DASHBOARD,
|
||||||
|
element_id: tab,
|
||||||
|
mode: "peek",
|
||||||
|
filters: {
|
||||||
|
target_date: issue.target_date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const filterParams = getRedirectionFilters(tab);
|
const filterParams = getRedirectionFilters(tab);
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
isWorkspaceLevel
|
isWorkspaceLevel
|
||||||
/>
|
/>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview issuesFilter={issueFilters} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,7 +62,7 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
<div className="relative h-full w-full overflow-auto">
|
<div className="relative h-full w-full overflow-auto">
|
||||||
<ArchivedIssueListLayout />
|
<ArchivedIssueListLayout />
|
||||||
</div>
|
</div>
|
||||||
<IssuePeekOverview is_archived />
|
<IssuePeekOverview is_archived issuesFilter={issuesFilter.issueFilters} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -133,7 +133,7 @@ export const CycleLayoutRoot: React.FC = observer(() => {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,7 +68,7 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
<DraftKanBanLayout />
|
<DraftKanBanLayout />
|
||||||
) : null}
|
) : null}
|
||||||
{/* issue peek overview */}
|
{/* issue peek overview */}
|
||||||
<IssuePeekOverview is_draft />
|
<IssuePeekOverview is_draft issuesFilter={issuesFilter.issueFilters}/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,7 +118,7 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters}/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,7 +86,7 @@ export const ProjectLayoutRoot: FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,7 +83,7 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview issuesFilter={issuesFilter.issueFilters}/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { ArchiveRestoreIcon, Link2, MoveDiagonal, MoveRight, Trash2 } from "lucide-react";
|
import { ArchiveRestoreIcon, Link2, MoveDiagonal, MoveRight, Trash2 } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import {
|
import {
|
||||||
@ -16,11 +17,14 @@ import {
|
|||||||
// components
|
// components
|
||||||
import { IssueSubscription, IssueUpdateStatus } from "@/components/issues";
|
import { IssueSubscription, IssueUpdateStatus } from "@/components/issues";
|
||||||
import { STATE_GROUPS } from "@/constants/state";
|
import { STATE_GROUPS } from "@/constants/state";
|
||||||
|
// constants
|
||||||
|
import { ISSUE_OPENED } from "constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
import { getElementIdFromPath } from "@/helpers/event-tracker.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||||
// store hooks
|
// store hooks
|
||||||
import { useIssueDetail, useProjectState, useUser } from "@/hooks/store";
|
import { useIssueDetail, useProjectState, useUser, useEventTracker } from "@/hooks/store";
|
||||||
// hooks
|
// hooks
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
export type TPeekModes = "side-peek" | "modal" | "full-screen";
|
export type TPeekModes = "side-peek" | "modal" | "full-screen";
|
||||||
@ -73,6 +77,8 @@ export const IssuePeekOverviewHeader: FC<PeekOverviewHeaderProps> = observer((pr
|
|||||||
handleRestoreIssue,
|
handleRestoreIssue,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
} = props;
|
} = props;
|
||||||
|
// router
|
||||||
|
const router = useRouter();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
const {
|
const {
|
||||||
@ -80,6 +86,7 @@ export const IssuePeekOverviewHeader: FC<PeekOverviewHeaderProps> = observer((pr
|
|||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
const { getStateById } = useProjectState();
|
const { getStateById } = useProjectState();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// derived values
|
// derived values
|
||||||
const issueDetails = getIssueById(issueId);
|
const issueDetails = getIssueById(issueId);
|
||||||
const stateDetails = issueDetails ? getStateById(issueDetails?.state_id) : undefined;
|
const stateDetails = issueDetails ? getStateById(issueDetails?.state_id) : undefined;
|
||||||
@ -118,7 +125,17 @@ export const IssuePeekOverviewHeader: FC<PeekOverviewHeaderProps> = observer((pr
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip tooltipContent="Open issue in full screen" isMobile={isMobile}>
|
<Tooltip tooltipContent="Open issue in full screen" isMobile={isMobile}>
|
||||||
<Link href={`/${issueLink}`} onClick={() => removeRoutePeekId()}>
|
<Link
|
||||||
|
href={`/${issueLink}`}
|
||||||
|
onClick={() => {
|
||||||
|
removeRoutePeekId();
|
||||||
|
captureEvent(ISSUE_OPENED, {
|
||||||
|
element: "peek",
|
||||||
|
elementId: getElementIdFromPath(router.asPath),
|
||||||
|
mode: "detail",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
<MoveDiagonal className="h-4 w-4 text-custom-text-300 hover:text-custom-text-200" />
|
<MoveDiagonal className="h-4 w-4 text-custom-text-300 hover:text-custom-text-200" />
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -2,21 +2,24 @@ import { FC, useEffect, useState, useMemo } from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// types
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { IIssueFilters, TIssue } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/ui";
|
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { IssueView } from "@/components/issues";
|
import { IssueView } from "@/components/issues";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED, ISSUE_RESTORED } from "@/constants/event-tracker";
|
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED, ISSUE_RESTORED, ISSUE_OPENED } from "@/constants/event-tracker";
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
import { EUserProjectRoles } from "@/constants/project";
|
||||||
|
// helpers
|
||||||
|
import { getElementFromPath, getElementIdFromPath } from "@/helpers/event-tracker.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
|
import { useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
|
||||||
|
|
||||||
interface IIssuePeekOverview {
|
interface IIssuePeekOverview {
|
||||||
is_archived?: boolean;
|
is_archived?: boolean;
|
||||||
is_draft?: boolean;
|
is_draft?: boolean;
|
||||||
|
issuesFilter?: IIssueFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TIssuePeekOperations = {
|
export type TIssuePeekOperations = {
|
||||||
@ -43,9 +46,10 @@ export type TIssuePeekOperations = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||||
const { is_archived = false, is_draft = false } = props;
|
const { is_archived = false, is_draft = false, issuesFilter } = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { userId } = router.query;
|
||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceAllProjectsRole },
|
membership: { currentWorkspaceAllProjectsRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
@ -67,7 +71,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
removeIssueFromModule,
|
removeIssueFromModule,
|
||||||
removeModulesFromIssue,
|
removeModulesFromIssue,
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
const { captureIssueEvent } = useEventTracker();
|
const { captureIssueEvent, captureEvent } = useEventTracker();
|
||||||
// state
|
// state
|
||||||
const [loader, setLoader] = useState(false);
|
const [loader, setLoader] = useState(false);
|
||||||
|
|
||||||
@ -387,6 +391,20 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
}
|
}
|
||||||
}, [peekIssue, issueOperations]);
|
}, [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,
|
||||||
|
element: getElementFromPath(router.asPath),
|
||||||
|
elementId: getElementIdFromPath(router.asPath),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [peekIssue, userId, captureEvent, router.asPath]);
|
||||||
|
|
||||||
if (!peekIssue?.workspaceSlug || !peekIssue?.projectId || !peekIssue?.issueId) return <></>;
|
if (!peekIssue?.workspaceSlug || !peekIssue?.projectId || !peekIssue?.issueId) return <></>;
|
||||||
|
|
||||||
const issue = getIssueById(peekIssue.issueId) || undefined;
|
const issue = getIssueById(peekIssue.issueId) || undefined;
|
||||||
|
@ -10,6 +10,7 @@ import type { IUserNotification, NotificationType } from "@plane/types";
|
|||||||
import { ArchiveIcon, CustomMenu, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ArchiveIcon, CustomMenu, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import {
|
import {
|
||||||
|
E_NOTIFICATION,
|
||||||
ISSUE_OPENED,
|
ISSUE_OPENED,
|
||||||
NOTIFICATIONS_READ,
|
NOTIFICATIONS_READ,
|
||||||
NOTIFICATION_ARCHIVED,
|
NOTIFICATION_ARCHIVED,
|
||||||
@ -27,6 +28,8 @@ type NotificationCardProps = {
|
|||||||
selectedTab: NotificationType;
|
selectedTab: NotificationType;
|
||||||
notification: IUserNotification;
|
notification: IUserNotification;
|
||||||
isSnoozedTabOpen: boolean;
|
isSnoozedTabOpen: boolean;
|
||||||
|
isArchivedTabOpen: boolean;
|
||||||
|
isUnreadTabOpen: boolean;
|
||||||
closePopover: () => void;
|
closePopover: () => void;
|
||||||
markNotificationReadStatus: (notificationId: string) => Promise<void>;
|
markNotificationReadStatus: (notificationId: string) => Promise<void>;
|
||||||
markNotificationReadStatusToggle: (notificationId: string) => Promise<void>;
|
markNotificationReadStatusToggle: (notificationId: string) => Promise<void>;
|
||||||
@ -40,6 +43,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
|||||||
selectedTab,
|
selectedTab,
|
||||||
notification,
|
notification,
|
||||||
isSnoozedTabOpen,
|
isSnoozedTabOpen,
|
||||||
|
isArchivedTabOpen,
|
||||||
|
isUnreadTabOpen,
|
||||||
closePopover,
|
closePopover,
|
||||||
markNotificationReadStatus,
|
markNotificationReadStatus,
|
||||||
markNotificationReadStatusToggle,
|
markNotificationReadStatusToggle,
|
||||||
@ -131,7 +136,14 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
|||||||
markNotificationReadStatus(notification.id);
|
markNotificationReadStatus(notification.id);
|
||||||
captureEvent(ISSUE_OPENED, {
|
captureEvent(ISSUE_OPENED, {
|
||||||
issue_id: notification.data.issue.id,
|
issue_id: notification.data.issue.id,
|
||||||
element: "notification",
|
element: E_NOTIFICATION,
|
||||||
|
element_id: isArchivedTabOpen
|
||||||
|
? "archived"
|
||||||
|
: isSnoozedTabOpen
|
||||||
|
? "snoozed"
|
||||||
|
: isUnreadTabOpen
|
||||||
|
? "unread"
|
||||||
|
: selectedTab,
|
||||||
});
|
});
|
||||||
closePopover();
|
closePopover();
|
||||||
}}
|
}}
|
||||||
|
@ -63,12 +63,12 @@ export const NotificationPopover = observer(() => {
|
|||||||
const currentTabEmptyState = snoozed
|
const currentTabEmptyState = snoozed
|
||||||
? EmptyStateType.NOTIFICATION_SNOOZED_EMPTY_STATE
|
? EmptyStateType.NOTIFICATION_SNOOZED_EMPTY_STATE
|
||||||
: archived
|
: archived
|
||||||
? EmptyStateType.NOTIFICATION_ARCHIVED_EMPTY_STATE
|
? EmptyStateType.NOTIFICATION_ARCHIVED_EMPTY_STATE
|
||||||
: selectedTab === "created"
|
: selectedTab === "created"
|
||||||
? EmptyStateType.NOTIFICATION_CREATED_EMPTY_STATE
|
? EmptyStateType.NOTIFICATION_CREATED_EMPTY_STATE
|
||||||
: selectedTab === "watching"
|
: selectedTab === "watching"
|
||||||
? EmptyStateType.NOTIFICATION_SUBSCRIBED_EMPTY_STATE
|
? EmptyStateType.NOTIFICATION_SUBSCRIBED_EMPTY_STATE
|
||||||
: EmptyStateType.NOTIFICATION_MY_ISSUE_EMPTY_STATE;
|
: EmptyStateType.NOTIFICATION_MY_ISSUE_EMPTY_STATE;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -154,6 +154,8 @@ export const NotificationPopover = observer(() => {
|
|||||||
selectedTab={selectedTab}
|
selectedTab={selectedTab}
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
isSnoozedTabOpen={snoozed}
|
isSnoozedTabOpen={snoozed}
|
||||||
|
isArchivedTabOpen={archived}
|
||||||
|
isUnreadTabOpen={readNotification}
|
||||||
closePopover={() => setIsActive(false)}
|
closePopover={() => setIsActive(false)}
|
||||||
notification={notification}
|
notification={notification}
|
||||||
markNotificationArchivedStatus={markNotificationArchivedStatus}
|
markNotificationArchivedStatus={markNotificationArchivedStatus}
|
||||||
|
@ -69,7 +69,7 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview issuesFilter={issueFilters} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -18,145 +18,6 @@ export type EventProps = {
|
|||||||
payload: any;
|
payload: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getWorkspaceEventPayload = (payload: any) => ({
|
|
||||||
workspace_id: payload.id,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
organization_size: payload.organization_size,
|
|
||||||
first_time: payload.first_time,
|
|
||||||
state: payload.state,
|
|
||||||
element: payload.element,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getProjectEventPayload = (payload: any) => ({
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
project_id: payload.id,
|
|
||||||
identifier: payload.identifier,
|
|
||||||
project_visibility: payload.network == 2 ? "Public" : "Private",
|
|
||||||
changed_properties: payload.changed_properties,
|
|
||||||
lead_id: payload.project_lead,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
state: payload.state,
|
|
||||||
element: payload.element,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getCycleEventPayload = (payload: any) => ({
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
project_id: payload.project,
|
|
||||||
cycle_id: payload.id,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
start_date: payload.start_date,
|
|
||||||
target_date: payload.target_date,
|
|
||||||
cycle_status: payload.status,
|
|
||||||
changed_properties: payload.changed_properties,
|
|
||||||
state: payload.state,
|
|
||||||
element: payload.element,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getModuleEventPayload = (payload: any) => ({
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
project_id: payload.project,
|
|
||||||
module_id: payload.id,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
start_date: payload.start_date,
|
|
||||||
target_date: payload.target_date,
|
|
||||||
module_status: payload.status,
|
|
||||||
lead_id: payload.lead,
|
|
||||||
changed_properties: payload.changed_properties,
|
|
||||||
member_ids: payload.members,
|
|
||||||
state: payload.state,
|
|
||||||
element: payload.element,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getPageEventPayload = (payload: any) => ({
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
project_id: payload.project,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
access: payload.access === 0 ? "Public" : "Private",
|
|
||||||
is_locked: payload.is_locked,
|
|
||||||
archived_at: payload.archived_at,
|
|
||||||
created_by: payload.created_by,
|
|
||||||
state: payload.state,
|
|
||||||
element: payload.element,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getIssueEventPayload = (props: IssueEventProps) => {
|
|
||||||
const { eventName, payload, updates, routePath } = props;
|
|
||||||
let eventPayload: any = {
|
|
||||||
issue_id: payload.id,
|
|
||||||
estimate_point: payload.estimate_point,
|
|
||||||
link_count: payload.link_count,
|
|
||||||
target_date: payload.target_date,
|
|
||||||
is_draft: payload.is_draft,
|
|
||||||
label_ids: payload.label_ids,
|
|
||||||
assignee_ids: payload.assignee_ids,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
sequence_id: payload.sequence_id,
|
|
||||||
module_ids: payload.module_ids,
|
|
||||||
sub_issues_count: payload.sub_issues_count,
|
|
||||||
parent_id: payload.parent_id,
|
|
||||||
project_id: payload.project_id,
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
priority: payload.priority,
|
|
||||||
state_id: payload.state_id,
|
|
||||||
start_date: payload.start_date,
|
|
||||||
attachment_count: payload.attachment_count,
|
|
||||||
cycle_id: payload.cycle_id,
|
|
||||||
module_id: payload.module_id,
|
|
||||||
archived_at: payload.archived_at,
|
|
||||||
state: payload.state,
|
|
||||||
view_id: routePath?.includes("workspace-views") || routePath?.includes("views") ? routePath.split("/").pop() : "",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (eventName === ISSUE_UPDATED) {
|
|
||||||
eventPayload = {
|
|
||||||
...eventPayload,
|
|
||||||
...updates,
|
|
||||||
updated_from: elementFromPath(routePath),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return eventPayload;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProjectStateEventPayload = (payload: any) => ({
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
project_id: payload.id,
|
|
||||||
state_id: payload.id,
|
|
||||||
created_at: payload.created_at,
|
|
||||||
updated_at: payload.updated_at,
|
|
||||||
group: payload.group,
|
|
||||||
color: payload.color,
|
|
||||||
default: payload.default,
|
|
||||||
state: payload.state,
|
|
||||||
element: payload.element,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getIssuesListOpenedPayload = (payload: any) => ({
|
|
||||||
type: payload.project_id ? "Project" : "Workspace",
|
|
||||||
layout: payload?.filters?.displayFilters?.layout,
|
|
||||||
filters: payload?.filters?.filters,
|
|
||||||
display_properties: payload?.filters?.displayProperties,
|
|
||||||
workspace_id: payload.workspace_id,
|
|
||||||
project_id: payload.project_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Returns the element based on the path
|
|
||||||
const elementFromPath = (routePath?: string) => {
|
|
||||||
if (routePath?.includes("workspace-views")) return E_WORKSPACE_VIEW;
|
|
||||||
if (routePath?.includes("cycles")) return E_CYCLE;
|
|
||||||
if (routePath?.includes("modules")) return E_MODULE;
|
|
||||||
if (routePath?.includes("views")) return E_PROJECT_VIEW;
|
|
||||||
if (routePath?.includes("inbox")) return E_INBOX;
|
|
||||||
if (routePath?.includes("draft")) return E_DRAFT;
|
|
||||||
if (routePath?.includes("archived")) return E_ARCHIVE;
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Workspace crud Events
|
// Workspace crud Events
|
||||||
export const WORKSPACE_CREATED = "Workspace created";
|
export const WORKSPACE_CREATED = "Workspace created";
|
||||||
export const WORKSPACE_UPDATED = "Workspace updated";
|
export const WORKSPACE_UPDATED = "Workspace updated";
|
||||||
@ -189,6 +50,8 @@ export const ISSUE_RESTORED = "Issue restored";
|
|||||||
// Issue Checkout Events
|
// Issue Checkout Events
|
||||||
export const ISSUES_LIST_OPENED = "Issues list opened";
|
export const ISSUES_LIST_OPENED = "Issues list opened";
|
||||||
export const ISSUE_OPENED = "Issue opened";
|
export const ISSUE_OPENED = "Issue opened";
|
||||||
|
// Layout & Filter Events
|
||||||
|
export const LAYOUT_CHANGED = "Layout changed";
|
||||||
// Project State Events
|
// Project State Events
|
||||||
export const STATE_CREATED = "State created";
|
export const STATE_CREATED = "State created";
|
||||||
export const STATE_UPDATED = "State updated";
|
export const STATE_UPDATED = "State updated";
|
||||||
@ -244,9 +107,13 @@ export const GROUP_WORKSPACE = "Workspace_metrics";
|
|||||||
// Elements
|
// Elements
|
||||||
export const E_PROJECT = "Project";
|
export const E_PROJECT = "Project";
|
||||||
export const E_CYCLE = "Cycle";
|
export const E_CYCLE = "Cycle";
|
||||||
|
export const E_ACYCLE = "Active cycle";
|
||||||
export const E_MODULE = "Module";
|
export const E_MODULE = "Module";
|
||||||
export const E_PROJECT_VIEW = "Project view";
|
export const E_PROJECT_VIEW = "Project view";
|
||||||
export const E_WORKSPACE_VIEW = "Workspace view";
|
export const E_WORKSPACE_VIEW = "Workspace view";
|
||||||
export const E_DRAFT = "Draft";
|
export const E_DRAFT = "Draft";
|
||||||
export const E_ARCHIVE = "Archives";
|
export const E_ARCHIVE = "Archives";
|
||||||
export const E_INBOX = "Inbox";
|
export const E_INBOX = "Inbox";
|
||||||
|
export const E_NOTIFICATION = "Notification";
|
||||||
|
export const E_DASHBOARD= "Dashboard";
|
||||||
|
export const E_PROFILE = "Profile";
|
||||||
|
168
web/helpers/event-tracker.helper.ts
Normal file
168
web/helpers/event-tracker.helper.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import {
|
||||||
|
// elements
|
||||||
|
E_ARCHIVE,
|
||||||
|
E_CYCLE,
|
||||||
|
E_DASHBOARD,
|
||||||
|
E_DRAFT,
|
||||||
|
E_INBOX,
|
||||||
|
E_MODULE,
|
||||||
|
E_PROFILE,
|
||||||
|
E_PROJECT,
|
||||||
|
E_PROJECT_VIEW,
|
||||||
|
E_WORKSPACE_VIEW,
|
||||||
|
// events
|
||||||
|
ISSUE_UPDATED,
|
||||||
|
// types
|
||||||
|
IssueEventProps,
|
||||||
|
} from "@/constants/event-tracker";
|
||||||
|
|
||||||
|
export const getWorkspaceEventPayload = (payload: any) => ({
|
||||||
|
workspace_id: payload.id,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
organization_size: payload.organization_size,
|
||||||
|
first_time: payload.first_time,
|
||||||
|
state: payload.state,
|
||||||
|
element: payload.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getProjectEventPayload = (payload: any) => ({
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
project_id: payload.id,
|
||||||
|
identifier: payload.identifier,
|
||||||
|
project_visibility: payload.network == 2 ? "Public" : "Private",
|
||||||
|
changed_properties: payload.changed_properties,
|
||||||
|
lead_id: payload.project_lead,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
state: payload.state,
|
||||||
|
element: payload.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCycleEventPayload = (payload: any) => ({
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
project_id: payload.project,
|
||||||
|
cycle_id: payload.id,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
start_date: payload.start_date,
|
||||||
|
target_date: payload.target_date,
|
||||||
|
cycle_status: payload.status,
|
||||||
|
changed_properties: payload.changed_properties,
|
||||||
|
state: payload.state,
|
||||||
|
element: payload.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getModuleEventPayload = (payload: any) => ({
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
project_id: payload.project,
|
||||||
|
module_id: payload.id,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
start_date: payload.start_date,
|
||||||
|
target_date: payload.target_date,
|
||||||
|
module_status: payload.status,
|
||||||
|
lead_id: payload.lead,
|
||||||
|
changed_properties: payload.changed_properties,
|
||||||
|
member_ids: payload.members,
|
||||||
|
state: payload.state,
|
||||||
|
element: payload.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getPageEventPayload = (payload: any) => ({
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
project_id: payload.project,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
access: payload.access === 0 ? "Public" : "Private",
|
||||||
|
is_locked: payload.is_locked,
|
||||||
|
archived_at: payload.archived_at,
|
||||||
|
created_by: payload.created_by,
|
||||||
|
state: payload.state,
|
||||||
|
element: payload.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getIssueEventPayload = (props: IssueEventProps) => {
|
||||||
|
const { eventName, payload, updates, routePath } = props;
|
||||||
|
let eventPayload: any = {
|
||||||
|
issue_id: payload.id,
|
||||||
|
estimate_point: payload.estimate_point,
|
||||||
|
link_count: payload.link_count,
|
||||||
|
target_date: payload.target_date,
|
||||||
|
is_draft: payload.is_draft,
|
||||||
|
label_ids: payload.label_ids,
|
||||||
|
assignee_ids: payload.assignee_ids,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
sequence_id: payload.sequence_id,
|
||||||
|
module_ids: payload.module_ids,
|
||||||
|
sub_issues_count: payload.sub_issues_count,
|
||||||
|
parent_id: payload.parent_id,
|
||||||
|
project_id: payload.project_id,
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
priority: payload.priority,
|
||||||
|
state_id: payload.state_id,
|
||||||
|
start_date: payload.start_date,
|
||||||
|
attachment_count: payload.attachment_count,
|
||||||
|
cycle_id: payload.cycle_id,
|
||||||
|
module_id: payload.module_id,
|
||||||
|
archived_at: payload.archived_at,
|
||||||
|
state: payload.state,
|
||||||
|
view_id: routePath?.includes("workspace-views") || routePath?.includes("views") ? routePath.split("/").pop() : "",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (eventName === ISSUE_UPDATED) {
|
||||||
|
eventPayload = {
|
||||||
|
...eventPayload,
|
||||||
|
...updates,
|
||||||
|
updated_from: getElementFromPath(routePath),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return eventPayload;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProjectStateEventPayload = (payload: any) => ({
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
project_id: payload.id,
|
||||||
|
state_id: payload.id,
|
||||||
|
created_at: payload.created_at,
|
||||||
|
updated_at: payload.updated_at,
|
||||||
|
group: payload.group,
|
||||||
|
color: payload.color,
|
||||||
|
default: payload.default,
|
||||||
|
state: payload.state,
|
||||||
|
element: payload.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getIssuesListOpenedPayload = (payload: any) => ({
|
||||||
|
type: payload.project_id ? "Project" : "Workspace",
|
||||||
|
layout: payload?.filters?.displayFilters?.layout,
|
||||||
|
filters: payload?.filters?.filters,
|
||||||
|
display_properties: payload?.filters?.displayProperties,
|
||||||
|
workspace_id: payload.workspace_id,
|
||||||
|
project_id: payload.project_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns the element based on the path
|
||||||
|
export const getElementFromPath = (routePath?: string) => {
|
||||||
|
if (routePath?.includes("workspace-views")) return E_WORKSPACE_VIEW;
|
||||||
|
if (routePath?.includes("profile")) return E_PROFILE;
|
||||||
|
if (routePath?.includes("cycles")) return E_CYCLE;
|
||||||
|
if (routePath?.includes("modules")) return E_MODULE;
|
||||||
|
if (routePath?.includes("views")) return E_PROJECT_VIEW;
|
||||||
|
if (routePath?.includes("inbox")) return E_INBOX;
|
||||||
|
if (routePath?.includes("draft")) return E_DRAFT;
|
||||||
|
if (routePath?.includes("archives")) return E_ARCHIVE;
|
||||||
|
if (routePath?.includes("projects")) return E_PROJECT;
|
||||||
|
if (routePath?.split("/").length === 2) return E_DASHBOARD;
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getElementIdFromPath = (routePath?: string) => {
|
||||||
|
const element = getElementFromPath(routePath);
|
||||||
|
return [E_PROJECT, E_DRAFT].includes(element)
|
||||||
|
? routePath?.split("/").at(-2)
|
||||||
|
: element === E_ARCHIVE
|
||||||
|
? routePath?.split("/").at(-3)
|
||||||
|
: routePath?.split("/").at(-1);
|
||||||
|
};
|
@ -2,10 +2,15 @@ import { action, computed, makeObservable, observable } from "mobx";
|
|||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
// stores
|
// stores
|
||||||
import {
|
import {
|
||||||
//types
|
|
||||||
EventProps,
|
EventProps,
|
||||||
IssueEventProps,
|
IssueEventProps,
|
||||||
// payload helpers
|
IssuesListOpenedEventProps,
|
||||||
|
ISSUES_LIST_OPENED,
|
||||||
|
GROUP_WORKSPACE,
|
||||||
|
WORKSPACE_CREATED,
|
||||||
|
} from "@/constants/event-tracker";
|
||||||
|
// helpers
|
||||||
|
import {
|
||||||
getCycleEventPayload,
|
getCycleEventPayload,
|
||||||
getIssueEventPayload,
|
getIssueEventPayload,
|
||||||
getModuleEventPayload,
|
getModuleEventPayload,
|
||||||
@ -14,12 +19,7 @@ import {
|
|||||||
getWorkspaceEventPayload,
|
getWorkspaceEventPayload,
|
||||||
getPageEventPayload,
|
getPageEventPayload,
|
||||||
getIssuesListOpenedPayload,
|
getIssuesListOpenedPayload,
|
||||||
// event constants
|
} from "@/helpers/event-tracker.helper";
|
||||||
ISSUES_LIST_OPENED,
|
|
||||||
GROUP_WORKSPACE,
|
|
||||||
WORKSPACE_CREATED,
|
|
||||||
IssuesListOpenedEventProps,
|
|
||||||
} from "@/constants/event-tracker";
|
|
||||||
import { RootStore } from "./root.store";
|
import { RootStore } from "./root.store";
|
||||||
|
|
||||||
export interface IEventTrackerStore {
|
export interface IEventTrackerStore {
|
||||||
|
Loading…
Reference in New Issue
Block a user