diff --git a/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx b/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx index 5eeafbc51d..c0a9264fe5 100644 --- a/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx +++ b/frontend/__tests__/components/context-menu/account-settings-context-menu.test.tsx @@ -1,9 +1,27 @@ import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { afterEach, describe, expect, it, test, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, test, vi } from "vitest"; import { AccountSettingsContextMenu } from "#/components/features/context-menu/account-settings-context-menu"; import { MemoryRouter } from "react-router"; import { renderWithProviders } from "../../../test-utils"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createMockWebClientConfig } from "../../helpers/mock-config"; + +const mockTrackAddTeamMembersButtonClick = vi.fn(); + +vi.mock("#/hooks/use-tracking", () => ({ + useTracking: () => ({ + trackAddTeamMembersButtonClick: mockTrackAddTeamMembersButtonClick, + }), +})); + +// Mock posthog feature flag +vi.mock("posthog-js/react", () => ({ + useFeatureFlagEnabled: vi.fn(), +})); + +// Import the mocked module to get access to the mock +import * as posthog from "posthog-js/react"; describe("AccountSettingsContextMenu", () => { const user = userEvent.setup(); @@ -11,15 +29,45 @@ describe("AccountSettingsContextMenu", () => { const onLogoutMock = vi.fn(); const onCloseMock = vi.fn(); + let queryClient: QueryClient; + + beforeEach(() => { + queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + // Set default feature flag to false + vi.mocked(posthog.useFeatureFlagEnabled).mockReturnValue(false); + }); + // Create a wrapper with MemoryRouter and renderWithProviders const renderWithRouter = (ui: React.ReactElement) => { return renderWithProviders({ui}); }; + const renderWithSaasConfig = (ui: React.ReactElement) => { + queryClient.setQueryData(["web-client-config"], createMockWebClientConfig({ app_mode: "saas" })); + return render( + + {ui} + + ); + }; + + const renderWithOssConfig = (ui: React.ReactElement) => { + queryClient.setQueryData(["web-client-config"], createMockWebClientConfig({ app_mode: "oss" })); + return render( + + {ui} + + ); + }; + afterEach(() => { onClickAccountSettingsMock.mockClear(); onLogoutMock.mockClear(); onCloseMock.mockClear(); + mockTrackAddTeamMembersButtonClick.mockClear(); + vi.mocked(posthog.useFeatureFlagEnabled).mockClear(); }); it("should always render the right options", () => { @@ -93,4 +141,59 @@ describe("AccountSettingsContextMenu", () => { expect(onCloseMock).toHaveBeenCalledOnce(); }); + + it("should show Add Team Members button in SaaS mode when feature flag is enabled", () => { + vi.mocked(posthog.useFeatureFlagEnabled).mockReturnValue(true); + renderWithSaasConfig( + , + ); + + expect(screen.getByTestId("add-team-members-button")).toBeInTheDocument(); + expect(screen.getByText("SETTINGS$NAV_ADD_TEAM_MEMBERS")).toBeInTheDocument(); + }); + + it("should not show Add Team Members button in SaaS mode when feature flag is disabled", () => { + vi.mocked(posthog.useFeatureFlagEnabled).mockReturnValue(false); + renderWithSaasConfig( + , + ); + + expect(screen.queryByTestId("add-team-members-button")).not.toBeInTheDocument(); + expect(screen.queryByText("SETTINGS$NAV_ADD_TEAM_MEMBERS")).not.toBeInTheDocument(); + }); + + it("should not show Add Team Members button in OSS mode even when feature flag is enabled", () => { + vi.mocked(posthog.useFeatureFlagEnabled).mockReturnValue(true); + renderWithOssConfig( + , + ); + + expect(screen.queryByTestId("add-team-members-button")).not.toBeInTheDocument(); + expect(screen.queryByText("SETTINGS$NAV_ADD_TEAM_MEMBERS")).not.toBeInTheDocument(); + }); + + it("should call tracking function and onClose when Add Team Members button is clicked", async () => { + vi.mocked(posthog.useFeatureFlagEnabled).mockReturnValue(true); + renderWithSaasConfig( + , + ); + + const addTeamMembersButton = screen.getByTestId("add-team-members-button"); + await user.click(addTeamMembersButton); + + expect(mockTrackAddTeamMembersButtonClick).toHaveBeenCalledOnce(); + expect(onCloseMock).toHaveBeenCalledOnce(); + }); }); diff --git a/frontend/src/components/features/context-menu/account-settings-context-menu.tsx b/frontend/src/components/features/context-menu/account-settings-context-menu.tsx index 0c2541237d..6dddd52620 100644 --- a/frontend/src/components/features/context-menu/account-settings-context-menu.tsx +++ b/frontend/src/components/features/context-menu/account-settings-context-menu.tsx @@ -1,6 +1,7 @@ import React from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; +import { useFeatureFlagEnabled } from "posthog-js/react"; import { ContextMenu } from "#/ui/context-menu"; import { ContextMenuListItem } from "./context-menu-list-item"; import { Divider } from "#/ui/divider"; @@ -8,7 +9,10 @@ import { useClickOutsideElement } from "#/hooks/use-click-outside-element"; import { I18nKey } from "#/i18n/declaration"; import LogOutIcon from "#/icons/log-out.svg?react"; import DocumentIcon from "#/icons/document.svg?react"; +import PlusIcon from "#/icons/plus.svg?react"; import { useSettingsNavItems } from "#/hooks/use-settings-nav-items"; +import { useConfig } from "#/hooks/query/use-config"; +import { useTracking } from "#/hooks/use-tracking"; interface AccountSettingsContextMenuProps { onLogout: () => void; @@ -21,9 +25,17 @@ export function AccountSettingsContextMenu({ }: AccountSettingsContextMenuProps) { const ref = useClickOutsideElement(onClose); const { t } = useTranslation(); + const { trackAddTeamMembersButtonClick } = useTracking(); + const { data: config } = useConfig(); + const isAddTeamMemberEnabled = useFeatureFlagEnabled( + "exp_add_team_member_button", + ); // Get navigation items and filter out LLM settings if the feature flag is enabled const items = useSettingsNavItems(); + const isSaasMode = config?.app_mode === "saas"; + const showAddTeamMembers = isSaasMode && isAddTeamMemberEnabled; + const navItems = items.map((item) => ({ ...item, icon: React.cloneElement(item.icon, { @@ -33,6 +45,11 @@ export function AccountSettingsContextMenu({ })); const handleNavigationClick = () => onClose(); + const handleAddTeamMembers = () => { + trackAddTeamMembersButtonClick(); + onClose(); + }; + return ( + {showAddTeamMembers && ( + + + + {t(I18nKey.SETTINGS$NAV_ADD_TEAM_MEMBERS)} + + + )} {navItems.map(({ to, text, icon }) => ( { }); }; + const trackAddTeamMembersButtonClick = () => { + posthog.capture("exp_add_team_members", { + ...commonProperties, + }); + }; + return { trackLoginButtonClick, trackConversationCreated, @@ -109,5 +115,6 @@ export const useTracking = () => { trackUserSignupCompleted, trackCreditsPurchased, trackCreditLimitReached, + trackAddTeamMembersButtonClick, }; }; diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts index 2d9599e810..71f4c963b4 100644 --- a/frontend/src/i18n/declaration.ts +++ b/frontend/src/i18n/declaration.ts @@ -693,6 +693,7 @@ export enum I18nKey { TIPS$PROTIP = "TIPS$PROTIP", FEEDBACK$SUBMITTING_LABEL = "FEEDBACK$SUBMITTING_LABEL", FEEDBACK$SUBMITTING_MESSAGE = "FEEDBACK$SUBMITTING_MESSAGE", + SETTINGS$NAV_ADD_TEAM_MEMBERS = "SETTINGS$NAV_ADD_TEAM_MEMBERS", SETTINGS$NAV_USER = "SETTINGS$NAV_USER", SETTINGS$USER_TITLE = "SETTINGS$USER_TITLE", SETTINGS$USER_EMAIL = "SETTINGS$USER_EMAIL", diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index 91530fe633..8db2e15e4f 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -11087,6 +11087,22 @@ "de": "Feedback senden, bitte warten...", "uk": "Відправляємо відгук, будь ласка, почекайте..." }, + "SETTINGS$NAV_ADD_TEAM_MEMBERS": { + "en": "Add Team Members", + "ja": "チームメンバーを追加", + "zh-CN": "添加团队成员", + "zh-TW": "新增團隊成員", + "ko-KR": "팀원 추가", + "no": "Legg til teammedlemmer", + "it": "Aggiungi membri del team", + "pt": "Adicionar membros da equipe", + "es": "Agregar miembros del equipo", + "ar": "إضافة أعضاء الفريق", + "fr": "Ajouter des membres de l'équipe", + "tr": "Takım üyeleri ekle", + "de": "Teammitglieder hinzufügen", + "uk": "Додати учасників команди" + }, "SETTINGS$NAV_USER": { "en": "User", "ja": "ユーザー",