From acf3faa11d1a693b51ff8904b183a1cf5a18885f Mon Sep 17 00:00:00 2001 From: LAKHAN BAHETI Date: Mon, 3 Jun 2024 14:15:08 +0530 Subject: [PATCH] added cycle events --- .../cycles/active-cycle/cycle-stats.tsx | 30 +++++++++++-------- .../cycles/archived-cycles/modal.tsx | 8 ++++- .../cycles/archived-cycles/root.tsx | 9 +++++- web/components/cycles/cycles-view-header.tsx | 15 ++++++++-- .../cycles/list/cycle-list-item-action.tsx | 2 -- web/components/cycles/quick-actions.tsx | 10 +++++-- web/constants/event-tracker.ts | 9 ++++++ .../projects/[projectId]/cycles/index.tsx | 9 +++++- 8 files changed, 70 insertions(+), 22 deletions(-) diff --git a/web/components/cycles/active-cycle/cycle-stats.tsx b/web/components/cycles/active-cycle/cycle-stats.tsx index e31e9af53..8b59a9271 100644 --- a/web/components/cycles/active-cycle/cycle-stats.tsx +++ b/web/components/cycles/active-cycle/cycle-stats.tsx @@ -15,13 +15,14 @@ import { StateDropdown } from "@/components/dropdowns"; import { EmptyState } from "@/components/empty-state"; // constants import { EmptyStateType } from "@/constants/empty-state"; +import { ACYCLE_TAB_CHANGED } from "@/constants/event-tracker"; import { CYCLE_ISSUES_WITH_PARAMS } from "@/constants/fetch-keys"; import { EIssuesStoreType } from "@/constants/issue"; // helper import { cn } from "@/helpers/common.helper"; import { renderFormattedDate, renderFormattedDateWithoutYear } from "@/helpers/date-time.helper"; // hooks -import { useIssues, useProject } from "@/hooks/store"; +import { useIssues, useProject, useEventTracker } from "@/hooks/store"; import useLocalStorage from "@/hooks/use-local-storage"; export type ActiveCycleStatsProps = { @@ -34,6 +35,7 @@ export const ActiveCycleStats: FC = observer((props) => { const { workspaceSlug, projectId, cycle } = props; const { storedValue: tab, setValue: setTab } = useLocalStorage("activeCycleTab", "Assignees"); + const { captureEvent } = useEventTracker(); const currentValue = (tab: string | null) => { switch (tab) { @@ -47,6 +49,18 @@ export const ActiveCycleStats: FC = observer((props) => { return 0; } }; + const getCurrentTab = (index: number) => { + switch (index) { + case 0: + return "Priority-Issues"; + case 1: + return "Assignees"; + case 2: + return "Labels"; + default: + return "Priority-Issues"; + } + }; const { issues: { fetchActiveCycleIssues }, } = useIssues(EIssuesStoreType.CYCLE); @@ -66,17 +80,9 @@ export const ActiveCycleStats: FC = observer((props) => { as={Fragment} defaultIndex={currentValue(tab)} onChange={(i) => { - switch (i) { - case 0: - return setTab("Priority-Issues"); - case 1: - return setTab("Assignees"); - case 2: - return setTab("Labels"); - - default: - return setTab("Priority-Issues"); - } + const currentTab = getCurrentTab(i); + setTab(currentTab); + captureEvent(ACYCLE_TAB_CHANGED, { tab: currentTab }); }} > = (props) => { const [isArchiving, setIsArchiving] = useState(false); // store hooks const { getCycleNameById, archiveCycle } = useCycle(); + const {captureEvent} =useEventTracker(); const cycleName = getCycleNameById(cycleId); @@ -42,6 +45,9 @@ export const ArchiveCycleModal: React.FC = (props) => { }); onClose(); router.push(`/${workspaceSlug}/projects/${projectId}/cycles`); + captureEvent(CYCLE_ARCHIVED, { + cycle_id: cycleId, + }); }) .catch(() => setToast({ diff --git a/web/components/cycles/archived-cycles/root.tsx b/web/components/cycles/archived-cycles/root.tsx index 4d47c8f34..bf5a61393 100644 --- a/web/components/cycles/archived-cycles/root.tsx +++ b/web/components/cycles/archived-cycles/root.tsx @@ -10,10 +10,11 @@ import { EmptyState } from "@/components/empty-state"; import { CycleModuleListLayout } from "@/components/ui"; // constants import { EmptyStateType } from "@/constants/empty-state"; +import { CYCLES_FILTER_REMOVED } from "@/constants/event-tracker"; // helpers import { calculateTotalFilters } from "@/helpers/filter.helper"; // hooks -import { useCycle, useCycleFilter } from "@/hooks/store"; +import { useCycle, useCycleFilter, useEventTracker } from "@/hooks/store"; export const ArchivedCycleLayoutRoot: React.FC = observer(() => { // router @@ -21,6 +22,7 @@ export const ArchivedCycleLayoutRoot: React.FC = observer(() => { const { workspaceSlug, projectId } = router.query; // hooks const { fetchArchivedCycles, currentProjectArchivedCycleIds, loader } = useCycle(); + const { captureEvent } = useEventTracker(); // cycle filters hook const { clearAllFilters, currentProjectArchivedFilters, updateFilters } = useCycleFilter(); // derived values @@ -43,6 +45,11 @@ export const ArchivedCycleLayoutRoot: React.FC = observer(() => { if (!value) newValues = []; else newValues = newValues.filter((val) => val !== value); + captureEvent(CYCLES_FILTER_REMOVED, { + filter_type: key, + filter_property: value, + current_filters: currentProjectArchivedFilters, + }); updateFilters(projectId.toString(), { [key]: newValues }, "archived"); }; diff --git a/web/components/cycles/cycles-view-header.tsx b/web/components/cycles/cycles-view-header.tsx index adb0eea4b..d62a7a03d 100644 --- a/web/components/cycles/cycles-view-header.tsx +++ b/web/components/cycles/cycles-view-header.tsx @@ -7,11 +7,13 @@ import { TCycleFilters } from "@plane/types"; // components import { CycleFiltersSelection } from "@/components/cycles"; import { FiltersDropdown } from "@/components/issues"; +// constants +import { CYCLES_FILTER_APPLIED, CYCLES_FILTER_REMOVED } from "@/constants/event-tracker"; // helpers import { cn } from "@/helpers/common.helper"; import { calculateTotalFilters } from "@/helpers/filter.helper"; // hooks -import { useCycleFilter } from "@/hooks/store"; +import { useCycleFilter, useEventTracker } from "@/hooks/store"; import useOutsideClickDetector from "@/hooks/use-outside-click-detector"; type Props = { @@ -24,6 +26,7 @@ export const CyclesViewHeader: React.FC = observer((props) => { const inputRef = useRef(null); // hooks const { currentProjectFilters, searchQuery, updateFilters, updateSearchQuery } = useCycleFilter(); + const { captureEvent } = useEventTracker(); // states const [isSearchOpen, setIsSearchOpen] = useState(searchQuery !== "" ? true : false); // outside click detector hook @@ -34,7 +37,7 @@ export const CyclesViewHeader: React.FC = observer((props) => { const handleFilters = useCallback( (key: keyof TCycleFilters, value: string | string[]) => { if (!projectId) return; - const newValues = currentProjectFilters?.[key] ?? []; + const newValues = Array.from(currentProjectFilters?.[key] ?? []); if (Array.isArray(value)) value.forEach((val) => { @@ -46,6 +49,14 @@ export const CyclesViewHeader: React.FC = observer((props) => { else newValues.push(value); } + captureEvent( + (currentProjectFilters?.[key] ?? []).length > newValues.length ? CYCLES_FILTER_REMOVED : CYCLES_FILTER_APPLIED, + { + filter_type: key, + filter_property: value, + current_filters: currentProjectFilters, + } + ); updateFilters(projectId, { [key]: newValues }); }, [currentProjectFilters, projectId, updateFilters] diff --git a/web/components/cycles/list/cycle-list-item-action.tsx b/web/components/cycles/list/cycle-list-item-action.tsx index c8493181c..d182c0131 100644 --- a/web/components/cycles/list/cycle-list-item-action.tsx +++ b/web/components/cycles/list/cycle-list-item-action.tsx @@ -57,7 +57,6 @@ export const CycleListItemAction: FC = observer((props) => { () => { captureEvent(CYCLE_FAVORITED, { cycle_id: cycleId, - element: "List layout", state: "SUCCESS", }); } @@ -87,7 +86,6 @@ export const CycleListItemAction: FC = observer((props) => { ).then(() => { captureEvent(CYCLE_UNFAVORITED, { cycle_id: cycleId, - element: "List layout", state: "SUCCESS", }); }); diff --git a/web/components/cycles/quick-actions.tsx b/web/components/cycles/quick-actions.tsx index 9c130ef7a..e5385e125 100644 --- a/web/components/cycles/quick-actions.tsx +++ b/web/components/cycles/quick-actions.tsx @@ -8,6 +8,7 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set // components import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles"; // constants +import { CYCLE_ARCHIVED, CYCLE_RESTORED, E_CYCLES } from "@/constants/event-tracker"; import { EUserProjectRoles } from "@/constants/project"; // helpers import { cn } from "@/helpers/common.helper"; @@ -31,7 +32,7 @@ export const CycleQuickActions: React.FC = observer((props) => { const [archiveCycleModal, setArchiveCycleModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false); // store hooks - const { setTrackElement } = useEventTracker(); + const { setTrackElement, captureEvent } = useEventTracker(); const { membership: { currentWorkspaceAllProjectsRole }, } = useUser(); @@ -56,7 +57,7 @@ export const CycleQuickActions: React.FC = observer((props) => { const handleOpenInNewTab = () => window.open(`/${cycleLink}`, "_blank"); const handleEditCycle = () => { - setTrackElement("Cycles page list layout"); + setTrackElement(E_CYCLES); setUpdateModal(true); }; @@ -65,6 +66,9 @@ export const CycleQuickActions: React.FC = observer((props) => { const handleRestoreCycle = async () => await restoreCycle(workspaceSlug, projectId, cycleId) .then(() => { + captureEvent(CYCLE_RESTORED, { + cycle_id: cycleId, + }); setToast({ type: TOAST_TYPE.SUCCESS, title: "Restore success", @@ -81,7 +85,7 @@ export const CycleQuickActions: React.FC = observer((props) => { ); const handleDeleteCycle = () => { - setTrackElement("Cycles page list layout"); + setTrackElement(E_CYCLES); setDeleteModal(true); }; diff --git a/web/constants/event-tracker.ts b/web/constants/event-tracker.ts index 7edfccba5..4c24c68f8 100644 --- a/web/constants/event-tracker.ts +++ b/web/constants/event-tracker.ts @@ -154,6 +154,12 @@ export const CYCLE_UPDATED = "Cycle updated"; export const CYCLE_DELETED = "Cycle deleted"; export const CYCLE_FAVORITED = "Cycle favorited"; export const CYCLE_UNFAVORITED = "Cycle unfavorited"; +export const ACYCLE_TAB_CHANGED = "Active cycle tab changed"; +export const CYCLES_FILTER_APPLIED = "Cycles filter applied"; +export const CYCLES_FILTER_REMOVED = "Cycles filter removed"; +export const CYCLE_ARCHIVED = "Cycle archived"; +export const CYCLE_RESTORED = "Cycle restored"; +export const CYCLE_LAYOUT_CHANGED = "Cycle layout changed"; // Module Events export const MODULE_CREATED = "Module created"; export const MODULE_UPDATED = "Module updated"; @@ -222,3 +228,6 @@ export const SNOOZED_NOTIFICATIONS = "Snoozed notifications viewed"; export const ARCHIVED_NOTIFICATIONS = "Archived notifications viewed"; // Groups export const GROUP_WORKSPACE = "Workspace_metrics"; + +// Elements +export const E_CYCLES = "Cycles"; \ No newline at end of file diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index 5b7f313e7..da4d7ff28 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -12,6 +12,7 @@ import { CyclesHeader } from "@/components/headers"; import { CycleModuleListLayout } from "@/components/ui"; // constants import { EmptyStateType } from "@/constants/empty-state"; +import { CYCLES_FILTER_REMOVED } from "@/constants/event-tracker"; // helpers import { calculateTotalFilters } from "@/helpers/filter.helper"; // hooks @@ -25,7 +26,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { // states const [createModal, setCreateModal] = useState(false); // store hooks - const { setTrackElement } = useEventTracker(); + const { setTrackElement,captureEvent } = useEventTracker(); const { currentProjectCycleIds, loader } = useCycle(); const { getProjectById, currentProjectDetails } = useProject(); // router @@ -45,6 +46,12 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { if (!value) newValues = []; else newValues = newValues.filter((val) => val !== value); + captureEvent(CYCLES_FILTER_REMOVED, { + filter_type: key, + filter_property: value, + current_filters: currentProjectFilters, + }); + updateFilters(projectId.toString(), { [key]: newValues }); };