chore: updated project-pages related events

This commit is contained in:
LAKHAN BAHETI 2024-02-22 12:33:58 +05:30
parent dd54f6bd77
commit fc5901d2d6
12 changed files with 171 additions and 35 deletions

View File

@ -11,7 +11,7 @@ import { Button, Input } from "@plane/ui";
import { checkEmailValidity } from "helpers/string.helper";
// constants
import { ESignUpSteps } from "components/account";
import { PASSWORD_CREATE_SELECTED, PASSWORD_CREATE_SKIPPED, SETUP_PASSWORD } from "constants/event-tracker";
import { PASSWORD_CREATE_SKIPPED, SETUP_PASSWORD } from "constants/event-tracker";
// icons
import { Eye, EyeOff } from "lucide-react";

View File

@ -17,7 +17,9 @@ type Props = {
isOpen: boolean;
projectId: string;
handleClose: () => void;
onResponse: (response: any) => void;
onResponse: (question: string, response: any) => void;
onGenerateResponse?: (question: string, response: any) => void;
onReGenerateResponse?: (question: string, response: any) => void;
onError?: (error: any) => void;
placement?: Placement;
prompt?: string;
@ -33,7 +35,19 @@ type FormData = {
const aiService = new AIService();
export const GptAssistantPopover: React.FC<Props> = (props) => {
const { isOpen, projectId, handleClose, onResponse, onError, placement, prompt, button, className = "" } = props;
const {
isOpen,
projectId,
handleClose,
onResponse,
onGenerateResponse,
onReGenerateResponse,
onError,
placement,
prompt,
button,
className = "",
} = props;
// states
const [response, setResponse] = useState("");
const [invalidResponse, setInvalidResponse] = useState(false);
@ -55,6 +69,7 @@ export const GptAssistantPopover: React.FC<Props> = (props) => {
handleSubmit,
control,
reset,
getValues,
setFocus,
formState: { isSubmitting },
} = useForm<FormData>({
@ -120,6 +135,8 @@ export const GptAssistantPopover: React.FC<Props> = (props) => {
}
await callAIService(formData);
if (response !== "" && onReGenerateResponse) onReGenerateResponse(formData.task, response);
else if (response === "" && onGenerateResponse) onGenerateResponse(formData.task, response);
};
useEffect(() => {
@ -164,7 +181,7 @@ export const GptAssistantPopover: React.FC<Props> = (props) => {
<Button
variant="primary"
onClick={() => {
onResponse(response);
onResponse(getValues("task"), response);
onClose();
}}
>

View File

@ -1,4 +1,4 @@
import { FC, useEffect, useState, useRef, use } from "react";
import { FC, useEffect, useState, useRef } from "react";
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
import { PlusIcon } from "lucide-react";

View File

@ -1,10 +1,10 @@
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
import { EIssueListRow, ISSUE_PRIORITIES } from "constants/issue";
import { ISSUE_PRIORITIES } from "constants/issue";
import { renderEmoji } from "helpers/emoji.helper";
import { IMemberRootStore } from "store/member";
import { IProjectStore } from "store/project/project.store";
import { IStateStore } from "store/state.store";
import { GroupByColumnTypes, IGroupByColumn, IIssueListRow, TGroupedIssues, TUnGroupedIssues } from "@plane/types";
import { GroupByColumnTypes, IGroupByColumn } from "@plane/types";
import { STATE_GROUPS } from "constants/state";
import { ILabelStore } from "store/label.store";

View File

@ -1,5 +1,6 @@
import { FC, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import {
AlertCircle,
@ -13,18 +14,21 @@ import {
Star,
Trash2,
} from "lucide-react";
// hooks
import { useProjectPages } from "hooks/store/use-project-specific-pages";
import { useEventTracker, useMember, usePage, useUser } from "hooks/store";
// helpers
import { copyUrlToClipboard } from "helpers/string.helper";
import { renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper";
// ui
import { CustomMenu, Tooltip } from "@plane/ui";
// components
import { CreateUpdatePageModal, DeletePageModal } from "components/pages";
// types
import { IIssueLabel } from "@plane/types";
// constants
import { EUserProjectRoles } from "constants/project";
import { useRouter } from "next/router";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
import { useMember, usePage, useUser } from "hooks/store";
import { IIssueLabel } from "@plane/types";
import { PAGE_ARCHIVED, PAGE_FAVORITED, PAGE_RESTORED, PAGE_UNFAVORITED, PAGE_UPDATED } from "constants/event-tracker";
export interface IPagesListItem {
pageId: string;
@ -38,21 +42,21 @@ export const PagesListItem: FC<IPagesListItem> = observer(({ pageId, projectId }
const pageStore = usePage(pageId);
// states
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// states
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
const [deletePageModal, setDeletePageModal] = useState(false);
// store hooks
const {
currentUser,
membership: { currentProjectRole },
} = useUser();
const {
project: { getProjectMemberDetails },
} = useMember();
const { captureEvent } = useEventTracker();
if (!pageStore) return null;
@ -81,42 +85,81 @@ export const PagesListItem: FC<IPagesListItem> = observer(({ pageId, projectId }
const handleAddToFavorites = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
addToFavorites();
addToFavorites().then(() => {
captureEvent(PAGE_FAVORITED, {
page_id: pageId,
element: "Project pages page",
state: "SUCCESS",
});
});
};
const handleRemoveFromFavorites = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
removeFromFavorites();
removeFromFavorites().then(() => {
captureEvent(PAGE_UNFAVORITED, {
page_id: pageId,
state: "SUCCESS",
});
});
};
const handleMakePublic = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
makePublic();
makePublic().then(() => {
captureEvent(PAGE_UPDATED, {
page_id: pageId,
access: "public",
element: "Project pages page",
state: "SUCCESS",
});
});
};
const handleMakePrivate = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
makePrivate();
makePrivate().then(() => {
captureEvent(PAGE_UPDATED, {
page_id: pageId,
access: "private",
element: "Project pages page",
state: "SUCCESS",
});
});
};
const handleArchivePage = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
await archivePage(workspaceSlug as string, projectId as string, pageId as string);
await archivePage(workspaceSlug as string, projectId as string, pageId as string).then(() => {
captureEvent(PAGE_ARCHIVED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Project pages page",
state: "SUCCESS",
});
});
};
const handleRestorePage = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
await restorePage(workspaceSlug as string, projectId as string, pageId as string);
await restorePage(workspaceSlug as string, projectId as string, pageId as string).then(() => {
captureEvent(PAGE_RESTORED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Project pages page",
state: "SUCCESS",
});
});
};
const handleDeletePage = (e: React.MouseEvent<HTMLElement>) => {

View File

@ -58,7 +58,7 @@ export const DeleteWorkspaceModal: React.FC<Props> = observer((props) => {
if (!data || !canDelete) return;
await deleteWorkspace(data.slug)
.then((res) => {
.then(() => {
handleClose();
router.push("/");
captureWorkspaceEvent({

View File

@ -56,7 +56,6 @@ export const WorkspaceSidebarDropdown = observer(() => {
const {
theme: { sidebarCollapsed, toggleMobileSidebar },
} = useApplication();
const { setTrackElement } = useEventTracker();
const { currentUser, updateCurrentUser, isUserInstanceAdmin, signOut } = useUser();
const { currentWorkspace: activeWorkspace, workspaces } = useWorkspace();
// hooks

View File

@ -66,7 +66,7 @@ export const GlobalViewsHeader: React.FC = observer(() => {
const activeTabElement = document.querySelector(`#global-view-${globalViewId.toString()}`);
if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" });
}, [globalViewId]);
}, [globalViewId, captureEvent]);
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;

View File

@ -184,6 +184,17 @@ export const STATE_DELETED = "State deleted";
export const PAGE_CREATED = "Page created";
export const PAGE_UPDATED = "Page updated";
export const PAGE_DELETED = "Page deleted";
export const PAGE_FAVORITED = "Page favorited";
export const PAGE_UNFAVORITED = "Page unfavorited";
export const PAGE_ARCHIVED = "Page archived";
export const PAGE_LOCKED = "Page locked";
export const PAGE_UNLOCKED = "Page unlocked";
export const PAGE_DUPLICATED = "Page duplicated";
export const PAGE_RESTORED = "Page restored";
// AI Assistant Events
export const AI_TRIGGERED = "AI triggered";
export const AI_RES_USED = "AI response used";
export const AI_RES_REGENERATED = "AI response regenerated";
// Member Events
export const MEMBER_INVITED = "Member invited";
export const MEMBER_ACCEPTED = "Member accepted";

View File

@ -6,7 +6,7 @@ import { ReactElement, useEffect, useRef, useState } from "react";
import { Controller, useForm } from "react-hook-form";
// hooks
import { useApplication, usePage, useUser, useWorkspace } from "hooks/store";
import { useApplication, useEventTracker, usePage, useUser, useWorkspace } from "hooks/store";
import useReloadConfirmations from "hooks/use-reload-confirmation";
import useToast from "hooks/use-toast";
// services
@ -29,6 +29,16 @@ import { NextPageWithLayout } from "lib/types";
import { EUserProjectRoles } from "constants/project";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
import { IssuePeekOverview } from "components/issues";
import {
AI_RES_REGENERATED,
AI_RES_USED,
AI_TRIGGERED,
PAGE_ARCHIVED,
PAGE_DUPLICATED,
PAGE_LOCKED,
PAGE_RESTORED,
PAGE_UNLOCKED,
} from "constants/event-tracker";
// services
const fileService = new FileService();
@ -53,6 +63,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
currentUser,
membership: { currentProjectRole },
} = useUser();
const { captureEvent } = useEventTracker();
// toast alert
const { setToastAlert } = useToast();
@ -115,6 +126,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
updateDescription: updateDescriptionAction,
id: pageIdMobx,
isSubmitting,
access,
setIsSubmitting,
owned_by,
is_locked,
@ -130,13 +142,19 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
await updateDescriptionAction(formData.description_html);
};
const handleAiAssistance = async (response: string) => {
const handleAiAssistance = async (question: string, response: string) => {
if (!workspaceSlug || !projectId || !pageId) return;
const newDescription = `${watch("description_html")}<p>${response}</p>`;
setValue("description_html", newDescription);
editorRef.current?.setEditorValue(newDescription);
updateDescriptionAction(newDescription);
captureEvent(AI_RES_USED, {
page_id: pageId,
element: "Pages detail page",
question: question,
answer: response,
});
};
const actionCompleteAlert = ({
@ -180,7 +198,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
};
try {
await createPage(formData);
await createPage(formData).then(() => {
captureEvent(PAGE_DUPLICATED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Pages detail page",
state: "SUCCESS",
});
});
} catch (error) {
actionCompleteAlert({
title: `Page could not be duplicated`,
@ -193,7 +218,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const archivePage = async () => {
if (!workspaceSlug || !projectId || !pageId) return;
try {
await archivePageAction(workspaceSlug as string, projectId as string, pageId as string);
await archivePageAction(workspaceSlug as string, projectId as string, pageId as string).then(() => {
captureEvent(PAGE_ARCHIVED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Pages detail page",
state: "SUCCESS",
});
});
} catch (error) {
actionCompleteAlert({
title: `Page could not be archived`,
@ -206,7 +238,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const unArchivePage = async () => {
if (!workspaceSlug || !projectId || !pageId) return;
try {
await restorePageAction(workspaceSlug as string, projectId as string, pageId as string);
await restorePageAction(workspaceSlug as string, projectId as string, pageId as string).then(() => {
captureEvent(PAGE_RESTORED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Pages detail page",
state: "SUCCESS",
});
});
} catch (error) {
actionCompleteAlert({
title: `Page could not be restored`,
@ -219,7 +258,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const lockPage = async () => {
if (!workspaceSlug || !projectId || !pageId) return;
try {
await lockPageAction();
await lockPageAction().then(() => {
captureEvent(PAGE_LOCKED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Pages detail page",
state: "SUCCESS",
});
});
} catch (error) {
actionCompleteAlert({
title: `Page could not be locked`,
@ -232,7 +278,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const unlockPage = async () => {
if (!workspaceSlug || !projectId || !pageId) return;
try {
await unlockPageAction();
await unlockPageAction().then(() => {
captureEvent(PAGE_UNLOCKED, {
page_id: pageId,
access: access == 1 ? "private" : "public",
element: "Pages detail page",
state: "SUCCESS",
});
});
} catch (error) {
actionCompleteAlert({
title: `Page could not be unlocked`,
@ -342,8 +395,21 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
// this is done so that the title do not reset after gpt popover closed
reset(getValues());
}}
onResponse={(response) => {
handleAiAssistance(response);
onResponse={handleAiAssistance}
onGenerateResponse={(question) => {
captureEvent(AI_TRIGGERED, {
page_id: pageId,
element: "Pages detail page",
question: question,
});
}}
onReGenerateResponse={(question, response) => {
captureEvent(AI_RES_REGENERATED, {
page_id: pageId,
element: "Pages detail page",
question: question,
prev_answer: response,
});
}}
placement="top-end"
button={

View File

@ -31,7 +31,7 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
const router = useRouter();
const { workspaceSlug } = router.query;
// store hooks
const { captureEvent, setTrackElement } = useEventTracker();
const { captureEvent } = useEventTracker();
const {
membership: { currentWorkspaceRole },
} = useUser();

View File

@ -80,7 +80,7 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => {
workspaceService
.joinWorkspaces({ invitations: invitationsRespond })
.then((res) => {
.then(() => {
mutate("USER_WORKSPACES");
const firstInviteId = invitationsRespond[0];
const invitation = invitations?.find((i) => i.id === firstInviteId);