mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
added project-labels events
This commit is contained in:
parent
ffc987bcc8
commit
3f88694366
@ -6,13 +6,13 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
import { Dialog, Popover, Transition } from "@headlessui/react";
|
import { Dialog, Popover, Transition } from "@headlessui/react";
|
||||||
import type { IIssueLabel, IState } from "@plane/types";
|
import type { IIssueLabel, IState } from "@plane/types";
|
||||||
// hooks
|
|
||||||
import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui";
|
|
||||||
import { LABEL_COLOR_OPTIONS, getRandomLabelColor } from "@/constants/label";
|
|
||||||
import { useLabel } from "@/hooks/store";
|
|
||||||
// ui
|
// ui
|
||||||
// types
|
import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
|
import { E_STATES, LABEL_CREATED } from "constants/event-tracker";
|
||||||
|
import { LABEL_COLOR_OPTIONS, getRandomLabelColor } from "@/constants/label";
|
||||||
|
// hooks
|
||||||
|
import { useLabel, useEventTracker } from "@/hooks/store";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -34,6 +34,7 @@ export const CreateLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { createLabel } = useLabel();
|
const { createLabel } = useLabel();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// form info
|
// form info
|
||||||
const {
|
const {
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
@ -70,6 +71,13 @@ export const CreateLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
onClose();
|
onClose();
|
||||||
if (onSuccess) onSuccess(res);
|
if (onSuccess) onSuccess(res);
|
||||||
|
captureEvent(LABEL_CREATED, {
|
||||||
|
label_id: res.id,
|
||||||
|
color: res.color,
|
||||||
|
parent: res.parent,
|
||||||
|
element: E_STATES,
|
||||||
|
state: "SUCCESS",
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setToast({
|
setToast({
|
||||||
@ -78,6 +86,9 @@ export const CreateLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
message: error?.detail ?? "Something went wrong. Please try again later.",
|
message: error?.detail ?? "Something went wrong. Please try again later.",
|
||||||
});
|
});
|
||||||
reset(formData);
|
reset(formData);
|
||||||
|
captureEvent(LABEL_CREATED, {
|
||||||
|
state: "FAILED",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,9 +8,10 @@ import { IIssueLabel } from "@plane/types";
|
|||||||
// ui
|
// ui
|
||||||
import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui";
|
import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
|
import { E_Labels, LABEL_CREATED, LABEL_UPDATED } from "constants/event-tracker";
|
||||||
import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "@/constants/label";
|
import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "@/constants/label";
|
||||||
// hooks
|
// hooks
|
||||||
import { useLabel } from "@/hooks/store";
|
import { useLabel, useEventTracker } from "@/hooks/store";
|
||||||
// types
|
// types
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -34,12 +35,13 @@ export const CreateUpdateLabelInline = observer(
|
|||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { createLabel, updateLabel } = useLabel();
|
const { createLabel, updateLabel } = useLabel();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// form info
|
// form info
|
||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
reset,
|
reset,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting, dirtyFields },
|
||||||
watch,
|
watch,
|
||||||
setValue,
|
setValue,
|
||||||
setFocus,
|
setFocus,
|
||||||
@ -57,7 +59,14 @@ export const CreateUpdateLabelInline = observer(
|
|||||||
if (!workspaceSlug || !projectId || isSubmitting) return;
|
if (!workspaceSlug || !projectId || isSubmitting) return;
|
||||||
|
|
||||||
await createLabel(workspaceSlug.toString(), projectId.toString(), formData)
|
await createLabel(workspaceSlug.toString(), projectId.toString(), formData)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
|
captureEvent(LABEL_CREATED, {
|
||||||
|
label_id: res.id,
|
||||||
|
color: res.color,
|
||||||
|
parent: res.parent,
|
||||||
|
element: E_Labels,
|
||||||
|
state: "SUCCESS",
|
||||||
|
});
|
||||||
handleClose();
|
handleClose();
|
||||||
reset(defaultValues);
|
reset(defaultValues);
|
||||||
})
|
})
|
||||||
@ -76,9 +85,17 @@ export const CreateUpdateLabelInline = observer(
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||||
await updateLabel(workspaceSlug.toString(), projectId.toString(), labelToUpdate?.id!, formData)
|
await updateLabel(workspaceSlug.toString(), projectId.toString(), labelToUpdate?.id!, formData)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
reset(defaultValues);
|
reset(defaultValues);
|
||||||
handleClose();
|
handleClose();
|
||||||
|
captureEvent(LABEL_UPDATED, {
|
||||||
|
label_id: res.id,
|
||||||
|
color: res.color,
|
||||||
|
parent: res.parent,
|
||||||
|
change_details: Object.keys(dirtyFields),
|
||||||
|
element: E_Labels,
|
||||||
|
state: "SUCCESS",
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setToast({
|
setToast({
|
||||||
|
@ -7,8 +7,10 @@ import type { IIssueLabel } from "@plane/types";
|
|||||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { AlertModalCore } from "@/components/core";
|
import { AlertModalCore } from "@/components/core";
|
||||||
|
// constants
|
||||||
|
import { E_Labels, LABEL_DELETED, LABEL_GROUP_DELETED } from "constants/event-tracker";
|
||||||
// hooks
|
// hooks
|
||||||
import { useLabel } from "@/hooks/store";
|
import { useLabel, useEventTracker } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -22,7 +24,8 @@ export const DeleteLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { deleteLabel } = useLabel();
|
const { deleteLabel, projectLabelsTree } = useLabel();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// states
|
// states
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
@ -39,6 +42,21 @@ export const DeleteLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
await deleteLabel(workspaceSlug.toString(), projectId.toString(), data.id)
|
await deleteLabel(workspaceSlug.toString(), projectId.toString(), data.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
handleClose();
|
handleClose();
|
||||||
|
const labelChildCount = projectLabelsTree?.find((label) => label.id === data.id)?.children?.length || 0;
|
||||||
|
if (labelChildCount > 0) {
|
||||||
|
captureEvent(LABEL_GROUP_DELETED, {
|
||||||
|
group_id: data.id,
|
||||||
|
children_count: labelChildCount,
|
||||||
|
element: E_Labels,
|
||||||
|
state: "SUCCESS",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
captureEvent(LABEL_DELETED, {
|
||||||
|
label_id: data.id,
|
||||||
|
element: E_Labels,
|
||||||
|
state: "SUCCESS",
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { MutableRefObject, useEffect, useRef, useState } from "react";
|
import { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||||
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
||||||
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
import {
|
||||||
|
ElementDragPayload,
|
||||||
|
draggable,
|
||||||
|
dropTargetForElements,
|
||||||
|
} from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
||||||
import { pointerOutsideOfPreview } from "@atlaskit/pragmatic-drag-and-drop/element/pointer-outside-of-preview";
|
import { pointerOutsideOfPreview } from "@atlaskit/pragmatic-drag-and-drop/element/pointer-outside-of-preview";
|
||||||
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
|
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
|
||||||
import { attachInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
|
import { attachInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
|
||||||
@ -10,6 +14,10 @@ import { createRoot } from "react-dom/client";
|
|||||||
import { IIssueLabel, InstructionType } from "@plane/types";
|
import { IIssueLabel, InstructionType } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { DropIndicator } from "@plane/ui";
|
import { DropIndicator } from "@plane/ui";
|
||||||
|
// constants
|
||||||
|
import { LABEL_ADDED_G, LABEL_REMOVED_G } from "@/constants/event-tracker";
|
||||||
|
// hooks
|
||||||
|
import { useEventTracker } from "@/hooks/store";
|
||||||
// components
|
// components
|
||||||
import { LabelName } from "./label-block/label-name";
|
import { LabelName } from "./label-block/label-name";
|
||||||
import { TargetData, getCanDrop, getInstructionFromPayload } from "./label-utils";
|
import { TargetData, getCanDrop, getInstructionFromPayload } from "./label-utils";
|
||||||
@ -52,10 +60,28 @@ export const LabelDndHOC = observer((props: Props) => {
|
|||||||
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [instruction, setInstruction] = useState<InstructionType | undefined>(undefined);
|
const [instruction, setInstruction] = useState<InstructionType | undefined>(undefined);
|
||||||
|
// hooks
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
// refs
|
// refs
|
||||||
const labelRef = useRef<HTMLDivElement | null>(null);
|
const labelRef = useRef<HTMLDivElement | null>(null);
|
||||||
const dragHandleRef = useRef<HTMLButtonElement | null>(null);
|
const dragHandleRef = useRef<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
const captureLabelDropEvent = (source: TargetData, destination: TargetData) => {
|
||||||
|
|
||||||
|
if (source?.parentId != destination.id) {
|
||||||
|
source?.parentId &&
|
||||||
|
captureEvent(LABEL_REMOVED_G, {
|
||||||
|
group_id: source?.parentId,
|
||||||
|
child_id: source?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
captureEvent(LABEL_ADDED_G, {
|
||||||
|
group_id: destination.id,
|
||||||
|
child_id: source?.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const element = labelRef.current;
|
const element = labelRef.current;
|
||||||
const dragHandleElement = dragHandleRef.current;
|
const dragHandleElement = dragHandleRef.current;
|
||||||
@ -144,6 +170,8 @@ export const LabelDndHOC = observer((props: Props) => {
|
|||||||
|
|
||||||
const sourceData = source.data as TargetData;
|
const sourceData = source.data as TargetData;
|
||||||
if (sourceData.id) onDrop(sourceData.id as string, parentId, droppedLabelId, dropAtEndOfList);
|
if (sourceData.id) onDrop(sourceData.id as string, parentId, droppedLabelId, dropAtEndOfList);
|
||||||
|
|
||||||
|
captureLabelDropEvent(sourceData, dropTargetData);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,9 @@ import { X, Pencil } from "lucide-react";
|
|||||||
// types
|
// types
|
||||||
import { IIssueLabel } from "@plane/types";
|
import { IIssueLabel } from "@plane/types";
|
||||||
// hooks
|
// hooks
|
||||||
import { useLabel } from "@/hooks/store";
|
import { useLabel, useEventTracker } from "@/hooks/store";
|
||||||
|
// constants
|
||||||
|
import { E_Labels, LABEL_REMOVED_G } from "@/constants/event-tracker";
|
||||||
// components
|
// components
|
||||||
import { CreateUpdateLabelInline } from "./create-update-label-inline";
|
import { CreateUpdateLabelInline } from "./create-update-label-inline";
|
||||||
import { ICustomMenuItem, LabelItemBlock } from "./label-block/label-item-block";
|
import { ICustomMenuItem, LabelItemBlock } from "./label-block/label-item-block";
|
||||||
@ -34,6 +36,7 @@ export const ProjectSettingLabelItem: React.FC<Props> = (props) => {
|
|||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { updateLabel } = useLabel();
|
const { updateLabel } = useLabel();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
|
|
||||||
const removeFromGroup = (label: IIssueLabel) => {
|
const removeFromGroup = (label: IIssueLabel) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
@ -41,6 +44,12 @@ export const ProjectSettingLabelItem: React.FC<Props> = (props) => {
|
|||||||
updateLabel(workspaceSlug.toString(), projectId.toString(), label.id, {
|
updateLabel(workspaceSlug.toString(), projectId.toString(), label.id, {
|
||||||
parent: null,
|
parent: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
captureEvent(LABEL_REMOVED_G, {
|
||||||
|
group_id: label.id,
|
||||||
|
child_id: label.id,
|
||||||
|
element: E_Labels,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const customMenuItems: ICustomMenuItem[] = [
|
const customMenuItems: ICustomMenuItem[] = [
|
||||||
|
@ -175,6 +175,13 @@ export const ISSUE_OPENED = "Issue opened";
|
|||||||
export const STATE_CREATED = "State created";
|
export const STATE_CREATED = "State created";
|
||||||
export const STATE_UPDATED = "State updated";
|
export const STATE_UPDATED = "State updated";
|
||||||
export const STATE_DELETED = "State deleted";
|
export const STATE_DELETED = "State deleted";
|
||||||
|
// Label Events
|
||||||
|
export const LABEL_CREATED = "Label created";
|
||||||
|
export const LABEL_UPDATED = "Label updated";
|
||||||
|
export const LABEL_DELETED = "Label deleted";
|
||||||
|
export const LABEL_GROUP_DELETED = "Label group deleted";
|
||||||
|
export const LABEL_ADDED_G = "Label added to group";
|
||||||
|
export const LABEL_REMOVED_G = "Label removed from group";
|
||||||
// Project Page Events
|
// Project Page Events
|
||||||
export const PAGE_CREATED = "Page created";
|
export const PAGE_CREATED = "Page created";
|
||||||
export const PAGE_UPDATED = "Page updated";
|
export const PAGE_UPDATED = "Page updated";
|
||||||
@ -225,4 +232,5 @@ export const ARCHIVED_NOTIFICATIONS = "Archived notifications viewed";
|
|||||||
export const GROUP_WORKSPACE = "Workspace_metrics";
|
export const GROUP_WORKSPACE = "Workspace_metrics";
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
export const E_STATES = "Project states page";
|
export const E_STATES = "Project states page";
|
||||||
|
export const E_Labels = "Project labels page";
|
||||||
|
Loading…
Reference in New Issue
Block a user