mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: onboarding events added
This commit is contained in:
parent
509d5fe554
commit
e44873e15a
@ -43,6 +43,7 @@ export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
|||||||
const [email, setEmail] = useState(emailParam ? emailParam.toString() : "");
|
const [email, setEmail] = useState(emailParam ? emailParam.toString() : "");
|
||||||
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined);
|
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined);
|
||||||
const [isPasswordAutoset, setIsPasswordAutoset] = useState(true);
|
const [isPasswordAutoset, setIsPasswordAutoset] = useState(true);
|
||||||
|
const [isExistingEmail, setIsExistingEmail] = useState(false);
|
||||||
// hooks
|
// hooks
|
||||||
const { config } = useInstance();
|
const { config } = useInstance();
|
||||||
|
|
||||||
@ -101,6 +102,7 @@ export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
|||||||
setAuthStep(EAuthSteps.PASSWORD);
|
setAuthStep(EAuthSteps.PASSWORD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setIsExistingEmail(response.existing);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const errorhandler = authErrorHandler(error?.error_code?.toString(), data?.email || undefined);
|
const errorhandler = authErrorHandler(error?.error_code?.toString(), data?.email || undefined);
|
||||||
@ -138,6 +140,7 @@ export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
|||||||
<AuthUniqueCodeForm
|
<AuthUniqueCodeForm
|
||||||
mode={authMode}
|
mode={authMode}
|
||||||
email={email}
|
email={email}
|
||||||
|
isExistingEmail={isExistingEmail}
|
||||||
handleEmailClear={() => {
|
handleEmailClear={() => {
|
||||||
setEmail("");
|
setEmail("");
|
||||||
setAuthStep(EAuthSteps.EMAIL);
|
setAuthStep(EAuthSteps.EMAIL);
|
||||||
|
@ -8,7 +8,12 @@ import { Button, Input, Spinner } from "@plane/ui";
|
|||||||
// components
|
// components
|
||||||
import { ForgotPasswordPopover, PasswordStrengthMeter } from "@/components/account";
|
import { ForgotPasswordPopover, PasswordStrengthMeter } from "@/components/account";
|
||||||
// constants
|
// constants
|
||||||
import { FORGOT_PASSWORD } from "@/constants/event-tracker";
|
import {
|
||||||
|
FORGOT_PASSWORD,
|
||||||
|
SIGN_IN_WITH_CODE,
|
||||||
|
SIGN_IN_WITH_PASSWORD,
|
||||||
|
SIGN_UP_WITH_PASSWORD,
|
||||||
|
} from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { EAuthModes, EAuthSteps } from "@/helpers/authentication.helper";
|
import { EAuthModes, EAuthSteps } from "@/helpers/authentication.helper";
|
||||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||||
@ -68,6 +73,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
|
|
||||||
const redirectToUniqueCodeSignIn = async () => {
|
const redirectToUniqueCodeSignIn = async () => {
|
||||||
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
|
captureEvent(SIGN_IN_WITH_CODE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const passwordSupport =
|
const passwordSupport =
|
||||||
@ -114,7 +120,10 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
className="mt-5 space-y-4"
|
className="mt-5 space-y-4"
|
||||||
method="POST"
|
method="POST"
|
||||||
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "sign-in" : "sign-up"}/`}
|
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "sign-in" : "sign-up"}/`}
|
||||||
onSubmit={() => setIsSubmitting(true)}
|
onSubmit={() => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
captureEvent(mode === EAuthModes.SIGN_IN ? SIGN_IN_WITH_PASSWORD : SIGN_UP_WITH_PASSWORD);
|
||||||
|
}}
|
||||||
onError={() => setIsSubmitting(false)}
|
onError={() => setIsSubmitting(false)}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { CircleCheck, XCircle } from "lucide-react";
|
import { CircleCheck, XCircle } from "lucide-react";
|
||||||
import { Button, Input, Spinner } from "@plane/ui";
|
import { Button, Input, Spinner } from "@plane/ui";
|
||||||
|
// constants
|
||||||
|
import { CODE_VERIFIED } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { EAuthModes } from "@/helpers/authentication.helper";
|
import { EAuthModes } from "@/helpers/authentication.helper";
|
||||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
|
import { useEventTracker } from "@/hooks/store";
|
||||||
import useTimer from "@/hooks/use-timer";
|
import useTimer from "@/hooks/use-timer";
|
||||||
// services
|
// services
|
||||||
import { AuthService } from "@/services/auth.service";
|
import { AuthService } from "@/services/auth.service";
|
||||||
@ -15,6 +18,7 @@ const authService = new AuthService();
|
|||||||
type TAuthUniqueCodeForm = {
|
type TAuthUniqueCodeForm = {
|
||||||
mode: EAuthModes;
|
mode: EAuthModes;
|
||||||
email: string;
|
email: string;
|
||||||
|
isExistingEmail: boolean;
|
||||||
handleEmailClear: () => void;
|
handleEmailClear: () => void;
|
||||||
generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>;
|
generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>;
|
||||||
};
|
};
|
||||||
@ -30,9 +34,9 @@ const defaultValues: TUniqueCodeFormValues = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
|
export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
|
||||||
const { mode, email, handleEmailClear, generateEmailUniqueCode } = props;
|
const { mode, email, handleEmailClear, generateEmailUniqueCode, isExistingEmail } = props;
|
||||||
// hooks
|
// hooks
|
||||||
// const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
// derived values
|
// derived values
|
||||||
const defaultResetTimerValue = 5;
|
const defaultResetTimerValue = 5;
|
||||||
// states
|
// states
|
||||||
@ -73,7 +77,13 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
|
|||||||
className="mt-5 space-y-4"
|
className="mt-5 space-y-4"
|
||||||
method="POST"
|
method="POST"
|
||||||
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "magic-sign-in" : "magic-sign-up"}/`}
|
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "magic-sign-in" : "magic-sign-up"}/`}
|
||||||
onSubmit={() => setIsSubmitting(true)}
|
onSubmit={() => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
captureEvent(CODE_VERIFIED, {
|
||||||
|
state: "SUCCESS",
|
||||||
|
first_time: isExistingEmail,
|
||||||
|
});
|
||||||
|
}}
|
||||||
onError={() => setIsSubmitting(false)}
|
onError={() => setIsSubmitting(false)}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||||
|
@ -5,7 +5,7 @@ import { IUser, IWorkspace, TOnboardingSteps } from "@plane/types";
|
|||||||
// ui
|
// ui
|
||||||
import { Button, CustomSelect, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
|
import { Button, CustomSelect, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import { WORKSPACE_CREATED } from "@/constants/event-tracker";
|
import { E_ONBOARDING, WORKSPACE_CREATED } from "@/constants/event-tracker";
|
||||||
import { ORGANIZATION_SIZE, RESTRICTED_URLS } from "@/constants/workspace";
|
import { ORGANIZATION_SIZE, RESTRICTED_URLS } from "@/constants/workspace";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useUserProfile, useWorkspace } from "@/hooks/store";
|
import { useEventTracker, useUserProfile, useWorkspace } from "@/hooks/store";
|
||||||
@ -66,10 +66,10 @@ export const CreateWorkspace: React.FC<Props> = (props) => {
|
|||||||
captureWorkspaceEvent({
|
captureWorkspaceEvent({
|
||||||
eventName: WORKSPACE_CREATED,
|
eventName: WORKSPACE_CREATED,
|
||||||
payload: {
|
payload: {
|
||||||
...res,
|
workspace_id: res.id,
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
first_time: true,
|
first_time: true,
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await fetchWorkspaces();
|
await fetchWorkspaces();
|
||||||
@ -81,7 +81,7 @@ export const CreateWorkspace: React.FC<Props> = (props) => {
|
|||||||
payload: {
|
payload: {
|
||||||
state: "FAILED",
|
state: "FAILED",
|
||||||
first_time: true,
|
first_time: true,
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
setToast({
|
setToast({
|
||||||
|
@ -13,7 +13,7 @@ import { PasswordStrengthMeter } from "@/components/account";
|
|||||||
import { UserImageUploadModal } from "@/components/core";
|
import { UserImageUploadModal } from "@/components/core";
|
||||||
import { OnboardingHeader, SwitchOrDeleteAccountDropdown } from "@/components/onboarding";
|
import { OnboardingHeader, SwitchOrDeleteAccountDropdown } from "@/components/onboarding";
|
||||||
// constants
|
// constants
|
||||||
import { USER_DETAILS } from "@/constants/event-tracker";
|
import { E_ONBOARDING, USER_DETAILS, USER_PERSONALIZATION } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { getPasswordStrength } from "@/helpers/password.helper";
|
import { getPasswordStrength } from "@/helpers/password.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@ -141,7 +141,13 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
]);
|
]);
|
||||||
captureEvent(USER_DETAILS, {
|
captureEvent(USER_DETAILS, {
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
|
});
|
||||||
|
captureEvent(USER_PERSONALIZATION, {
|
||||||
|
use_case: formData.use_case,
|
||||||
|
role: formData.role,
|
||||||
|
state: "SUCCESS",
|
||||||
|
element: E_ONBOARDING,
|
||||||
});
|
});
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
@ -155,7 +161,7 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
} catch {
|
} catch {
|
||||||
captureEvent(USER_DETAILS, {
|
captureEvent(USER_DETAILS, {
|
||||||
state: "FAILED",
|
state: "FAILED",
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
});
|
});
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.ERROR,
|
type: TOAST_TYPE.ERROR,
|
||||||
@ -179,7 +185,7 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
} catch {
|
} catch {
|
||||||
captureEvent(USER_DETAILS, {
|
captureEvent(USER_DETAILS, {
|
||||||
state: "FAILED",
|
state: "FAILED",
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
});
|
});
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.ERROR,
|
type: TOAST_TYPE.ERROR,
|
||||||
@ -199,9 +205,11 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
updateUserProfile(profileUpdatePayload),
|
updateUserProfile(profileUpdatePayload),
|
||||||
totalSteps > 2 && stepChange({ profile_complete: true }),
|
totalSteps > 2 && stepChange({ profile_complete: true }),
|
||||||
]);
|
]);
|
||||||
captureEvent(USER_DETAILS, {
|
captureEvent(USER_PERSONALIZATION, {
|
||||||
|
use_case: formData.use_case,
|
||||||
|
role: formData.role,
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
});
|
});
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
@ -213,9 +221,9 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
finishOnboarding();
|
finishOnboarding();
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
captureEvent(USER_DETAILS, {
|
captureEvent(USER_PERSONALIZATION, {
|
||||||
state: "FAILED",
|
state: "FAILED",
|
||||||
element: "Onboarding",
|
element: E_ONBOARDING,
|
||||||
});
|
});
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.ERROR,
|
type: TOAST_TYPE.ERROR,
|
||||||
|
@ -192,11 +192,14 @@ export const SETUP_PASSWORD = "Password setup";
|
|||||||
export const PASSWORD_CREATE_SELECTED = "Password created";
|
export const PASSWORD_CREATE_SELECTED = "Password created";
|
||||||
export const PASSWORD_CREATE_SKIPPED = "Skipped to setup";
|
export const PASSWORD_CREATE_SKIPPED = "Skipped to setup";
|
||||||
export const SIGN_IN_WITH_PASSWORD = "Sign in with password";
|
export const SIGN_IN_WITH_PASSWORD = "Sign in with password";
|
||||||
|
export const SIGN_UP_WITH_PASSWORD = "Sign up with password";
|
||||||
|
export const SIGN_IN_WITH_CODE = "Sign in with magic link";
|
||||||
export const FORGOT_PASSWORD = "Forgot password clicked";
|
export const FORGOT_PASSWORD = "Forgot password clicked";
|
||||||
export const FORGOT_PASS_LINK = "Forgot password link generated";
|
export const FORGOT_PASS_LINK = "Forgot password link generated";
|
||||||
export const NEW_PASS_CREATED = "New password created";
|
export const NEW_PASS_CREATED = "New password created";
|
||||||
// Onboarding Events
|
// Onboarding Events
|
||||||
export const USER_DETAILS = "User details added";
|
export const USER_DETAILS = "User details added";
|
||||||
|
export const USER_PERSONALIZATION = "User personalization added";
|
||||||
export const USER_ONBOARDING_COMPLETED = "User onboarding completed";
|
export const USER_ONBOARDING_COMPLETED = "User onboarding completed";
|
||||||
// Product Tour Events
|
// Product Tour Events
|
||||||
export const PRODUCT_TOUR_STARTED = "Product tour started";
|
export const PRODUCT_TOUR_STARTED = "Product tour started";
|
||||||
@ -222,3 +225,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_ONBOARDING = "Onboarding";
|
@ -115,7 +115,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
|
|||||||
New to Plane?{" "}
|
New to Plane?{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
onClick={() => captureEvent(NAVIGATE_TO_SIGNUP, {})}
|
onClick={() => captureEvent(NAVIGATE_TO_SIGNUP)}
|
||||||
className="font-semibold text-custom-primary-100 hover:underline"
|
className="font-semibold text-custom-primary-100 hover:underline"
|
||||||
>
|
>
|
||||||
Create an account
|
Create an account
|
||||||
|
@ -9,6 +9,8 @@ import { Button, Input } from "@plane/ui";
|
|||||||
// components
|
// components
|
||||||
import { AuthBanner, PasswordStrengthMeter } from "@/components/account";
|
import { AuthBanner, PasswordStrengthMeter } from "@/components/account";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
|
// constants
|
||||||
|
import { NEW_PASS_CREATED } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import {
|
import {
|
||||||
EAuthenticationErrorCodes,
|
EAuthenticationErrorCodes,
|
||||||
@ -19,6 +21,8 @@ import {
|
|||||||
} from "@/helpers/authentication.helper";
|
} from "@/helpers/authentication.helper";
|
||||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||||
import { getPasswordStrength } from "@/helpers/password.helper";
|
import { getPasswordStrength } from "@/helpers/password.helper";
|
||||||
|
// hooks
|
||||||
|
import { useEventTracker } from "@/hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import DefaultLayout from "@/layouts/default-layout";
|
import DefaultLayout from "@/layouts/default-layout";
|
||||||
// lib
|
// lib
|
||||||
@ -66,6 +70,7 @@ const ResetPasswordPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
|
const { captureEvent } = useEventTracker();
|
||||||
|
|
||||||
const handleShowPassword = (key: keyof typeof showPassword) =>
|
const handleShowPassword = (key: keyof typeof showPassword) =>
|
||||||
setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));
|
setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||||
@ -132,6 +137,7 @@ const ResetPasswordPage: NextPageWithLayout = () => {
|
|||||||
<form
|
<form
|
||||||
className="mt-5 space-y-4"
|
className="mt-5 space-y-4"
|
||||||
method="POST"
|
method="POST"
|
||||||
|
onSubmit={() => captureEvent(NEW_PASS_CREATED)}
|
||||||
action={`${API_BASE_URL}/auth/reset-password/${uidb64?.toString()}/${token?.toString()}/`}
|
action={`${API_BASE_URL}/auth/reset-password/${uidb64?.toString()}/${token?.toString()}/`}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||||
|
@ -51,7 +51,7 @@ const SignInPage: NextPageWithLayout = observer(() => {
|
|||||||
New to Plane?{" "}
|
New to Plane?{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
onClick={() => captureEvent(NAVIGATE_TO_SIGNUP, {})}
|
onClick={() => captureEvent(NAVIGATE_TO_SIGNUP)}
|
||||||
className="font-semibold text-custom-primary-100 hover:underline"
|
className="font-semibold text-custom-primary-100 hover:underline"
|
||||||
>
|
>
|
||||||
Create an account
|
Create an account
|
||||||
|
Loading…
Reference in New Issue
Block a user