mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
added cycle events
This commit is contained in:
parent
81dfc15d1f
commit
acf3faa11d
@ -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 { ACYCLE_TAB_CHANGED } 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 = {
|
||||||
@ -34,6 +35,7 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||||||
const { workspaceSlug, projectId, cycle } = props;
|
const { workspaceSlug, projectId, cycle } = props;
|
||||||
|
|
||||||
const { storedValue: tab, setValue: setTab } = useLocalStorage("activeCycleTab", "Assignees");
|
const { storedValue: tab, setValue: setTab } = useLocalStorage("activeCycleTab", "Assignees");
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
|
|
||||||
const currentValue = (tab: string | null) => {
|
const currentValue = (tab: string | null) => {
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
@ -47,6 +49,18 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||||||
return 0;
|
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 {
|
const {
|
||||||
issues: { fetchActiveCycleIssues },
|
issues: { fetchActiveCycleIssues },
|
||||||
} = useIssues(EIssuesStoreType.CYCLE);
|
} = useIssues(EIssuesStoreType.CYCLE);
|
||||||
@ -66,17 +80,9 @@ export const ActiveCycleStats: FC<ActiveCycleStatsProps> = observer((props) => {
|
|||||||
as={Fragment}
|
as={Fragment}
|
||||||
defaultIndex={currentValue(tab)}
|
defaultIndex={currentValue(tab)}
|
||||||
onChange={(i) => {
|
onChange={(i) => {
|
||||||
switch (i) {
|
const currentTab = getCurrentTab(i);
|
||||||
case 0:
|
setTab(currentTab);
|
||||||
return setTab("Priority-Issues");
|
captureEvent(ACYCLE_TAB_CHANGED, { tab: currentTab });
|
||||||
case 1:
|
|
||||||
return setTab("Assignees");
|
|
||||||
case 2:
|
|
||||||
return setTab("Labels");
|
|
||||||
|
|
||||||
default:
|
|
||||||
return setTab("Priority-Issues");
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab.List
|
<Tab.List
|
||||||
|
@ -3,8 +3,10 @@ import { useRouter } from "next/router";
|
|||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
|
// constants
|
||||||
|
import { CYCLE_ARCHIVED } from "@/constants/event-tracker";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle } from "@/hooks/store";
|
import { useCycle, useEventTracker } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -23,6 +25,7 @@ export const ArchiveCycleModal: React.FC<Props> = (props) => {
|
|||||||
const [isArchiving, setIsArchiving] = useState(false);
|
const [isArchiving, setIsArchiving] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { getCycleNameById, archiveCycle } = useCycle();
|
const { getCycleNameById, archiveCycle } = useCycle();
|
||||||
|
const {captureEvent} =useEventTracker();
|
||||||
|
|
||||||
const cycleName = getCycleNameById(cycleId);
|
const cycleName = getCycleNameById(cycleId);
|
||||||
|
|
||||||
@ -42,6 +45,9 @@ export const ArchiveCycleModal: React.FC<Props> = (props) => {
|
|||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
router.push(`/${workspaceSlug}/projects/${projectId}/cycles`);
|
router.push(`/${workspaceSlug}/projects/${projectId}/cycles`);
|
||||||
|
captureEvent(CYCLE_ARCHIVED, {
|
||||||
|
cycle_id: cycleId,
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() =>
|
.catch(() =>
|
||||||
setToast({
|
setToast({
|
||||||
|
@ -10,10 +10,11 @@ import { EmptyState } from "@/components/empty-state";
|
|||||||
import { CycleModuleListLayout } from "@/components/ui";
|
import { CycleModuleListLayout } from "@/components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
|
import { CYCLES_FILTER_REMOVED } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle, useCycleFilter } from "@/hooks/store";
|
import { useCycle, useCycleFilter, useEventTracker } from "@/hooks/store";
|
||||||
|
|
||||||
export const ArchivedCycleLayoutRoot: React.FC = observer(() => {
|
export const ArchivedCycleLayoutRoot: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -21,6 +22,7 @@ export const ArchivedCycleLayoutRoot: React.FC = observer(() => {
|
|||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// hooks
|
// hooks
|
||||||
const { fetchArchivedCycles, currentProjectArchivedCycleIds, loader } = useCycle();
|
const { fetchArchivedCycles, currentProjectArchivedCycleIds, loader } = useCycle();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// cycle filters hook
|
// cycle filters hook
|
||||||
const { clearAllFilters, currentProjectArchivedFilters, updateFilters } = useCycleFilter();
|
const { clearAllFilters, currentProjectArchivedFilters, updateFilters } = useCycleFilter();
|
||||||
// derived values
|
// derived values
|
||||||
@ -43,6 +45,11 @@ export const ArchivedCycleLayoutRoot: React.FC = observer(() => {
|
|||||||
if (!value) newValues = [];
|
if (!value) newValues = [];
|
||||||
else newValues = newValues.filter((val) => val !== value);
|
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");
|
updateFilters(projectId.toString(), { [key]: newValues }, "archived");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,11 +7,13 @@ import { TCycleFilters } from "@plane/types";
|
|||||||
// components
|
// components
|
||||||
import { CycleFiltersSelection } from "@/components/cycles";
|
import { CycleFiltersSelection } from "@/components/cycles";
|
||||||
import { FiltersDropdown } from "@/components/issues";
|
import { FiltersDropdown } from "@/components/issues";
|
||||||
|
// constants
|
||||||
|
import { CYCLES_FILTER_APPLIED, CYCLES_FILTER_REMOVED } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycleFilter } from "@/hooks/store";
|
import { useCycleFilter, useEventTracker } from "@/hooks/store";
|
||||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -24,6 +26,7 @@ export const CyclesViewHeader: React.FC<Props> = observer((props) => {
|
|||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
// hooks
|
// hooks
|
||||||
const { currentProjectFilters, searchQuery, updateFilters, updateSearchQuery } = useCycleFilter();
|
const { currentProjectFilters, searchQuery, updateFilters, updateSearchQuery } = useCycleFilter();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// states
|
// states
|
||||||
const [isSearchOpen, setIsSearchOpen] = useState(searchQuery !== "" ? true : false);
|
const [isSearchOpen, setIsSearchOpen] = useState(searchQuery !== "" ? true : false);
|
||||||
// outside click detector hook
|
// outside click detector hook
|
||||||
@ -34,7 +37,7 @@ export const CyclesViewHeader: React.FC<Props> = observer((props) => {
|
|||||||
const handleFilters = useCallback(
|
const handleFilters = useCallback(
|
||||||
(key: keyof TCycleFilters, value: string | string[]) => {
|
(key: keyof TCycleFilters, value: string | string[]) => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
const newValues = currentProjectFilters?.[key] ?? [];
|
const newValues = Array.from(currentProjectFilters?.[key] ?? []);
|
||||||
|
|
||||||
if (Array.isArray(value))
|
if (Array.isArray(value))
|
||||||
value.forEach((val) => {
|
value.forEach((val) => {
|
||||||
@ -46,6 +49,14 @@ export const CyclesViewHeader: React.FC<Props> = observer((props) => {
|
|||||||
else newValues.push(value);
|
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 });
|
updateFilters(projectId, { [key]: newValues });
|
||||||
},
|
},
|
||||||
[currentProjectFilters, projectId, updateFilters]
|
[currentProjectFilters, projectId, updateFilters]
|
||||||
|
@ -57,7 +57,6 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||||||
() => {
|
() => {
|
||||||
captureEvent(CYCLE_FAVORITED, {
|
captureEvent(CYCLE_FAVORITED, {
|
||||||
cycle_id: cycleId,
|
cycle_id: cycleId,
|
||||||
element: "List layout",
|
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -87,7 +86,6 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||||||
).then(() => {
|
).then(() => {
|
||||||
captureEvent(CYCLE_UNFAVORITED, {
|
captureEvent(CYCLE_UNFAVORITED, {
|
||||||
cycle_id: cycleId,
|
cycle_id: cycleId,
|
||||||
element: "List layout",
|
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,6 +8,7 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
|
|||||||
// components
|
// components
|
||||||
import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles";
|
import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles";
|
||||||
// constants
|
// constants
|
||||||
|
import { CYCLE_ARCHIVED, CYCLE_RESTORED, E_CYCLES } from "@/constants/event-tracker";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
import { EUserProjectRoles } from "@/constants/project";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
@ -31,7 +32,7 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
|||||||
const [archiveCycleModal, setArchiveCycleModal] = useState(false);
|
const [archiveCycleModal, setArchiveCycleModal] = useState(false);
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement, captureEvent } = useEventTracker();
|
||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceAllProjectsRole },
|
membership: { currentWorkspaceAllProjectsRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
@ -56,7 +57,7 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
|||||||
const handleOpenInNewTab = () => window.open(`/${cycleLink}`, "_blank");
|
const handleOpenInNewTab = () => window.open(`/${cycleLink}`, "_blank");
|
||||||
|
|
||||||
const handleEditCycle = () => {
|
const handleEditCycle = () => {
|
||||||
setTrackElement("Cycles page list layout");
|
setTrackElement(E_CYCLES);
|
||||||
setUpdateModal(true);
|
setUpdateModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,6 +66,9 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
|||||||
const handleRestoreCycle = async () =>
|
const handleRestoreCycle = async () =>
|
||||||
await restoreCycle(workspaceSlug, projectId, cycleId)
|
await restoreCycle(workspaceSlug, projectId, cycleId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
captureEvent(CYCLE_RESTORED, {
|
||||||
|
cycle_id: cycleId,
|
||||||
|
});
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Restore success",
|
title: "Restore success",
|
||||||
@ -81,7 +85,7 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleDeleteCycle = () => {
|
const handleDeleteCycle = () => {
|
||||||
setTrackElement("Cycles page list layout");
|
setTrackElement(E_CYCLES);
|
||||||
setDeleteModal(true);
|
setDeleteModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,6 +154,12 @@ export const CYCLE_UPDATED = "Cycle updated";
|
|||||||
export const CYCLE_DELETED = "Cycle deleted";
|
export const CYCLE_DELETED = "Cycle deleted";
|
||||||
export const CYCLE_FAVORITED = "Cycle favorited";
|
export const CYCLE_FAVORITED = "Cycle favorited";
|
||||||
export const CYCLE_UNFAVORITED = "Cycle unfavorited";
|
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
|
// Module Events
|
||||||
export const MODULE_CREATED = "Module created";
|
export const MODULE_CREATED = "Module created";
|
||||||
export const MODULE_UPDATED = "Module updated";
|
export const MODULE_UPDATED = "Module updated";
|
||||||
@ -222,3 +228,6 @@ export const SNOOZED_NOTIFICATIONS = "Snoozed notifications viewed";
|
|||||||
export const ARCHIVED_NOTIFICATIONS = "Archived notifications viewed";
|
export const ARCHIVED_NOTIFICATIONS = "Archived notifications viewed";
|
||||||
// Groups
|
// Groups
|
||||||
export const GROUP_WORKSPACE = "Workspace_metrics";
|
export const GROUP_WORKSPACE = "Workspace_metrics";
|
||||||
|
|
||||||
|
// Elements
|
||||||
|
export const E_CYCLES = "Cycles";
|
@ -12,6 +12,7 @@ import { CyclesHeader } from "@/components/headers";
|
|||||||
import { CycleModuleListLayout } from "@/components/ui";
|
import { CycleModuleListLayout } from "@/components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
|
import { CYCLES_FILTER_REMOVED } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@ -25,7 +26,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
// states
|
// states
|
||||||
const [createModal, setCreateModal] = useState(false);
|
const [createModal, setCreateModal] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement,captureEvent } = useEventTracker();
|
||||||
const { currentProjectCycleIds, loader } = useCycle();
|
const { currentProjectCycleIds, loader } = useCycle();
|
||||||
const { getProjectById, currentProjectDetails } = useProject();
|
const { getProjectById, currentProjectDetails } = useProject();
|
||||||
// router
|
// router
|
||||||
@ -45,6 +46,12 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
if (!value) newValues = [];
|
if (!value) newValues = [];
|
||||||
else newValues = newValues.filter((val) => val !== value);
|
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 });
|
updateFilters(projectId.toString(), { [key]: newValues });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user