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"; import { checkEmailValidity } from "helpers/string.helper";
// constants // constants
import { ESignUpSteps } from "components/account"; 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 // icons
import { Eye, EyeOff } from "lucide-react"; import { Eye, EyeOff } from "lucide-react";

View File

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

View File

@ -1,10 +1,10 @@
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui"; 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 { renderEmoji } from "helpers/emoji.helper";
import { IMemberRootStore } from "store/member"; import { IMemberRootStore } from "store/member";
import { IProjectStore } from "store/project/project.store"; import { IProjectStore } from "store/project/project.store";
import { IStateStore } from "store/state.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 { STATE_GROUPS } from "constants/state";
import { ILabelStore } from "store/label.store"; import { ILabelStore } from "store/label.store";

View File

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

View File

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

View File

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

View File

@ -66,7 +66,7 @@ export const GlobalViewsHeader: React.FC = observer(() => {
const activeTabElement = document.querySelector(`#global-view-${globalViewId.toString()}`); const activeTabElement = document.querySelector(`#global-view-${globalViewId.toString()}`);
if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" }); if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" });
}, [globalViewId]); }, [globalViewId, captureEvent]);
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; 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_CREATED = "Page created";
export const PAGE_UPDATED = "Page updated"; export const PAGE_UPDATED = "Page updated";
export const PAGE_DELETED = "Page deleted"; 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 // Member Events
export const MEMBER_INVITED = "Member invited"; export const MEMBER_INVITED = "Member invited";
export const MEMBER_ACCEPTED = "Member accepted"; 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"; import { Controller, useForm } from "react-hook-form";
// hooks // 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 useReloadConfirmations from "hooks/use-reload-confirmation";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// services // services
@ -29,6 +29,16 @@ import { NextPageWithLayout } from "lib/types";
import { EUserProjectRoles } from "constants/project"; import { EUserProjectRoles } from "constants/project";
import { useProjectPages } from "hooks/store/use-project-specific-pages"; import { useProjectPages } from "hooks/store/use-project-specific-pages";
import { IssuePeekOverview } from "components/issues"; 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 // services
const fileService = new FileService(); const fileService = new FileService();
@ -53,6 +63,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
currentUser, currentUser,
membership: { currentProjectRole }, membership: { currentProjectRole },
} = useUser(); } = useUser();
const { captureEvent } = useEventTracker();
// toast alert // toast alert
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -115,6 +126,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
updateDescription: updateDescriptionAction, updateDescription: updateDescriptionAction,
id: pageIdMobx, id: pageIdMobx,
isSubmitting, isSubmitting,
access,
setIsSubmitting, setIsSubmitting,
owned_by, owned_by,
is_locked, is_locked,
@ -130,13 +142,19 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
await updateDescriptionAction(formData.description_html); await updateDescriptionAction(formData.description_html);
}; };
const handleAiAssistance = async (response: string) => { const handleAiAssistance = async (question: string, response: string) => {
if (!workspaceSlug || !projectId || !pageId) return; if (!workspaceSlug || !projectId || !pageId) return;
const newDescription = `${watch("description_html")}<p>${response}</p>`; const newDescription = `${watch("description_html")}<p>${response}</p>`;
setValue("description_html", newDescription); setValue("description_html", newDescription);
editorRef.current?.setEditorValue(newDescription); editorRef.current?.setEditorValue(newDescription);
updateDescriptionAction(newDescription); updateDescriptionAction(newDescription);
captureEvent(AI_RES_USED, {
page_id: pageId,
element: "Pages detail page",
question: question,
answer: response,
});
}; };
const actionCompleteAlert = ({ const actionCompleteAlert = ({
@ -180,7 +198,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
}; };
try { 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) { } catch (error) {
actionCompleteAlert({ actionCompleteAlert({
title: `Page could not be duplicated`, title: `Page could not be duplicated`,
@ -193,7 +218,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const archivePage = async () => { const archivePage = async () => {
if (!workspaceSlug || !projectId || !pageId) return; if (!workspaceSlug || !projectId || !pageId) return;
try { 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) { } catch (error) {
actionCompleteAlert({ actionCompleteAlert({
title: `Page could not be archived`, title: `Page could not be archived`,
@ -206,7 +238,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const unArchivePage = async () => { const unArchivePage = async () => {
if (!workspaceSlug || !projectId || !pageId) return; if (!workspaceSlug || !projectId || !pageId) return;
try { 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) { } catch (error) {
actionCompleteAlert({ actionCompleteAlert({
title: `Page could not be restored`, title: `Page could not be restored`,
@ -219,7 +258,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const lockPage = async () => { const lockPage = async () => {
if (!workspaceSlug || !projectId || !pageId) return; if (!workspaceSlug || !projectId || !pageId) return;
try { 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) { } catch (error) {
actionCompleteAlert({ actionCompleteAlert({
title: `Page could not be locked`, title: `Page could not be locked`,
@ -232,7 +278,14 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const unlockPage = async () => { const unlockPage = async () => {
if (!workspaceSlug || !projectId || !pageId) return; if (!workspaceSlug || !projectId || !pageId) return;
try { 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) { } catch (error) {
actionCompleteAlert({ actionCompleteAlert({
title: `Page could not be unlocked`, 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 // this is done so that the title do not reset after gpt popover closed
reset(getValues()); reset(getValues());
}} }}
onResponse={(response) => { onResponse={handleAiAssistance}
handleAiAssistance(response); 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" placement="top-end"
button={ button={

View File

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

View File

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