chore: added sign-up/in, onboarding, dashboard, all-issues related events (#3595)

* chore: added event constants

* chore: added workspace events

* chore: added workspace group for events

* chore: member invitation event added

* chore: added project pages related events.

* fix: member integer role to string

* chore: added sign-up & sign-in events

* chore: added global-view related events

* chore: added notification related events

* chore: project, cycle property change added

* chore: cycle favourite, and change-properties added

* chore: module davorite, and sidebar property changes added

* fix: build errors

* chore: all events defined in constants
This commit is contained in:
Lakhan Baheti 2024-02-09 16:22:08 +05:30 committed by GitHub
parent 8d730e6680
commit 4f72ebded9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 1276 additions and 507 deletions

View file

@ -11,11 +11,13 @@ import { WorkspaceService } from "services/workspace.service";
// constants
import { USER_WORKSPACES, USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys";
import { ROLE } from "constants/workspace";
import { MEMBER_ACCEPTED } from "constants/event-tracker";
// types
import { IWorkspaceMemberInvitation } from "@plane/types";
// icons
import { CheckCircle2, Search } from "lucide-react";
import {} from "hooks/store/use-event-tracker";
import { getUserRole } from "helpers/user.helper";
type Props = {
handleNextStep: () => void;
@ -58,11 +60,19 @@ export const Invitations: React.FC<Props> = (props) => {
if (invitationsRespond.length <= 0) return;
setIsJoiningWorkspaces(true);
const invitation = invitations?.find((invitation) => invitation.id === invitationsRespond[0]);
await workspaceService
.joinWorkspaces({ invitations: invitationsRespond })
.then(async (res) => {
captureEvent("Member accepted", { ...res, state: "SUCCESS", accepted_from: "App" });
.then(async () => {
captureEvent(MEMBER_ACCEPTED, {
member_id: invitation?.id,
role: getUserRole(invitation?.role!),
project_id: undefined,
accepted_from: "App",
state: "SUCCESS",
element: "Workspace invitations page",
});
await fetchWorkspaces();
await mutate(USER_WORKSPACES);
await updateLastWorkspace();
@ -71,7 +81,14 @@ export const Invitations: React.FC<Props> = (props) => {
})
.catch((error) => {
console.error(error);
captureEvent("Member accepted", { state: "FAILED", accepted_from: "App" });
captureEvent(MEMBER_ACCEPTED, {
member_id: invitation?.id,
role: getUserRole(invitation?.role!),
project_id: undefined,
accepted_from: "App",
state: "FAILED",
element: "Workspace invitations page",
});
})
.finally(() => setIsJoiningWorkspaces(false));
};

View file

@ -18,6 +18,7 @@ import { Check, ChevronDown, Plus, XCircle } from "lucide-react";
import { WorkspaceService } from "services/workspace.service";
// hooks
import useToast from "hooks/use-toast";
import { useEventTracker } from "hooks/store";
// ui
import { Button, Input } from "@plane/ui";
// components
@ -28,6 +29,9 @@ import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
import { IUser, IWorkspace, TOnboardingSteps } from "@plane/types";
// constants
import { EUserWorkspaceRoles, ROLE } from "constants/workspace";
import { MEMBER_INVITED } from "constants/event-tracker";
// helpers
import { getUserRole } from "helpers/user.helper";
// assets
import user1 from "public/users/user-1.png";
import user2 from "public/users/user-2.png";
@ -267,6 +271,8 @@ export const InviteMembers: React.FC<Props> = (props) => {
const { setToastAlert } = useToast();
const { resolvedTheme } = useTheme();
// store hooks
const { captureEvent } = useEventTracker();
const {
control,
@ -305,6 +311,17 @@ export const InviteMembers: React.FC<Props> = (props) => {
})),
})
.then(async () => {
captureEvent(MEMBER_INVITED, {
emails: [
...payload.emails.map((email) => ({
email: email.email,
role: getUserRole(email.role),
})),
],
project_id: undefined,
state: "SUCCESS",
element: "Onboarding",
});
setToastAlert({
type: "success",
title: "Success!",
@ -313,13 +330,18 @@ export const InviteMembers: React.FC<Props> = (props) => {
await nextStep();
})
.catch((err) =>
.catch((err) => {
captureEvent(MEMBER_INVITED, {
project_id: undefined,
state: "FAILED",
element: "Onboarding",
});
setToastAlert({
type: "error",
title: "Error!",
message: err?.error,
})
);
});
});
};
const appendField = () => {

View file

@ -15,6 +15,8 @@ import CyclesTour from "public/onboarding/cycles.webp";
import ModulesTour from "public/onboarding/modules.webp";
import ViewsTour from "public/onboarding/views.webp";
import PagesTour from "public/onboarding/pages.webp";
// constants
import { PRODUCT_TOUR_SKIPPED, PRODUCT_TOUR_STARTED } from "constants/event-tracker";
type Props = {
onComplete: () => void;
@ -79,7 +81,7 @@ export const TourRoot: React.FC<Props> = observer((props) => {
const [step, setStep] = useState<TTourSteps>("welcome");
// store hooks
const { commandPalette: commandPaletteStore } = useApplication();
const { setTrackElement } = useEventTracker();
const { setTrackElement, captureEvent } = useEventTracker();
const { currentUser } = useUser();
const currentStepIndex = TOUR_STEPS.findIndex((tourStep) => tourStep.key === step);
@ -103,13 +105,22 @@ export const TourRoot: React.FC<Props> = observer((props) => {
</p>
<div className="flex h-full items-end">
<div className="mt-8 flex items-center gap-6">
<Button variant="primary" onClick={() => setStep("issues")}>
<Button
variant="primary"
onClick={() => {
captureEvent(PRODUCT_TOUR_STARTED);
setStep("issues");
}}
>
Take a Product Tour
</Button>
<button
type="button"
className="bg-transparent text-xs font-medium text-custom-primary-100 outline-custom-text-100"
onClick={onComplete}
onClick={() => {
captureEvent(PRODUCT_TOUR_SKIPPED);
onComplete();
}}
>
No thanks, I will explore it myself
</button>
@ -156,8 +167,8 @@ export const TourRoot: React.FC<Props> = observer((props) => {
<Button
variant="primary"
onClick={() => {
setTrackElement("Product tour");
onComplete();
setTrackElement("Onboarding tour");
commandPaletteStore.toggleCreateProjectModal(true);
}}
>

View file

@ -4,7 +4,7 @@ import { Controller, useForm } from "react-hook-form";
import { observer } from "mobx-react-lite";
import { Camera, User2 } from "lucide-react";
// hooks
import { useUser, useWorkspace } from "hooks/store";
import { useEventTracker, useUser, useWorkspace } from "hooks/store";
// components
import { Button, Input } from "@plane/ui";
import { OnboardingSidebar, OnboardingStepIndicator } from "components/onboarding";
@ -15,6 +15,7 @@ import { IUser } from "@plane/types";
import { FileService } from "services/file.service";
// assets
import IssuesSvg from "public/onboarding/onboarding-issues.webp";
import { USER_DETAILS } from "constants/event-tracker";
const defaultValues: Partial<IUser> = {
first_name: "",
@ -48,6 +49,7 @@ export const UserDetails: React.FC<Props> = observer((props) => {
// store hooks
const { updateCurrentUser } = useUser();
const { workspaces } = useWorkspace();
const { captureEvent } = useEventTracker();
// derived values
const workspaceName = workspaces ? Object.values(workspaces)?.[0]?.name : "New Workspace";
// form info
@ -76,7 +78,21 @@ export const UserDetails: React.FC<Props> = observer((props) => {
},
};
await updateCurrentUser(payload);
await updateCurrentUser(payload)
.then(() => {
captureEvent(USER_DETAILS, {
use_case: formData.use_case,
state: "SUCCESS",
element: "Onboarding",
});
})
.catch(() => {
captureEvent(USER_DETAILS, {
use_case: formData.use_case,
state: "FAILED",
element: "Onboarding",
});
});
};
const handleDelete = (url: string | null | undefined) => {
if (!url) return;

View file

@ -5,12 +5,13 @@ import { Button, Input } from "@plane/ui";
// types
import { IUser, IWorkspace, TOnboardingSteps } from "@plane/types";
// hooks
import { useUser, useWorkspace } from "hooks/store";
import { useEventTracker, useUser, useWorkspace } from "hooks/store";
import useToast from "hooks/use-toast";
// services
import { WorkspaceService } from "services/workspace.service";
// constants
import { RESTRICTED_URLS } from "constants/workspace";
import { WORKSPACE_CREATED } from "constants/event-tracker";
type Props = {
stepChange: (steps: Partial<TOnboardingSteps>) => Promise<void>;
@ -33,6 +34,7 @@ export const Workspace: React.FC<Props> = (props) => {
// store hooks
const { updateCurrentUser } = useUser();
const { createWorkspace, fetchWorkspaces, workspaces } = useWorkspace();
const { captureWorkspaceEvent } = useEventTracker();
// toast alert
const { setToastAlert } = useToast();
@ -46,31 +48,48 @@ export const Workspace: React.FC<Props> = (props) => {
setSlugError(false);
await createWorkspace(formData)
.then(async () => {
.then(async (res) => {
setToastAlert({
type: "success",
title: "Success!",
message: "Workspace created successfully.",
});
captureWorkspaceEvent({
eventName: WORKSPACE_CREATED,
payload: {
...res,
state: "SUCCESS",
first_time: true,
element: "Onboarding",
},
});
await fetchWorkspaces();
await completeStep();
})
.catch(() =>
.catch(() => {
captureWorkspaceEvent({
eventName: WORKSPACE_CREATED,
payload: {
state: "FAILED",
first_time: true,
element: "Onboarding",
},
});
setToastAlert({
type: "error",
title: "Error!",
message: "Workspace could not be created. Please try again.",
})
);
});
});
} else setSlugError(true);
})
.catch(() => {
.catch(() =>
setToastAlert({
type: "error",
title: "Error!",
message: "Some error occurred while creating workspace. Please try again.",
});
});
})
);
};
const completeStep = async () => {