();
const { t } = useTranslation();
// derived values
const coverImage = watch("cover_image_url");
@@ -37,14 +48,18 @@ function ProjectCreateHeader(props: Props) {
alt={t("project_cover_image_alt")}
className="absolute left-0 top-0 h-full w-full rounded-lg"
/>
-
-
-
-
+ {showActionButtons && (
+
+ )}
+ {isClosable && (
+
+
+
+ )}
(
{
+ onChange(data);
+ handleFormOnChange?.();
+ }}
control={control}
- onChange={onChange}
value={value ?? null}
tabIndex={getIndex("cover_image")}
/>
@@ -72,7 +90,7 @@ function ProjectCreateHeader(props: Props) {
className="flex items-center justify-center"
buttonClassName="flex items-center justify-center"
label={
-
+
}
@@ -85,15 +103,20 @@ function ProjectCreateHeader(props: Props) {
};
else if (val?.type === "icon") logoValue = val.value;
- onChange({
+ const newLogoProps = {
in_use: val?.type,
[val?.type]: logoValue,
+ };
+ setValue("logo_props", newLogoProps, {
+ shouldDirty: true,
});
+ onChange(newLogoProps);
+ handleFormOnChange?.();
setIsOpen(false);
}}
- defaultIconColor={value.in_use && value.in_use === "icon" ? value.icon?.color : undefined}
+ defaultIconColor={value?.in_use && value.in_use === "icon" ? value.icon?.color : undefined}
defaultOpen={
- value.in_use && value.in_use === "emoji" ? EmojiIconPickerTypes.EMOJI : EmojiIconPickerTypes.ICON
+ value?.in_use && value.in_use === "emoji" ? EmojiIconPickerTypes.EMOJI : EmojiIconPickerTypes.ICON
}
/>
)}
diff --git a/apps/web/core/components/project/delete-project-modal.tsx b/apps/web/core/components/project/delete-project-modal.tsx
index 8b4f33f39f..3a0276fee9 100644
--- a/apps/web/core/components/project/delete-project-modal.tsx
+++ b/apps/web/core/components/project/delete-project-modal.tsx
@@ -1,16 +1,12 @@
import { useParams } from "next/navigation";
import { Controller, useForm } from "react-hook-form";
import { AlertTriangle } from "lucide-react";
-// types
-import { PROJECT_TRACKER_EVENTS } from "@plane/constants";
+// Plane imports
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IProject } from "@plane/types";
-// ui
import { Input, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
-// constants
// hooks
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
import { useProject } from "@/hooks/store/use-project";
import { useAppRouter } from "@/hooks/use-app-router";
@@ -55,36 +51,22 @@ export function DeleteProjectModal(props: DeleteProjectModal) {
const onSubmit = async () => {
if (!workspaceSlug || !canDelete) return;
- await deleteProject(workspaceSlug.toString(), project.id)
- .then(() => {
- if (projectId && projectId.toString() === project.id) router.push(`/${workspaceSlug}/projects`);
-
- handleClose();
- captureSuccess({
- eventName: PROJECT_TRACKER_EVENTS.delete,
- payload: {
- id: project.id,
- },
- });
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Success!",
- message: "Project deleted successfully.",
- });
- })
- .catch(() => {
- captureError({
- eventName: PROJECT_TRACKER_EVENTS.delete,
- payload: {
- id: project.id,
- },
- });
- setToast({
- type: TOAST_TYPE.ERROR,
- title: "Error!",
- message: "Something went wrong. Please try again later.",
- });
+ try {
+ await deleteProject(workspaceSlug.toString(), project.id);
+ if (projectId && projectId.toString() === project.id) router.push(`/${workspaceSlug}/projects`);
+ handleClose();
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: "Success!",
+ message: "Project deleted successfully.",
});
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: "Error!",
+ message: "Something went wrong. Please try again later.",
+ });
+ }
};
return (
diff --git a/apps/web/core/components/project/form.tsx b/apps/web/core/components/project/form.tsx
index cc405aaab6..634275c40f 100644
--- a/apps/web/core/components/project/form.tsx
+++ b/apps/web/core/components/project/form.tsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { Info } from "lucide-react";
-import { NETWORK_CHOICES, PROJECT_TRACKER_ELEMENTS, PROJECT_TRACKER_EVENTS } from "@plane/constants";
+import { NETWORK_CHOICES } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
// plane imports
import { Button } from "@plane/propel/button";
@@ -18,7 +18,6 @@ import { ImagePickerPopover } from "@/components/core/image-picker-popover";
import { TimezoneSelect } from "@/components/global";
// helpers
import { handleCoverImageChange } from "@/helpers/cover-image.helper";
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { usePlatformOS } from "@/hooks/use-platform-os";
@@ -87,12 +86,6 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
if (!workspaceSlug || !project) return;
return updateProject(workspaceSlug.toString(), project.id, payload)
.then(() => {
- captureSuccess({
- eventName: PROJECT_TRACKER_EVENTS.update,
- payload: {
- id: projectId,
- },
- });
setToast({
type: TOAST_TYPE.SUCCESS,
title: t("toast.success"),
@@ -101,13 +94,6 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
})
.catch((err) => {
try {
- captureError({
- eventName: PROJECT_TRACKER_EVENTS.update,
- payload: {
- id: projectId,
- },
- });
-
// Handle the new error format where codes are nested in arrays under field names
const errorData = err ?? {};
@@ -437,14 +423,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
<>
-
{secondaryButton}
-
+
{isSubmitting ? t(primaryButtonText.loading) : t(primaryButtonText.default)}
{!secondaryButton && (
diff --git a/apps/web/core/components/workspace/delete-workspace-form.tsx b/apps/web/core/components/workspace/delete-workspace-form.tsx
index 52603caabb..c0041d8ab6 100644
--- a/apps/web/core/components/workspace/delete-workspace-form.tsx
+++ b/apps/web/core/components/workspace/delete-workspace-form.tsx
@@ -1,18 +1,14 @@
-import React from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { AlertTriangle } from "lucide-react";
-// types
-import { WORKSPACE_TRACKER_EVENTS } from "@plane/constants";
+// Plane Imports
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspace } from "@plane/types";
-// ui
import { Input } from "@plane/ui";
-// hooks
import { cn } from "@plane/utils";
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
+// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserSettings } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
@@ -59,33 +55,23 @@ export const DeleteWorkspaceForm = observer(function DeleteWorkspaceForm(props:
const onSubmit = async () => {
if (!data || !canDelete) return;
- await deleteWorkspace(data.slug)
- .then(async () => {
- await fetchCurrentUserSettings();
- handleClose();
- router.push(getWorkspaceRedirectionUrl());
- captureSuccess({
- eventName: WORKSPACE_TRACKER_EVENTS.delete,
- payload: { slug: data.slug },
- });
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: t("workspace_settings.settings.general.delete_modal.success_title"),
- message: t("workspace_settings.settings.general.delete_modal.success_message"),
- });
- })
- .catch(() => {
- setToast({
- type: TOAST_TYPE.ERROR,
- title: t("workspace_settings.settings.general.delete_modal.error_title"),
- message: t("workspace_settings.settings.general.delete_modal.error_message"),
- });
- captureError({
- eventName: WORKSPACE_TRACKER_EVENTS.delete,
- payload: { slug: data.slug },
- error: new Error("Error deleting workspace"),
- });
+ try {
+ await deleteWorkspace(data.slug);
+ await fetchCurrentUserSettings();
+ handleClose();
+ router.push(getWorkspaceRedirectionUrl());
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: t("workspace_settings.settings.general.delete_modal.success_title"),
+ message: t("workspace_settings.settings.general.delete_modal.success_message"),
});
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: t("workspace_settings.settings.general.delete_modal.error_title"),
+ message: t("workspace_settings.settings.general.delete_modal.error_message"),
+ });
+ }
};
return (
diff --git a/apps/web/core/components/workspace/settings/invitations-list-item.tsx b/apps/web/core/components/workspace/settings/invitations-list-item.tsx
index eded4bbc30..12b4cc821f 100644
--- a/apps/web/core/components/workspace/settings/invitations-list-item.tsx
+++ b/apps/web/core/components/workspace/settings/invitations-list-item.tsx
@@ -1,9 +1,8 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
-
// plane imports
-import { ROLE, EUserPermissions, EUserPermissionsLevel, MEMBER_TRACKER_ELEMENTS } from "@plane/constants";
+import { ROLE, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { LinkIcon, TrashIcon, ChevronDownIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
@@ -13,10 +12,8 @@ import { cn, copyTextToClipboard } from "@plane/utils";
// components
import { ConfirmWorkspaceMemberRemove } from "@/components/workspace/confirm-workspace-member-remove";
// hooks
-import { captureClick } from "@/helpers/event-tracker.helper";
import { useMember } from "@/hooks/store/use-member";
import { useUserPermissions } from "@/hooks/store/user";
-import { useWorkspace } from "@/hooks/store/use-workspace";
type Props = {
invitationId: string;
@@ -32,7 +29,6 @@ export const WorkspaceInvitationsListItem = observer(function WorkspaceInvitatio
const { t } = useTranslation();
// store hooks
const { allowPermissions, workspaceInfoBySlug } = useUserPermissions();
- const { mutateWorkspaceMembersActivity } = useWorkspace();
const {
workspace: { updateMemberInvitation, deleteMemberInvitation, getWorkspaceInvitationDetails },
} = useMember();
@@ -61,7 +57,6 @@ export const WorkspaceInvitationsListItem = observer(function WorkspaceInvitatio
title: "Success!",
message: "Invitation removed successfully.",
});
- void mutateWorkspaceMembersActivity(workspaceSlug);
} catch (err: unknown) {
const error = err as { error?: string };
setToast({
@@ -99,9 +94,6 @@ export const WorkspaceInvitationsListItem = observer(function WorkspaceInvitatio
{
key: "remove",
action: () => {
- captureClick({
- elementName: MEMBER_TRACKER_ELEMENTS.WORKSPACE_INVITATIONS_LIST_CONTEXT_MENU,
- });
setRemoveMemberModal(true);
},
title: t("common.remove"),
diff --git a/apps/web/core/components/workspace/settings/member-columns.tsx b/apps/web/core/components/workspace/settings/member-columns.tsx
index b98438b1d4..4167a79397 100644
--- a/apps/web/core/components/workspace/settings/member-columns.tsx
+++ b/apps/web/core/components/workspace/settings/member-columns.tsx
@@ -16,8 +16,6 @@ import { getFileURL } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
import { useUser, useUserPermissions } from "@/hooks/store/user";
-import { useWorkspace } from "@/hooks/store/use-workspace";
-// plane web constants
export interface RowData {
member: IWorkspaceMember;
@@ -121,7 +119,6 @@ export const AccountTypeColumn = observer(function AccountTypeColumn(props: Acco
const {
workspace: { updateMember },
} = useMember();
- const { mutateWorkspaceMembersActivity } = useWorkspace();
const { data: currentUser } = useUser();
// derived values
@@ -156,7 +153,6 @@ export const AccountTypeColumn = observer(function AccountTypeColumn(props: Acco
await updateMember(workspaceSlug.toString(), rowData.member.id, {
role: value as unknown as EUserPermissions,
});
- void mutateWorkspaceMembersActivity(workspaceSlug);
} catch (err: unknown) {
const error = err as { error?: string | string[] };
const errorString = Array.isArray(error?.error) ? error.error[0] : error?.error;
diff --git a/apps/web/core/components/workspace/settings/members-list-item.tsx b/apps/web/core/components/workspace/settings/members-list-item.tsx
index f980e4eee3..9499110ee5 100644
--- a/apps/web/core/components/workspace/settings/members-list-item.tsx
+++ b/apps/web/core/components/workspace/settings/members-list-item.tsx
@@ -1,7 +1,6 @@
import { isEmpty } from "lodash-es";
import { observer } from "mobx-react";
// plane imports
-import { MEMBER_TRACKER_EVENTS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspaceMember } from "@plane/types";
@@ -10,8 +9,6 @@ import { Table } from "@plane/ui";
import { MembersLayoutLoader } from "@/components/ui/loader/layouts/members-layout-loader";
import { ConfirmWorkspaceMemberRemove } from "@/components/workspace/confirm-workspace-member-remove";
import type { RowData } from "@/components/workspace/settings/member-columns";
-// helpers
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
// hooks
import { useMember } from "@/hooks/store/use-member";
import { useWorkspace } from "@/hooks/store/use-workspace";
@@ -35,7 +32,7 @@ export const WorkspaceMembersListItem = observer(function WorkspaceMembersListIt
workspace: { removeMemberFromWorkspace },
} = useMember();
const { leaveWorkspace } = useUserPermissions();
- const { getWorkspaceRedirectionUrl, mutateWorkspaceMembersActivity } = useWorkspace();
+ const { getWorkspaceRedirectionUrl } = useWorkspace();
const { fetchCurrentUserSettings } = useUserSettings();
const { t } = useTranslation();
// derived values
@@ -47,22 +44,8 @@ export const WorkspaceMembersListItem = observer(function WorkspaceMembersListIt
await leaveWorkspace(workspaceSlug.toString());
await fetchCurrentUserSettings();
router.push(getWorkspaceRedirectionUrl());
- captureSuccess({
- eventName: MEMBER_TRACKER_EVENTS.workspace.leave,
- payload: {
- workspace: workspaceSlug,
- },
- });
} catch (err: unknown) {
const error = err as { error?: string };
- const errorForCapture: Error | string = err instanceof Error ? err : String(err);
- captureError({
- eventName: MEMBER_TRACKER_EVENTS.workspace.leave,
- payload: {
- workspace: workspaceSlug,
- },
- error: errorForCapture,
- });
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -76,7 +59,6 @@ export const WorkspaceMembersListItem = observer(function WorkspaceMembersListIt
try {
await removeMemberFromWorkspace(workspaceSlug.toString(), memberId);
- void mutateWorkspaceMembersActivity(workspaceSlug);
} catch (err: unknown) {
const error = err as { error?: string };
setToast({
diff --git a/apps/web/core/components/workspace/settings/workspace-details.tsx b/apps/web/core/components/workspace/settings/workspace-details.tsx
index bc13dfbd25..5bcc832d2d 100644
--- a/apps/web/core/components/workspace/settings/workspace-details.tsx
+++ b/apps/web/core/components/workspace/settings/workspace-details.tsx
@@ -1,15 +1,8 @@
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
-
-// constants
-import {
- ORGANIZATION_SIZE,
- EUserPermissions,
- EUserPermissionsLevel,
- WORKSPACE_TRACKER_EVENTS,
- WORKSPACE_TRACKER_ELEMENTS,
-} from "@plane/constants";
+// Plane Imports
+import { ORGANIZATION_SIZE, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { EditIcon } from "@plane/propel/icons";
@@ -19,9 +12,8 @@ import { CustomSelect, Input } from "@plane/ui";
import { copyUrlToClipboard, getFileURL } from "@plane/utils";
// components
import { WorkspaceImageUploadModal } from "@/components/core/modals/workspace-image-upload-modal";
-// helpers
import { TimezoneSelect } from "@/components/global/timezone-select";
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
+// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserPermissions } from "@/hooks/store/user";
// plane web components
@@ -70,21 +62,12 @@ export const WorkspaceDetails = observer(function WorkspaceDetails() {
try {
await updateWorkspace(currentWorkspace.slug, payload);
- captureSuccess({
- eventName: WORKSPACE_TRACKER_EVENTS.update,
- payload: { slug: currentWorkspace.slug },
- });
setToast({
title: "Success!",
type: TOAST_TYPE.SUCCESS,
message: "Workspace updated successfully",
});
} catch (err: unknown) {
- captureError({
- eventName: WORKSPACE_TRACKER_EVENTS.update,
- payload: { slug: currentWorkspace.slug },
- error: err instanceof Error ? err : new Error(String(err)),
- });
console.error(err);
} finally {
setTimeout(() => {
@@ -305,7 +288,6 @@ export const WorkspaceDetails = observer(function WorkspaceDetails() {
{isAdmin && (
{
diff --git a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx
index 97f6b2b2c8..d869a12cb6 100644
--- a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx
+++ b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx
@@ -255,7 +255,7 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem
if (!project) return null;
const handleItemClick = () => {
- if (projectPreferences.navigationMode === "accordion") {
+ if (projectPreferences.navigationMode === "ACCORDION") {
setIsProjectListOpen(!isProjectListOpen);
} else {
router.push(defaultTabUrl);
@@ -266,9 +266,9 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem
}
};
- const isAccordionMode = projectPreferences.navigationMode === "accordion";
+ const isAccordionMode = projectPreferences.navigationMode === "ACCORDION";
- const shouldHighlightProject = URLProjectId === project?.id && projectPreferences.navigationMode !== "accordion";
+ const shouldHighlightProject = URLProjectId === project?.id && projectPreferences.navigationMode !== "ACCORDION";
return (
<>
diff --git a/apps/web/core/components/workspace/sidebar/user-menu-item.tsx b/apps/web/core/components/workspace/sidebar/user-menu-item.tsx
index 183332502c..1360511bb9 100644
--- a/apps/web/core/components/workspace/sidebar/user-menu-item.tsx
+++ b/apps/web/core/components/workspace/sidebar/user-menu-item.tsx
@@ -2,15 +2,13 @@ import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
// plane imports
-import { EUserPermissionsLevel, SIDEBAR_TRACKER_ELEMENTS } from "@plane/constants";
-
+import { EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import type { EUserWorkspaceRoles } from "@plane/types";
// components
import { SidebarNavItem } from "@/components/sidebar/sidebar-navigation";
import { NotificationAppSidebarOption } from "@/components/workspace-notifications/notification-app-sidebar-option";
// hooks
-import { captureClick } from "@/helpers/event-tracker.helper";
import { useAppTheme } from "@/hooks/store/use-app-theme";
import { useUserPermissions } from "@/hooks/store/user";
@@ -44,20 +42,14 @@ export const SidebarUserMenuItem = observer(function SidebarUserMenuItem(props:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) return null;
- const handleLinkClick = (itemKey: string) => {
+ const handleLinkClick = () => {
if (window.innerWidth < 768) {
toggleSidebar();
}
- captureClick({
- elementName: SIDEBAR_TRACKER_ELEMENTS.USER_MENU_ITEM,
- context: {
- destination: itemKey,
- },
- });
};
return (
- handleLinkClick(item.key)}>
+
diff --git a/apps/web/core/components/workspace/views/delete-view-modal.tsx b/apps/web/core/components/workspace/views/delete-view-modal.tsx
index 744807a0e6..6562fe5333 100644
--- a/apps/web/core/components/workspace/views/delete-view-modal.tsx
+++ b/apps/web/core/components/workspace/views/delete-view-modal.tsx
@@ -1,16 +1,12 @@
-import React, { useState } from "react";
+import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
-// types
-import { GLOBAL_VIEW_TRACKER_EVENTS } from "@plane/constants";
+// Plane Imports
import { useTranslation } from "@plane/i18n";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspaceView } from "@plane/types";
-// ui
import { AlertModalCore } from "@plane/ui";
-// constants
// hooks
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
import { useGlobalView } from "@/hooks/store/use-global-view";
type Props = {
@@ -32,37 +28,20 @@ export const DeleteGlobalViewModal = observer(function DeleteGlobalViewModal(pro
const handleDeletion = async () => {
if (!workspaceSlug) return;
-
setIsDeleteLoading(true);
- await deleteGlobalView(workspaceSlug.toString(), data.id)
- .then(() => {
- captureSuccess({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.delete,
- payload: {
- view_id: data.id,
- },
- });
- })
- .catch((error: any) => {
- captureError({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.delete,
- payload: {
- view_id: data.id,
- },
- error: error,
- });
- setToast({
- type: TOAST_TYPE.ERROR,
- title: "Error!",
- message: error?.error ?? "Something went wrong while deleting the view. Please try again.",
- });
- })
- .finally(() => {
- setIsDeleteLoading(false);
- handleClose();
+ try {
+ await deleteGlobalView(workspaceSlug.toString(), data.id);
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: "Error!",
+ message: "Failed to delete the view. Please try again.",
});
+ }
+ setIsDeleteLoading(false);
+ handleClose();
// remove filters from local storage
localStorage.removeItem(`global_view_filters/${data.id}`);
};
diff --git a/apps/web/core/components/workspace/views/header.tsx b/apps/web/core/components/workspace/views/header.tsx
index ef09a1e233..dd26d830c7 100644
--- a/apps/web/core/components/workspace/views/header.tsx
+++ b/apps/web/core/components/workspace/views/header.tsx
@@ -1,20 +1,12 @@
-import React, { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
-import {
- DEFAULT_GLOBAL_VIEWS_LIST,
- EUserPermissions,
- EUserPermissionsLevel,
- GLOBAL_VIEW_TRACKER_ELEMENTS,
- GLOBAL_VIEW_TRACKER_EVENTS,
-} from "@plane/constants";
+import { DEFAULT_GLOBAL_VIEWS_LIST, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { PlusIcon } from "@plane/propel/icons";
import type { TStaticViewTypes } from "@plane/types";
import { Header, EHeaderVariant } from "@plane/ui";
-// helpers
-import { captureSuccess } from "@/helpers/event-tracker.helper";
// hooks
import { useGlobalView } from "@/hooks/store/use-global-view";
import { useUserPermissions } from "@/hooks/store/user";
@@ -76,15 +68,6 @@ export const GlobalViewsHeader = observer(function GlobalViewsHeader() {
// bring the active view to the centre of the header
useEffect(() => {
if (globalViewId && currentWorkspaceViews) {
- captureSuccess({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.open,
- payload: {
- view_id: globalViewId,
- view_type: ["all-issues", "assigned", "created", "subscribed"].includes(globalViewId.toString())
- ? "Default"
- : "Custom",
- },
- });
const activeTabElement = document.querySelector(`#global-view-${globalViewId.toString()}`);
if (activeTabElement && containerRef.current) {
const containerRect = containerRef.current.getBoundingClientRect();
@@ -119,7 +102,6 @@ export const GlobalViewsHeader = observer(function GlobalViewsHeader() {
{isAuthorizedUser ? (
setCreateViewModal(true)}
>
diff --git a/apps/web/core/components/workspace/views/modal.tsx b/apps/web/core/components/workspace/views/modal.tsx
index 71483d7126..89a9102f9a 100644
--- a/apps/web/core/components/workspace/views/modal.tsx
+++ b/apps/web/core/components/workspace/views/modal.tsx
@@ -1,14 +1,10 @@
-import React from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
-import { GLOBAL_VIEW_TRACKER_EVENTS } from "@plane/constants";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspaceView } from "@plane/types";
import { EIssuesStoreType } from "@plane/types";
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
-// helpers
-import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
// hooks
import { useGlobalView } from "@/hooks/store/use-global-view";
import { useWorkItemFilters } from "@/hooks/store/work-item-filters/use-work-item-filters";
@@ -40,83 +36,57 @@ export const CreateUpdateWorkspaceViewModal = observer(function CreateUpdateWork
const handleCreateView = async (payload: Partial) => {
if (!workspaceSlug) return;
- const payloadData: Partial = {
- ...payload,
- rich_filters: {
- ...payload?.rich_filters,
- },
- };
-
- await createGlobalView(workspaceSlug, payloadData)
- .then((res) => {
- captureSuccess({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.create,
- payload: {
- id: res.id,
- },
- });
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Success!",
- message: "View created successfully.",
- });
-
- router.push(`/${workspaceSlug}/workspace-views/${res.id}`);
- handleClose();
- })
- .catch(() => {
- captureError({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.create,
- });
- setToast({
- type: TOAST_TYPE.ERROR,
- title: "Error!",
- message: "View could not be created. Please try again.",
- });
+ try {
+ const payloadData: Partial = {
+ ...payload,
+ rich_filters: {
+ ...payload?.rich_filters,
+ },
+ };
+ const res = await createGlobalView(workspaceSlug, payloadData);
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: "Success!",
+ message: "View created successfully.",
});
+ router.push(`/${workspaceSlug}/workspace-views/${res.id}`);
+ handleClose();
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: "Error!",
+ message: "View could not be created. Please try again.",
+ });
+ }
};
const handleUpdateView = async (payload: Partial) => {
if (!workspaceSlug || !data) return;
- const payloadData: Partial = {
- ...payload,
- query: {
- ...payload?.rich_filters,
- },
- };
-
- await updateGlobalView(workspaceSlug, data.id, payloadData)
- .then((res) => {
- if (res) {
- resetExpression(EIssuesStoreType.GLOBAL, data.id, res.rich_filters);
- captureSuccess({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.update,
- payload: {
- id: res.id,
- },
- });
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Success!",
- message: "View updated successfully.",
- });
- handleClose();
- }
- })
- .catch(() => {
- captureError({
- eventName: GLOBAL_VIEW_TRACKER_EVENTS.update,
- payload: {
- id: data.id,
- },
- });
+ try {
+ const payloadData: Partial = {
+ ...payload,
+ query: {
+ ...payload?.rich_filters,
+ },
+ };
+ const res = await updateGlobalView(workspaceSlug, data.id, payloadData);
+ if (res) {
+ resetExpression(EIssuesStoreType.GLOBAL, data.id, res.rich_filters);
setToast({
- type: TOAST_TYPE.ERROR,
- title: "Error!",
- message: "View could not be updated. Please try again.",
+ type: TOAST_TYPE.SUCCESS,
+ title: "Success!",
+ message: "View updated successfully.",
});
+ handleClose();
+ }
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: "Error!",
+ message: "View could not be updated. Please try again.",
});
+ }
};
const handleFormSubmit = async (formData: Partial) => {
diff --git a/apps/web/core/components/workspace/views/quick-action.tsx b/apps/web/core/components/workspace/views/quick-action.tsx
index 4739b2d076..0d70a3b8c4 100644
--- a/apps/web/core/components/workspace/views/quick-action.tsx
+++ b/apps/web/core/components/workspace/views/quick-action.tsx
@@ -1,14 +1,13 @@
import { useState } from "react";
import { observer } from "mobx-react";
// plane imports
-import { EUserPermissions, EUserPermissionsLevel, GLOBAL_VIEW_TRACKER_ELEMENTS } from "@plane/constants";
+import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspaceView } from "@plane/types";
import { CustomMenu } from "@plane/ui";
import { copyUrlToClipboard, cn } from "@plane/utils";
// helpers
import { useViewMenuItems } from "@/components/common/quick-actions-helper";
-import { captureClick } from "@/helpers/event-tracker.helper";
// hooks
import { useUser, useUserPermissions } from "@/hooks/store/user";
// local imports
@@ -33,14 +32,15 @@ export const WorkspaceViewQuickActions = observer(function WorkspaceViewQuickAct
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const viewLink = `${workspaceSlug}/workspace-views/${view.id}`;
- const handleCopyText = () =>
- copyUrlToClipboard(viewLink).then(() => {
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Link Copied!",
- message: "View link copied to clipboard.",
- });
+ const handleCopyText = async () => {
+ await copyUrlToClipboard(viewLink);
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: "Link Copied!",
+ message: "View link copied to clipboard.",
});
+ };
+
const handleOpenInNewTab = () => window.open(`/${viewLink}`, "_blank");
const MENU_ITEMS = useViewMenuItems({
@@ -70,9 +70,6 @@ export const WorkspaceViewQuickActions = observer(function WorkspaceViewQuickAct
{
- captureClick({
- elementName: GLOBAL_VIEW_TRACKER_ELEMENTS.QUICK_ACTIONS,
- });
item.action();
}}
className={cn(
diff --git a/apps/web/core/components/workspace/views/view-list-item.tsx b/apps/web/core/components/workspace/views/view-list-item.tsx
index 8f754fe592..0db808d0a0 100644
--- a/apps/web/core/components/workspace/views/view-list-item.tsx
+++ b/apps/web/core/components/workspace/views/view-list-item.tsx
@@ -2,14 +2,10 @@ import { useState } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams } from "next/navigation";
-
-import { GLOBAL_VIEW_TRACKER_ELEMENTS } from "@plane/constants";
-import { EditIcon, TrashIcon } from "@plane/propel/icons";
// plane imports
+import { EditIcon, TrashIcon } from "@plane/propel/icons";
import { CustomMenu } from "@plane/ui";
import { truncateText } from "@plane/utils";
-// helpers
-import { captureClick } from "@/helpers/event-tracker.helper";
// hooks
import { useGlobalView } from "@/hooks/store/use-global-view";
// local imports
@@ -51,9 +47,6 @@ export const GlobalViewListItem = observer(function GlobalViewListItem(props: Pr
{
- captureClick({
- elementName: GLOBAL_VIEW_TRACKER_ELEMENTS.LIST_ITEM,
- });
setUpdateViewModal(true);
}}
>
@@ -64,9 +57,6 @@ export const GlobalViewListItem = observer(function GlobalViewListItem(props: Pr
{
- captureClick({
- elementName: GLOBAL_VIEW_TRACKER_ELEMENTS.LIST_ITEM,
- });
setDeleteViewModal(true);
}}
>
diff --git a/apps/web/core/constants/fetch-keys.ts b/apps/web/core/constants/fetch-keys.ts
index 0a54ccc196..b7d669506e 100644
--- a/apps/web/core/constants/fetch-keys.ts
+++ b/apps/web/core/constants/fetch-keys.ts
@@ -83,6 +83,9 @@ export const WORKSPACE_STATES = (workspaceSlug: string) => `WORKSPACE_STATES_${w
export const WORKSPACE_SIDEBAR_PREFERENCES = (workspaceSlug: string) =>
`WORKSPACE_SIDEBAR_PREFERENCES_${workspaceSlug.toUpperCase()}`;
+export const WORKSPACE_PROJECT_NAVIGATION_PREFERENCES = (workspaceSlug: string) =>
+ `WORKSPACE_PROJECT_NAVIGATION_PREFERENCES_${workspaceSlug.toUpperCase()}`;
+
export const PROJECT_GITHUB_REPOSITORY = (projectId: string) => `PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`;
// cycles
diff --git a/apps/web/core/hooks/use-navigation-preferences.ts b/apps/web/core/hooks/use-navigation-preferences.ts
index 1672936ccd..155414cb8e 100644
--- a/apps/web/core/hooks/use-navigation-preferences.ts
+++ b/apps/web/core/hooks/use-navigation-preferences.ts
@@ -19,7 +19,6 @@ import {
import { useWorkspace } from "./store/use-workspace";
import useLocalStorage from "./use-local-storage";
-const PROJECT_PREFERENCES_KEY = "navigation_preferences_projects";
const APP_RAIL_PREFERENCES_KEY = "app_rail_preferences";
export const usePersonalNavigationPreferences = () => {
@@ -105,49 +104,73 @@ export const usePersonalNavigationPreferences = () => {
};
export const useProjectNavigationPreferences = () => {
- const { storedValue, setValue } = useLocalStorage(
- PROJECT_PREFERENCES_KEY,
- DEFAULT_PROJECT_PREFERENCES
- );
+ const { workspaceSlug } = useParams();
+ const { getProjectNavigationPreferences, updateProjectNavigationPreferences } = useWorkspace();
+ // Get preferences from the store
+ const storePreferences = getProjectNavigationPreferences(workspaceSlug?.toString() || "");
+
+ // Computed preferences with fallback logic: API → defaults
+ const preferences: TProjectNavigationPreferences = useMemo(() => {
+ // 1. Try API data first
+ if (
+ storePreferences &&
+ (storePreferences.navigation_control_preference || storePreferences.navigation_project_limit !== undefined)
+ ) {
+ const limit = storePreferences.navigation_project_limit ?? DEFAULT_PROJECT_PREFERENCES.limitedProjectsCount;
+
+ return {
+ navigationMode: storePreferences.navigation_control_preference || DEFAULT_PROJECT_PREFERENCES.navigationMode,
+ limitedProjectsCount: limit > 0 ? limit : DEFAULT_PROJECT_PREFERENCES.limitedProjectsCount,
+ showLimitedProjects: limit > 0, // Derived: 0 = false, >0 = true
+ };
+ }
+
+ // 2. Fall back to defaults
+ return DEFAULT_PROJECT_PREFERENCES;
+ }, [storePreferences]);
+
+ // Update navigation mode
const updateNavigationMode = useCallback(
- (mode: TProjectNavigationMode) => {
- const currentPreferences = storedValue || DEFAULT_PROJECT_PREFERENCES;
- setValue({
- navigationMode: mode,
- showLimitedProjects: currentPreferences.showLimitedProjects,
- limitedProjectsCount: currentPreferences.limitedProjectsCount,
+ async (mode: TProjectNavigationMode) => {
+ if (!workspaceSlug) return;
+
+ await updateProjectNavigationPreferences(workspaceSlug.toString(), {
+ navigation_control_preference: mode,
});
},
- [storedValue, setValue]
+ [workspaceSlug, updateProjectNavigationPreferences]
);
+ // Update show limited projects
const updateShowLimitedProjects = useCallback(
- (show: boolean) => {
- const currentPreferences = storedValue || DEFAULT_PROJECT_PREFERENCES;
- setValue({
- navigationMode: currentPreferences.navigationMode,
- showLimitedProjects: show,
- limitedProjectsCount: currentPreferences.limitedProjectsCount,
+ async (show: boolean) => {
+ if (!workspaceSlug) return;
+
+ // When toggling off, set to 0; when toggling on, use current count or default
+ const newLimit = show ? preferences.limitedProjectsCount || DEFAULT_PROJECT_PREFERENCES.limitedProjectsCount : 0;
+
+ await updateProjectNavigationPreferences(workspaceSlug.toString(), {
+ navigation_project_limit: newLimit,
});
},
- [storedValue, setValue]
+ [workspaceSlug, updateProjectNavigationPreferences, preferences.limitedProjectsCount]
);
+ // Update limited projects count
const updateLimitedProjectsCount = useCallback(
- (count: number) => {
- const currentPreferences = storedValue || DEFAULT_PROJECT_PREFERENCES;
- setValue({
- navigationMode: currentPreferences.navigationMode,
- showLimitedProjects: currentPreferences.showLimitedProjects,
- limitedProjectsCount: count,
+ async (count: number) => {
+ if (!workspaceSlug) return;
+
+ await updateProjectNavigationPreferences(workspaceSlug.toString(), {
+ navigation_project_limit: count,
});
},
- [storedValue, setValue]
+ [workspaceSlug, updateProjectNavigationPreferences]
);
return {
- preferences: storedValue || DEFAULT_PROJECT_PREFERENCES,
+ preferences,
updateNavigationMode,
updateShowLimitedProjects,
updateLimitedProjectsCount,
diff --git a/apps/web/core/hooks/use-page-operations.ts b/apps/web/core/hooks/use-page-operations.ts
index 95537ef5dd..da63c3e768 100644
--- a/apps/web/core/hooks/use-page-operations.ts
+++ b/apps/web/core/hooks/use-page-operations.ts
@@ -1,12 +1,9 @@
import { useMemo } from "react";
// plane imports
-import { IS_FAVORITE_MENU_OPEN, PROJECT_PAGE_TRACKER_EVENTS } from "@plane/constants";
-import type { EditorRefApi } from "@plane/editor";
+import { IS_FAVORITE_MENU_OPEN } from "@plane/constants";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { EPageAccess } from "@plane/types";
import { copyUrlToClipboard } from "@plane/utils";
-// helpers
-import { captureSuccess, captureError } from "@/helpers/event-tracker.helper";
// hooks
import { useCollaborativePageActions } from "@/hooks/use-collaborative-page-actions";
// store types
@@ -57,35 +54,23 @@ export const usePageOperations = (
const pageLink = getRedirectionLink();
return {
- copyLink: () => {
- copyUrlToClipboard(pageLink).then(() => {
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Link Copied!",
- message: "Page link copied to clipboard.",
- });
+ copyLink: async () => {
+ await copyUrlToClipboard(pageLink);
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: "Link Copied!",
+ message: "Page link copied to clipboard.",
});
},
duplicate: async () => {
try {
await duplicate();
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.duplicate,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Page duplicated successfully.",
});
- } catch (error: any) {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.duplicate,
- error,
- });
+ } catch (_error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -97,33 +82,16 @@ export const usePageOperations = (
openInNewTab: () => window.open(pageLink, "_blank"),
toggleAccess: async () => {
const changedPageType = access === EPageAccess.PUBLIC ? "private" : "public";
- const eventName = PROJECT_PAGE_TRACKER_EVENTS.access_update;
-
try {
if (access === EPageAccess.PUBLIC)
await executeCollaborativeAction({ type: "sendMessageToServer", message: "make-private" });
else await executeCollaborativeAction({ type: "sendMessageToServer", message: "make-public" });
-
- captureSuccess({
- eventName,
- payload: {
- id: page.id,
- from_access: access === EPageAccess.PUBLIC ? "Public" : "Private",
- to_access: access === EPageAccess.PUBLIC ? "Private" : "Public",
- state: "SUCCESS",
- },
- });
-
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: `The page has been marked ${changedPageType} and moved to the ${changedPageType} section.`,
});
- } catch (error: any) {
- captureError({
- eventName,
- error,
- });
+ } catch (_error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -135,23 +103,12 @@ export const usePageOperations = (
if (archived_at) {
try {
await executeCollaborativeAction({ type: "sendMessageToServer", message: "unarchive" });
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.restore,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Page restored successfully.",
});
- } catch (error: any) {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.restore,
- error,
- });
+ } catch (_error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -161,23 +118,12 @@ export const usePageOperations = (
} else {
try {
await executeCollaborativeAction({ type: "sendMessageToServer", message: "archive" });
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.archive,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Page archived successfully.",
});
- } catch (error: any) {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.archive,
- error,
- });
+ } catch (_error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -186,75 +132,50 @@ export const usePageOperations = (
}
}
},
- toggleFavorite: () => {
+ toggleFavorite: async () => {
if (is_favorite) {
- removePageFromFavorites()
- .then(() => {
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.unfavorite,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Success!",
- message: "Page removed from favorites.",
- });
- })
- .catch((error) => {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.unfavorite,
- error,
- });
+ try {
+ await removePageFromFavorites();
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: "Success!",
+ message: "Page removed from favorites.",
});
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: "Error!",
+ message: "Page could not be removed from favorites. Please try again later.",
+ });
+ }
} else {
- addToFavorites()
- .then(() => {
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.favorite,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
- if (!isFavoriteMenuOpen) toggleFavoriteMenu(true);
- setToast({
- type: TOAST_TYPE.SUCCESS,
- title: "Success!",
- message: "Page added to favorites.",
- });
- })
- .catch((error) => {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.favorite,
- error,
- });
+ try {
+ await addToFavorites();
+ if (!isFavoriteMenuOpen) toggleFavoriteMenu(true);
+ setToast({
+ type: TOAST_TYPE.SUCCESS,
+ title: "Success!",
+ message: "Page added to favorites.",
});
+ } catch (_error) {
+ setToast({
+ type: TOAST_TYPE.ERROR,
+ title: "Error!",
+ message: "Page could not be added to favorites. Please try again later.",
+ });
+ }
}
},
toggleLock: async () => {
if (is_locked) {
try {
await executeCollaborativeAction({ type: "sendMessageToServer", message: "unlock" });
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.unlock,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Page unlocked successfully.",
});
- } catch (error: any) {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.unlock,
- error,
- });
+ } catch (_error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -264,23 +185,12 @@ export const usePageOperations = (
} else {
try {
await executeCollaborativeAction({ type: "sendMessageToServer", message: "lock" });
- captureSuccess({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.lock,
- payload: {
- id: page.id,
- state: "SUCCESS",
- },
- });
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Page locked successfully.",
});
- } catch (error: any) {
- captureError({
- eventName: PROJECT_PAGE_TRACKER_EVENTS.lock,
- error,
- });
+ } catch (_error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -300,7 +210,6 @@ export const usePageOperations = (
is_favorite,
is_locked,
isFavoriteMenuOpen,
- page.id,
removePageFromFavorites,
toggleFavoriteMenu,
]);
diff --git a/apps/web/core/layouts/auth-layout/workspace-wrapper.tsx b/apps/web/core/layouts/auth-layout/workspace-wrapper.tsx
index b8c000e386..256afec7df 100644
--- a/apps/web/core/layouts/auth-layout/workspace-wrapper.tsx
+++ b/apps/web/core/layouts/auth-layout/workspace-wrapper.tsx
@@ -24,6 +24,7 @@ import {
WORKSPACE_FAVORITE,
WORKSPACE_STATES,
WORKSPACE_SIDEBAR_PREFERENCES,
+ WORKSPACE_PROJECT_NAVIGATION_PREFERENCES,
} from "@/constants/fetch-keys";
// hooks
import { useFavorite } from "@/hooks/store/use-favorite";
@@ -50,7 +51,7 @@ export const WorkspaceAuthWrapper = observer(function WorkspaceAuthWrapper(props
const {
workspace: { fetchWorkspaceMembers },
} = useMember();
- const { workspaces, fetchSidebarNavigationPreferences } = useWorkspace();
+ const { workspaces, fetchSidebarNavigationPreferences, fetchProjectNavigationPreferences } = useWorkspace();
const { isMobile } = usePlatformOS();
const { loader, workspaceInfoBySlug, fetchUserWorkspaceInfo, fetchUserProjectPermissions, allowPermissions } =
useUserPermissions();
@@ -113,6 +114,13 @@ export const WorkspaceAuthWrapper = observer(function WorkspaceAuthWrapper(props
{ revalidateIfStale: false, revalidateOnFocus: false }
);
+ // fetch workspace project navigation preferences
+ useSWR(
+ workspaceSlug ? WORKSPACE_PROJECT_NAVIGATION_PREFERENCES(workspaceSlug.toString()) : null,
+ workspaceSlug ? () => fetchProjectNavigationPreferences(workspaceSlug.toString()) : null,
+ { revalidateIfStale: false, revalidateOnFocus: false }
+ );
+
const handleSignOut = async () => {
await signOut().catch(() =>
setToast({
diff --git a/apps/web/core/lib/posthog-provider.tsx b/apps/web/core/lib/posthog-provider.tsx
deleted file mode 100644
index cb3d691a98..0000000000
--- a/apps/web/core/lib/posthog-provider.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import type { ReactNode } from "react";
-import { lazy, Suspense, useEffect, useCallback, useRef, useState } from "react";
-import { PostHogProvider as PHProvider } from "@posthog/react";
-import { observer } from "mobx-react";
-import { useParams } from "next/navigation";
-import posthog from "posthog-js";
-// constants
-import { GROUP_WORKSPACE_TRACKER_EVENT } from "@plane/constants";
-// helpers
-import { getUserRole } from "@plane/utils";
-// hooks
-import { captureClick, joinEventGroup } from "@/helpers/event-tracker.helper";
-import { useInstance } from "@/hooks/store/use-instance";
-import { useWorkspace } from "@/hooks/store/use-workspace";
-import { useUser, useUserPermissions } from "@/hooks/store/user";
-// dynamic imports
-const PostHogPageView = lazy(function PostHogPageView() {
- return import("@/lib/posthog-view");
-});
-
-export interface IPosthogWrapper {
- children: ReactNode;
-}
-
-const PostHogProvider = observer(function PostHogProvider(props: IPosthogWrapper) {
- const { children } = props;
- const { data: user } = useUser();
- const { currentWorkspace } = useWorkspace();
- const { instance } = useInstance();
- const { workspaceSlug, projectId } = useParams();
- const { getWorkspaceRoleByWorkspaceSlug, getProjectRoleByWorkspaceSlugAndProjectId } = useUserPermissions();
- // refs
- const isInitializedRef = useRef(false);
- // states
- const [hydrated, setHydrated] = useState(false);
- // derived values
- const currentProjectRole = getProjectRoleByWorkspaceSlugAndProjectId(
- workspaceSlug?.toString(),
- projectId?.toString()
- );
- const currentWorkspaceRole = getWorkspaceRoleByWorkspaceSlug(workspaceSlug?.toString());
- const is_telemetry_enabled = instance?.is_telemetry_enabled || false;
- const is_posthog_enabled = process.env.VITE_POSTHOG_KEY && process.env.VITE_POSTHOG_HOST && is_telemetry_enabled;
-
- useEffect(() => {
- if (user && hydrated) {
- // Identify sends an event, so you want may want to limit how often you call it
- posthog?.identify(user.email, {
- id: user.id,
- first_name: user.first_name,
- last_name: user.last_name,
- email: user.email,
- workspace_role: currentWorkspaceRole ? getUserRole(currentWorkspaceRole) : undefined,
- project_role: currentProjectRole ? getUserRole(currentProjectRole) : undefined,
- });
- if (currentWorkspace) {
- joinEventGroup(GROUP_WORKSPACE_TRACKER_EVENT, currentWorkspace?.id, {
- date: new Date().toDateString(),
- workspace_id: currentWorkspace?.id,
- });
- }
- }
- }, [user, currentProjectRole, currentWorkspaceRole, currentWorkspace, hydrated]);
-
- useEffect(() => {
- if (isInitializedRef.current) return; // prevent multiple initializations
- const posthogKey = process.env.VITE_POSTHOG_KEY;
- const posthogHost = process.env.VITE_POSTHOG_HOST;
- const isDebugMode = process.env.VITE_POSTHOG_DEBUG === "1";
- if (posthogKey && posthogHost && !posthog.__loaded) {
- posthog.init(posthogKey, {
- api_host: posthogHost,
- ui_host: posthogHost,
- debug: isDebugMode, // Debug mode based on the environment variable
- autocapture: false,
- capture_pageview: false, // Disable automatic pageview capture, as we capture manually
- capture_pageleave: true,
- disable_session_recording: true,
- });
- isInitializedRef.current = true;
- setHydrated(true);
- }
- }, []);
-
- const clickHandler = useCallback((event: MouseEvent) => {
- const target = event.target as HTMLElement;
- // Use closest to find the nearest parent element with data-ph-element attribute
- const elementWithAttribute = target.closest("[data-ph-element]") as HTMLElement;
- if (elementWithAttribute) {
- const element = elementWithAttribute.getAttribute("data-ph-element");
- if (element) {
- captureClick({ elementName: element });
- }
- }
- }, []);
-
- useEffect(() => {
- if (!is_posthog_enabled || !hydrated) return;
-
- document.addEventListener("click", clickHandler);
-
- return () => {
- document.removeEventListener("click", clickHandler);
- };
- }, [hydrated, is_posthog_enabled, clickHandler]);
-
- if (is_posthog_enabled && hydrated)
- return (
-
-
-
-
- {children}
-
- );
-
- return <>{children}>;
-});
-
-export default PostHogProvider;
diff --git a/apps/web/core/lib/posthog-view.tsx b/apps/web/core/lib/posthog-view.tsx
deleted file mode 100644
index 1c29ea5670..0000000000
--- a/apps/web/core/lib/posthog-view.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useEffect } from "react";
-import { usePathname, useSearchParams } from "next/navigation";
-// posthog
-import { usePostHog } from "posthog-js/react";
-
-export default function PostHogPageView(): null {
- const pathname = usePathname();
- const searchParams = useSearchParams();
- const posthog = usePostHog();
- useEffect(() => {
- // Track pageviews
- if (pathname && posthog) {
- let url = window.origin + pathname;
- if (searchParams.toString()) {
- url = url + `?${searchParams.toString()}`;
- }
- posthog.capture("$pageview", {
- $current_url: url,
- });
- }
- }, [pathname, searchParams, posthog]);
-
- return null;
-}
diff --git a/apps/web/core/services/workspace.service.ts b/apps/web/core/services/workspace.service.ts
index 0811a562ad..c544348266 100644
--- a/apps/web/core/services/workspace.service.ts
+++ b/apps/web/core/services/workspace.service.ts
@@ -19,6 +19,7 @@ import type {
TActivityEntityData,
IWorkspaceSidebarNavigationItem,
IWorkspaceSidebarNavigation,
+ IWorkspaceUserPropertiesResponse,
} from "@plane/types";
// services
import { APIService } from "@/services/api.service";
@@ -397,4 +398,23 @@ export class WorkspaceService extends APIService {
throw error?.response;
});
}
+
+ async fetchWorkspaceFilters(workspaceSlug: string): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/user-properties/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ async patchWorkspaceFilters(
+ workspaceSlug: string,
+ data: Partial
+ ): Promise {
+ return this.patch(`/api/workspaces/${workspaceSlug}/user-properties/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
}
diff --git a/apps/web/core/store/member/workspace/workspace-member.store.ts b/apps/web/core/store/member/workspace/workspace-member.store.ts
index edf9d2a297..ad0df64fcf 100644
--- a/apps/web/core/store/member/workspace/workspace-member.store.ts
+++ b/apps/web/core/store/member/workspace/workspace-member.store.ts
@@ -11,10 +11,10 @@ import { WorkspaceService } from "@/plane-web/services";
import type { IRouterStore } from "@/store/router.store";
import type { IUserStore } from "@/store/user";
// store
-import type { CoreRootStore } from "../../root.store";
import type { IMemberRootStore } from "../index.ts";
import type { IWorkspaceMemberFiltersStore } from "./workspace-member-filters.store";
import { WorkspaceMemberFiltersStore } from "./workspace-member-filters.store";
+import type { RootStore } from "@/plane-web/store/root.store";
export interface IWorkspaceMembership {
id: string;
@@ -72,7 +72,7 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
// services
workspaceService;
- constructor(_memberRoot: IMemberRootStore, _rootStore: CoreRootStore) {
+ constructor(_memberRoot: IMemberRootStore, _rootStore: RootStore) {
makeObservable(this, {
// observables
workspaceMemberMap: observable,
diff --git a/apps/web/core/store/workspace/index.ts b/apps/web/core/store/workspace/index.ts
index eea83642f2..797398a1fd 100644
--- a/apps/web/core/store/workspace/index.ts
+++ b/apps/web/core/store/workspace/index.ts
@@ -2,7 +2,12 @@ import { clone, set } from "lodash-es";
import { action, computed, observable, makeObservable, runInAction } from "mobx";
// types
import { computedFn } from "mobx-utils";
-import type { IWorkspaceSidebarNavigationItem, IWorkspace, IWorkspaceSidebarNavigation } from "@plane/types";
+import type {
+ IWorkspaceSidebarNavigationItem,
+ IWorkspace,
+ IWorkspaceSidebarNavigation,
+ IWorkspaceUserPropertiesResponse,
+} from "@plane/types";
// services
import { WorkspaceService } from "@/plane-web/services";
// store
@@ -23,6 +28,7 @@ export interface IWorkspaceRootStore {
currentWorkspace: IWorkspace | null;
workspacesCreatedByCurrentUser: IWorkspace[] | null;
navigationPreferencesMap: Record;
+ projectNavigationPreferencesMap: Record;
getWorkspaceRedirectionUrl: () => string;
// computed actions
getWorkspaceBySlug: (workspaceSlug: string) => IWorkspace | null;
@@ -45,6 +51,12 @@ export interface IWorkspaceRootStore {
data: Array<{ key: string; is_pinned: boolean; sort_order: number }>
) => Promise;
getNavigationPreferences: (workspaceSlug: string) => IWorkspaceSidebarNavigation | undefined;
+ getProjectNavigationPreferences: (workspaceSlug: string) => IWorkspaceUserPropertiesResponse | undefined;
+ fetchProjectNavigationPreferences: (workspaceSlug: string) => Promise;
+ updateProjectNavigationPreferences: (
+ workspaceSlug: string,
+ data: Partial
+ ) => Promise;
mutateWorkspaceMembersActivity: (workspaceSlug: string) => Promise;
// sub-stores
webhook: IWebhookStore;
@@ -57,6 +69,7 @@ export abstract class BaseWorkspaceRootStore implements IWorkspaceRootStore {
// observables
workspaces: Record = {};
navigationPreferencesMap: Record = {};
+ projectNavigationPreferencesMap: Record = {};
// services
workspaceService;
// root store
@@ -73,6 +86,7 @@ export abstract class BaseWorkspaceRootStore implements IWorkspaceRootStore {
// observables
workspaces: observable,
navigationPreferencesMap: observable,
+ projectNavigationPreferencesMap: observable,
// computed
currentWorkspace: computed,
workspacesCreatedByCurrentUser: computed,
@@ -88,6 +102,8 @@ export abstract class BaseWorkspaceRootStore implements IWorkspaceRootStore {
fetchSidebarNavigationPreferences: action,
updateSidebarPreference: action,
updateBulkSidebarPreferences: action,
+ fetchProjectNavigationPreferences: action,
+ updateProjectNavigationPreferences: action,
});
// services
@@ -315,6 +331,51 @@ export abstract class BaseWorkspaceRootStore implements IWorkspaceRootStore {
}
};
+ getProjectNavigationPreferences = computedFn(
+ (workspaceSlug: string): IWorkspaceUserPropertiesResponse | undefined =>
+ this.projectNavigationPreferencesMap[workspaceSlug]
+ );
+
+ fetchProjectNavigationPreferences = async (workspaceSlug: string) => {
+ try {
+ const response = await this.workspaceService.fetchWorkspaceFilters(workspaceSlug);
+
+ runInAction(() => {
+ this.projectNavigationPreferencesMap[workspaceSlug] = response;
+ });
+ } catch (error) {
+ console.error("Failed to fetch project navigation preferences:", error);
+ throw error;
+ }
+ };
+
+ updateProjectNavigationPreferences = async (
+ workspaceSlug: string,
+ data: Partial
+ ) => {
+ const beforeUpdateData = clone(this.projectNavigationPreferencesMap[workspaceSlug]);
+
+ try {
+ // Optimistically update store
+ runInAction(() => {
+ this.projectNavigationPreferencesMap[workspaceSlug] = {
+ ...this.projectNavigationPreferencesMap[workspaceSlug],
+ ...data,
+ };
+ });
+
+ // Call API to persist changes
+ await this.workspaceService.patchWorkspaceFilters(workspaceSlug, data);
+ } catch (error) {
+ // Rollback on failure
+ runInAction(() => {
+ this.projectNavigationPreferencesMap[workspaceSlug] = beforeUpdateData;
+ });
+ console.error("Failed to update project navigation preferences:", error);
+ throw error;
+ }
+ };
+
/**
* Mutate workspace members activity
* @param workspaceSlug
diff --git a/apps/web/core/types/navigation-preferences.ts b/apps/web/core/types/navigation-preferences.ts
index 357593a5aa..3f03802f52 100644
--- a/apps/web/core/types/navigation-preferences.ts
+++ b/apps/web/core/types/navigation-preferences.ts
@@ -11,7 +11,7 @@ export interface TPersonalNavigationItemState {
sort_order: number;
}
-export type TProjectNavigationMode = "accordion" | "horizontal";
+export type TProjectNavigationMode = "ACCORDION" | "TABBED";
export interface TProjectDisplaySettings {
navigationMode: TProjectNavigationMode;
@@ -54,7 +54,7 @@ export const DEFAULT_PERSONAL_PREFERENCES: TPersonalNavigationPreferences = {
};
export const DEFAULT_PROJECT_PREFERENCES: TProjectNavigationPreferences = {
- navigationMode: "accordion",
+ navigationMode: "ACCORDION",
showLimitedProjects: false,
limitedProjectsCount: 10,
};
diff --git a/apps/web/helpers/event-tracker.helper.ts b/apps/web/helpers/event-tracker.helper.ts
deleted file mode 100644
index 908db1e54b..0000000000
--- a/apps/web/helpers/event-tracker.helper.ts
+++ /dev/null
@@ -1,146 +0,0 @@
-import posthog from "posthog-js";
-
-export type TEventState = "SUCCESS" | "ERROR";
-export type TElementContext = Record;
-export type TEventContext = Record;
-export type TInteractionType = "clicked" | "viewed" | "hovered";
-
-/**
- * Join a event group in PostHog
- * @param groupName - The name of the group
- * @param groupId - The ID of the group
- * @param properties - The properties of the group
- */
-export const joinEventGroup = (groupName: string, groupId: string, properties: Record) => {
- posthog?.group(groupName, groupId, properties);
-};
-
-type TCaptureElementParams = {
- elementName: string;
- interaction_type: TInteractionType;
- context?: TElementContext;
-};
-
-/**
- * Capture UI element interactions (clicks, hovers, views, etc.)
- * This helps understand user behavior and interaction patterns
- *
- * @param element - Generic UI element type
- * @param context - Context about where and why the interaction happened
- */
-const captureElement = (params: TCaptureElementParams) => {
- const { elementName, interaction_type, context } = params;
- if (!posthog) return;
-
- const elementEvent = `${elementName}_${interaction_type}`;
-
- const payload = {
- element_type: elementName,
- timestamp: new Date().toISOString(),
- ...context,
- };
-
- posthog.capture(elementEvent, payload);
-};
-
-type TCaptureClickParams = Omit;
-/**
- * Capture click events
- * @param element - The element that was clicked
- * @param context - Additional context
- */
-export const captureClick = (params: TCaptureClickParams) => {
- captureElement({ ...params, interaction_type: "clicked" });
-};
-
-type TCaptureViewParams = Omit;
-/**
- * Capture view events
- * @param element - The element that was viewed
- * @param context - Additional context
- */
-export const captureView = (params: TCaptureViewParams) => {
- captureElement({ ...params, interaction_type: "viewed" });
-};
-
-type TCaptureHoverParams = Omit;
-/**
- * Capture hover events
- * @param element - The element that was hovered
- * @param context - Additional context
- */
-export const captureHover = (params: TCaptureHoverParams) => {
- captureElement({ ...params, interaction_type: "hovered" });
-};
-
-type TCaptureEventParams = {
- eventName: string;
- payload?: Record;
- context?: TEventContext;
- state: TEventState;
-};
-/**
- * Capture business events (outcomes, state changes, etc.)
- * This helps understand business metrics and conversion rates
- *
- * @param eventName - Business event name (e.g., "cycle_created", "project_updated")
- * @param state - Success or error state
- * @param payload - Event-specific data
- * @param context - Additional context
- */
-const captureEvent = (params: TCaptureEventParams) => {
- const { eventName, payload, context, state } = params;
- if (!posthog) return;
-
- const finalPayload = {
- ...context,
- ...payload,
- state,
- timestamp: new Date().toISOString(),
- };
-
- posthog.capture(eventName, finalPayload);
-};
-
-type TCaptureSuccessParams = Omit;
-/**
- * Capture success events
- * @param eventName - The name of the event
- * @param payload - Additional payload
- * @param context - Additional context
- */
-export const captureSuccess = (params: TCaptureSuccessParams) => {
- captureEvent({ ...params, state: "SUCCESS" });
-};
-
-type TCaptureErrorParams = Omit & {
- error?: Error | string;
-};
-/**
- * Capture error events
- * @param eventName - The name of the event
- * @param error - The error object
- * @param payload - Additional payload
- * @param context - Additional context
- */
-export const captureError = (params: TCaptureErrorParams) => {
- captureEvent({ ...params, state: "ERROR", payload: { ...params.payload, error: params.error } });
-};
-
-type TCaptureElementAndEventParams = {
- element: Omit;
- event: TCaptureEventParams;
-};
-/**
- * Capture both element interaction and business event together
- * @param element - The element that was interacted with
- * @param event - The business event that was triggered
- */
-export const captureElementAndEvent = (params: TCaptureElementAndEventParams) => {
- const { element, event } = params;
- // Capture the element interaction first
- captureElement({ ...element, interaction_type: "clicked" });
-
- // Then capture the business event
- captureEvent(event);
-};
diff --git a/apps/web/package.json b/apps/web/package.json
index f775fd5c06..b738271348 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -37,7 +37,6 @@
"@plane/ui": "workspace:*",
"@plane/utils": "workspace:*",
"@popperjs/core": "^2.11.8",
- "@posthog/react": "^1.4.0",
"@react-pdf/renderer": "^3.4.5",
"@react-router/node": "catalog:",
"@sentry/react-router": "catalog:",
@@ -56,7 +55,6 @@
"mobx-react": "catalog:",
"mobx-utils": "catalog:",
"next-themes": "0.4.6",
- "posthog-js": "^1.255.1",
"react": "catalog:",
"react-color": "^2.19.3",
"react-dom": "catalog:",
diff --git a/packages/editor/src/core/extensions/custom-image/components/uploader.tsx b/packages/editor/src/core/extensions/custom-image/components/uploader.tsx
index ccd67ad096..109073d0b9 100644
--- a/packages/editor/src/core/extensions/custom-image/components/uploader.tsx
+++ b/packages/editor/src/core/extensions/custom-image/components/uploader.tsx
@@ -168,8 +168,7 @@ export function CustomImageUploader(props: CustomImageUploaderProps) {
[uploadFile, editor, getPos]
);
- // const isErrorState = failedToLoadImage || hasDuplicationFailed;
- const isErrorState = true;
+ const isErrorState = failedToLoadImage || hasDuplicationFailed;
const borderColor =
selected && editor.isEditable && !isErrorState
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 9908d5dba6..9d3103c83b 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -50,3 +50,4 @@ export * from "./workspace-draft-issues/base";
export * from "./workspace-notifications";
export * from "./workspace-views";
export * from "./base-layouts";
+export * from "./pagination";
diff --git a/packages/types/src/pagination.ts b/packages/types/src/pagination.ts
new file mode 100644
index 0000000000..94ed237e75
--- /dev/null
+++ b/packages/types/src/pagination.ts
@@ -0,0 +1,15 @@
+// Generic paginated response type for API responses
+export type TPaginatedResponse = {
+ results: T;
+ grouped_by?: string | null;
+ sub_grouped_by?: string | null;
+ total_count?: number;
+ next_cursor?: string;
+ prev_cursor?: string;
+ next_page_results?: boolean;
+ prev_page_results?: boolean;
+ count?: number;
+ total_pages?: number;
+ total_results?: number;
+ extra_stats?: string | null;
+};
diff --git a/packages/types/src/view-props.ts b/packages/types/src/view-props.ts
index 7211b58a4a..aa90541a95 100644
--- a/packages/types/src/view-props.ts
+++ b/packages/types/src/view-props.ts
@@ -194,6 +194,12 @@ export interface IIssueFiltersResponse {
display_properties: IIssueDisplayProperties;
}
+export interface IWorkspaceUserPropertiesResponse extends IIssueFiltersResponse {
+ navigation_project_limit?: number;
+ navigation_control_preference?: "ACCORDION" | "TABBED";
+ // Note: show_limited_projects is derived from navigation_project_limit (0 = false, >0 = true)
+}
+
export interface IWorkspaceIssueFilterOptions {
assignees?: string[] | null;
created_by?: string[] | null;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b330a1e0be..2a4b33c52a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -639,9 +639,6 @@ importers:
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
- '@posthog/react':
- specifier: ^1.4.0
- version: 1.4.0(@types/react@18.3.11)(posthog-js@1.255.1)(react@18.3.1)
'@react-pdf/renderer':
specifier: ^3.4.5
version: 3.4.5(react@18.3.1)
@@ -696,9 +693,6 @@ importers:
next-themes:
specifier: 0.4.6
version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- posthog-js:
- specifier: ^1.255.1
- version: 1.255.1
react:
specifier: 'catalog:'
version: 18.3.1
@@ -2603,16 +2597,6 @@ packages:
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
- '@posthog/react@1.4.0':
- resolution: {integrity: sha512-xzPeZ753fQ0deZzdgY/0YavZvNpmdaxUzLYJYu5XjONNcZ8PwJnNLEK+7D/Cj8UM4Q8nWI7QC5mjum0uLWa4FA==}
- peerDependencies:
- '@types/react': '>=16.8.0'
- posthog-js: '>=1.257.2'
- react: '>=16.8.0'
- peerDependenciesMeta:
- '@types/react':
- optional: true
-
'@prettier/plugin-oxc@0.1.3':
resolution: {integrity: sha512-aABz3zIRilpWMekbt1FL1JVBQrQLR8L4Td2SRctECrWSsXGTNn/G1BqNSKCdbvQS1LWstAXfqcXzDki7GAAJyg==}
engines: {node: '>=14'}
@@ -5116,9 +5100,6 @@ packages:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
- core-js@3.45.1:
- resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==}
-
cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@@ -5874,9 +5855,6 @@ packages:
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
- fflate@0.4.8:
- resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
-
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
@@ -7756,20 +7734,6 @@ packages:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
- posthog-js@1.255.1:
- resolution: {integrity: sha512-KMh0o9MhORhEZVjXpktXB5rJ8PfDk+poqBoTSoLzWgNjhJf6D8jcyB9jUMA6vVPfn4YeepVX5NuclDRqOwr5Mw==}
- peerDependencies:
- '@rrweb/types': 2.0.0-alpha.17
- rrweb-snapshot: 2.0.0-alpha.17
- peerDependenciesMeta:
- '@rrweb/types':
- optional: true
- rrweb-snapshot:
- optional: true
-
- preact@10.27.1:
- resolution: {integrity: sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==}
-
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -9231,9 +9195,6 @@ packages:
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
- web-vitals@4.2.4:
- resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
-
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -10715,13 +10676,6 @@ snapshots:
'@popperjs/core@2.11.8': {}
- '@posthog/react@1.4.0(@types/react@18.3.11)(posthog-js@1.255.1)(react@18.3.1)':
- dependencies:
- posthog-js: 1.255.1
- react: 18.3.1
- optionalDependencies:
- '@types/react': 18.3.11
-
'@prettier/plugin-oxc@0.1.3':
dependencies:
oxc-parser: 0.99.0
@@ -13546,8 +13500,6 @@ snapshots:
cookie@1.0.2: {}
- core-js@3.45.1: {}
-
cors@2.8.5:
dependencies:
object-assign: 4.1.1
@@ -14438,8 +14390,6 @@ snapshots:
fecha@4.2.3: {}
- fflate@0.4.8: {}
-
fflate@0.8.2: {}
file-entry-cache@8.0.0:
@@ -16633,15 +16583,6 @@ snapshots:
dependencies:
xtend: 4.0.2
- posthog-js@1.255.1:
- dependencies:
- core-js: 3.45.1
- fflate: 0.4.8
- preact: 10.27.1
- web-vitals: 4.2.4
-
- preact@10.27.1: {}
-
prelude-ls@1.2.1: {}
prettier@3.7.4: {}
@@ -18416,8 +18357,6 @@ snapshots:
web-namespaces@2.0.1: {}
- web-vitals@4.2.4: {}
-
webidl-conversions@3.0.1: {}
webidl-conversions@7.0.0:
diff --git a/turbo.json b/turbo.json
index 443c8fc703..5d1c227553 100644
--- a/turbo.json
+++ b/turbo.json
@@ -17,9 +17,6 @@
"VITE_ENABLE_SESSION_RECORDER",
"VITE_LIVE_BASE_PATH",
"VITE_LIVE_BASE_URL",
- "VITE_POSTHOG_DEBUG",
- "VITE_POSTHOG_HOST",
- "VITE_POSTHOG_KEY",
"VITE_SENTRY_DSN",
"VITE_SENTRY_ENVIRONMENT",
"VITE_SENTRY_PROFILES_SAMPLE_RATE",