diff --git a/frontend/__tests__/components/features/org/invite-organization-member-modal.test.tsx b/frontend/__tests__/components/features/org/invite-organization-member-modal.test.tsx index 971a143316..24b1559402 100644 --- a/frontend/__tests__/components/features/org/invite-organization-member-modal.test.tsx +++ b/frontend/__tests__/components/features/org/invite-organization-member-modal.test.tsx @@ -1,9 +1,14 @@ import { within, screen, render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { QueryClientProvider, QueryClient } from "@tanstack/react-query"; import { organizationService } from "#/api/organization-service/organization-service.api"; import { InviteOrganizationMemberModal } from "#/components/features/org/invite-organization-member-modal"; +import { useSelectedOrganizationStore } from "#/stores/selected-organization-store"; + +vi.mock("react-router", () => ({ + useRevalidator: vi.fn(() => ({ revalidate: vi.fn() })), +})); const renderInviteOrganizationMemberModal = (config?: { onClose: () => void; @@ -19,16 +24,14 @@ const renderInviteOrganizationMemberModal = (config?: { }, ); -vi.mock("#/context/use-selected-organization", () => ({ - useSelectedOrganizationId: vi.fn(() => ({ - orgId: "1", - setOrgId: vi.fn(), - })), -})); - describe("InviteOrganizationMemberModal", () => { + beforeEach(() => { + useSelectedOrganizationStore.setState({ organizationId: "1" }); + }); + afterEach(() => { vi.clearAllMocks(); + useSelectedOrganizationStore.setState({ organizationId: null }); }); it("should call onClose the modal when the close button is clicked", async () => { diff --git a/frontend/__tests__/stores/selected-organization-store.test.ts b/frontend/__tests__/stores/selected-organization-store.test.ts index 3f0155e7b1..d3abd6d975 100644 --- a/frontend/__tests__/stores/selected-organization-store.test.ts +++ b/frontend/__tests__/stores/selected-organization-store.test.ts @@ -3,35 +3,35 @@ import { describe, expect, it } from "vitest"; import { useSelectedOrganizationStore } from "#/stores/selected-organization-store"; describe("useSelectedOrganizationStore", () => { - it("should have null as initial orgId", () => { + it("should have null as initial organizationId", () => { const { result } = renderHook(() => useSelectedOrganizationStore()); - expect(result.current.orgId).toBeNull(); + expect(result.current.organizationId).toBeNull(); }); - it("should update orgId when setOrgId is called", () => { + it("should update organizationId when setOrganizationId is called", () => { const { result } = renderHook(() => useSelectedOrganizationStore()); act(() => { - result.current.setOrgId("org-123"); + result.current.setOrganizationId("org-123"); }); - expect(result.current.orgId).toBe("org-123"); + expect(result.current.organizationId).toBe("org-123"); }); - it("should allow setting orgId to null", () => { + it("should allow setting organizationId to null", () => { const { result } = renderHook(() => useSelectedOrganizationStore()); act(() => { - result.current.setOrgId("org-123"); + result.current.setOrganizationId("org-123"); }); - expect(result.current.orgId).toBe("org-123"); + expect(result.current.organizationId).toBe("org-123"); act(() => { - result.current.setOrgId(null); + result.current.setOrganizationId(null); }); - expect(result.current.orgId).toBeNull(); + expect(result.current.organizationId).toBeNull(); }); it("should share state across multiple hook instances", () => { @@ -43,9 +43,9 @@ describe("useSelectedOrganizationStore", () => { ); act(() => { - result1.current.setOrgId("shared-org"); + result1.current.setOrganizationId("shared-organization"); }); - expect(result2.current.orgId).toBe("shared-org"); + expect(result2.current.organizationId).toBe("shared-organization"); }); }); diff --git a/frontend/src/components/features/settings/settings-navigation.tsx b/frontend/src/components/features/settings/settings-navigation.tsx index 3d7382c776..1c6bf20293 100644 --- a/frontend/src/components/features/settings/settings-navigation.tsx +++ b/frontend/src/components/features/settings/settings-navigation.tsx @@ -22,7 +22,7 @@ export function SettingsNavigation({ onCloseMobileMenu, navigationItems, }: SettingsNavigationProps) { - const { orgId, setOrgId } = useSelectedOrganizationId(); + const { organizationId, setOrganizationId } = useSelectedOrganizationId(); const { data: organizations } = useOrganizations(); const { data: me } = useMe(); @@ -56,7 +56,7 @@ export function SettingsNavigation({ testId="org-select" name="organization" placeholder="Please select an organization" - selectedKey={orgId || ""} + selectedKey={organizationId || ""} items={ organizations?.map((org) => ({ key: org.id, @@ -65,9 +65,9 @@ export function SettingsNavigation({ } onSelectionChange={(org) => { if (org) { - setOrgId(org.toString()); + setOrganizationId(org.toString()); } else { - setOrgId(null); + setOrganizationId(null); } }} /> @@ -96,7 +96,7 @@ export function SettingsNavigation({ if ( (navItem.to === "/settings/org-members" || navItem.to === "/settings/org") && - (isUser || !orgId) + (isUser || !organizationId) ) { return false; } diff --git a/frontend/src/components/features/user/user-context-menu.tsx b/frontend/src/components/features/user/user-context-menu.tsx index 8b287dc68e..b1fe8dc29c 100644 --- a/frontend/src/components/features/user/user-context-menu.tsx +++ b/frontend/src/components/features/user/user-context-menu.tsx @@ -36,7 +36,7 @@ interface UserContextMenuProps { export function UserContextMenu({ type, onClose }: UserContextMenuProps) { const { t } = useTranslation(); const navigate = useNavigate(); - const { orgId, setOrgId } = useSelectedOrganizationId(); + const { organizationId, setOrganizationId } = useSelectedOrganizationId(); const { data: organizations } = useOrganizations(); const { mutate: logout } = useLogout(); const ref = useClickOutsideElement(onClose); @@ -100,7 +100,7 @@ export function UserContextMenu({ type, onClose }: UserContextMenuProps) { testId="org-selector" name="organization" placeholder="Please select an organization" - selectedKey={orgId || "personal"} + selectedKey={organizationId || "personal"} items={[ { key: "personal", label: "Personal Account" }, ...(organizations?.map((org) => ({ @@ -110,11 +110,11 @@ export function UserContextMenu({ type, onClose }: UserContextMenuProps) { ]} onSelectionChange={(org) => { if (org === "personal") { - setOrgId(null); + setOrganizationId(null); } else if (org) { - setOrgId(org.toString()); + setOrganizationId(org.toString()); } else { - setOrgId(null); + setOrganizationId(null); } }} /> diff --git a/frontend/src/context/use-selected-organization.ts b/frontend/src/context/use-selected-organization.ts index 0d1e56f05c..10c7c4f16c 100644 --- a/frontend/src/context/use-selected-organization.ts +++ b/frontend/src/context/use-selected-organization.ts @@ -3,14 +3,15 @@ import { useSelectedOrganizationStore } from "#/stores/selected-organization-sto export const useSelectedOrganizationId = () => { const revalidator = useRevalidator(); - const { orgId, setOrgId: setOrgIdStore } = useSelectedOrganizationStore(); + const { organizationId, setOrganizationId: setOrganizationIdStore } = + useSelectedOrganizationStore(); - const setOrgId = (newOrgId: string | null) => { - setOrgIdStore(newOrgId); + const setOrganizationId = (newOrganizationId: string | null) => { + setOrganizationIdStore(newOrganizationId); // Revalidate route to ensure the latest orgId is used. // This is useful for redirecting the user away from admin-only org pages. revalidator.revalidate(); }; - return { orgId, setOrgId }; + return { organizationId, setOrganizationId }; }; diff --git a/frontend/src/hooks/mutation/use-delete-organization.ts b/frontend/src/hooks/mutation/use-delete-organization.ts index 32edacedfd..94a69c5fcf 100644 --- a/frontend/src/hooks/mutation/use-delete-organization.ts +++ b/frontend/src/hooks/mutation/use-delete-organization.ts @@ -6,16 +6,16 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useDeleteOrganization = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); - const { orgId, setOrgId } = useSelectedOrganizationId(); + const { organizationId, setOrganizationId } = useSelectedOrganizationId(); return useMutation({ mutationFn: () => { - if (!orgId) throw new Error("Organization ID is required"); - return organizationService.deleteOrganization({ orgId }); + if (!organizationId) throw new Error("Organization ID is required"); + return organizationService.deleteOrganization({ orgId: organizationId }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["organizations"] }); - setOrgId(null); + setOrganizationId(null); navigate("/"); }, }); diff --git a/frontend/src/hooks/mutation/use-invite-members-batch.ts b/frontend/src/hooks/mutation/use-invite-members-batch.ts index be29266508..005b31292e 100644 --- a/frontend/src/hooks/mutation/use-invite-members-batch.ts +++ b/frontend/src/hooks/mutation/use-invite-members-batch.ts @@ -4,14 +4,14 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useInviteMembersBatch = () => { const queryClient = useQueryClient(); - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useMutation({ mutationFn: ({ emails }: { emails: string[] }) => - organizationService.inviteMembers({ orgId: orgId!, emails }), + organizationService.inviteMembers({ orgId: organizationId!, emails }), onSuccess: () => { queryClient.invalidateQueries({ - queryKey: ["organizations", "members", orgId], + queryKey: ["organizations", "members", organizationId], }); }, }); diff --git a/frontend/src/hooks/mutation/use-remove-member.ts b/frontend/src/hooks/mutation/use-remove-member.ts index 96d38928fa..22d75f83ae 100644 --- a/frontend/src/hooks/mutation/use-remove-member.ts +++ b/frontend/src/hooks/mutation/use-remove-member.ts @@ -4,14 +4,14 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useRemoveMember = () => { const queryClient = useQueryClient(); - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useMutation({ mutationFn: ({ userId }: { userId: string }) => - organizationService.removeMember({ orgId: orgId!, userId }), + organizationService.removeMember({ orgId: organizationId!, userId }), onSuccess: () => { queryClient.invalidateQueries({ - queryKey: ["organizations", "members", orgId], + queryKey: ["organizations", "members", organizationId], }); }, }); diff --git a/frontend/src/hooks/mutation/use-update-member-role.ts b/frontend/src/hooks/mutation/use-update-member-role.ts index d239c2ba17..4b9a5263fa 100644 --- a/frontend/src/hooks/mutation/use-update-member-role.ts +++ b/frontend/src/hooks/mutation/use-update-member-role.ts @@ -5,7 +5,7 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useUpdateMemberRole = () => { const queryClient = useQueryClient(); - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useMutation({ mutationFn: async ({ @@ -15,11 +15,11 @@ export const useUpdateMemberRole = () => { userId: string; role: OrganizationUserRole; }) => { - if (!orgId) { + if (!organizationId) { throw new Error("Organization ID is required to update member role"); } return organizationService.updateMember({ - orgId, + orgId: organizationId, userId, role, }); diff --git a/frontend/src/hooks/mutation/use-update-organization.ts b/frontend/src/hooks/mutation/use-update-organization.ts index b5f0b0c6b7..43156a0e65 100644 --- a/frontend/src/hooks/mutation/use-update-organization.ts +++ b/frontend/src/hooks/mutation/use-update-organization.ts @@ -4,15 +4,20 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useUpdateOrganization = () => { const queryClient = useQueryClient(); - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useMutation({ mutationFn: (name: string) => { - if (!orgId) throw new Error("Organization ID is required"); - return organizationService.updateOrganization({ orgId, name }); + if (!organizationId) throw new Error("Organization ID is required"); + return organizationService.updateOrganization({ + orgId: organizationId, + name, + }); }, onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["organizations", orgId] }); + queryClient.invalidateQueries({ + queryKey: ["organizations", organizationId], + }); }, }); }; diff --git a/frontend/src/hooks/query/use-me.ts b/frontend/src/hooks/query/use-me.ts index 0a8e60ec74..41afae2db7 100644 --- a/frontend/src/hooks/query/use-me.ts +++ b/frontend/src/hooks/query/use-me.ts @@ -5,13 +5,13 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useMe = () => { const { data: config } = useConfig(); - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); const isSaas = config?.APP_MODE === "saas"; return useQuery({ - queryKey: ["organizations", orgId, "me"], - queryFn: () => organizationService.getMe({ orgId: orgId! }), - enabled: isSaas && !!orgId, + queryKey: ["organizations", organizationId, "me"], + queryFn: () => organizationService.getMe({ orgId: organizationId! }), + enabled: isSaas && !!organizationId, }); }; diff --git a/frontend/src/hooks/query/use-organization-members.ts b/frontend/src/hooks/query/use-organization-members.ts index b77e13d2bf..a038e48d06 100644 --- a/frontend/src/hooks/query/use-organization-members.ts +++ b/frontend/src/hooks/query/use-organization-members.ts @@ -3,12 +3,12 @@ import { organizationService } from "#/api/organization-service/organization-ser import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useOrganizationMembers = () => { - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useQuery({ - queryKey: ["organizations", "members", orgId], + queryKey: ["organizations", "members", organizationId], queryFn: () => - organizationService.getOrganizationMembers({ orgId: orgId! }), - enabled: !!orgId, + organizationService.getOrganizationMembers({ orgId: organizationId! }), + enabled: !!organizationId, }); }; diff --git a/frontend/src/hooks/query/use-organization-payment-info.tsx b/frontend/src/hooks/query/use-organization-payment-info.tsx index 7f09cda934..736673704d 100644 --- a/frontend/src/hooks/query/use-organization-payment-info.tsx +++ b/frontend/src/hooks/query/use-organization-payment-info.tsx @@ -3,12 +3,14 @@ import { organizationService } from "#/api/organization-service/organization-ser import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useOrganizationPaymentInfo = () => { - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useQuery({ - queryKey: ["organizations", orgId, "payment"], + queryKey: ["organizations", organizationId, "payment"], queryFn: () => - organizationService.getOrganizationPaymentInfo({ orgId: orgId! }), - enabled: !!orgId, + organizationService.getOrganizationPaymentInfo({ + orgId: organizationId!, + }), + enabled: !!organizationId, }); }; diff --git a/frontend/src/hooks/query/use-organization.ts b/frontend/src/hooks/query/use-organization.ts index 8802170cf1..337eb8601a 100644 --- a/frontend/src/hooks/query/use-organization.ts +++ b/frontend/src/hooks/query/use-organization.ts @@ -3,11 +3,12 @@ import { organizationService } from "#/api/organization-service/organization-ser import { useSelectedOrganizationId } from "#/context/use-selected-organization"; export const useOrganization = () => { - const { orgId } = useSelectedOrganizationId(); + const { organizationId } = useSelectedOrganizationId(); return useQuery({ - queryKey: ["organizations", orgId], - queryFn: () => organizationService.getOrganization({ orgId: orgId! }), - enabled: !!orgId, + queryKey: ["organizations", organizationId], + queryFn: () => + organizationService.getOrganization({ orgId: organizationId! }), + enabled: !!organizationId, }); }; diff --git a/frontend/src/stores/selected-organization-store.ts b/frontend/src/stores/selected-organization-store.ts index de487ce5e2..54f1a5a756 100644 --- a/frontend/src/stores/selected-organization-store.ts +++ b/frontend/src/stores/selected-organization-store.ts @@ -2,29 +2,29 @@ import { create } from "zustand"; import { devtools } from "zustand/middleware"; interface SelectedOrganizationState { - orgId: string | null; + organizationId: string | null; } interface SelectedOrganizationActions { - setOrgId: (orgId: string | null) => void; + setOrganizationId: (orgId: string | null) => void; } type SelectedOrganizationStore = SelectedOrganizationState & SelectedOrganizationActions; const initialState: SelectedOrganizationState = { - orgId: null, + organizationId: null, }; export const useSelectedOrganizationStore = create()( devtools( (set) => ({ ...initialState, - setOrgId: (orgId) => set({ orgId }), + setOrganizationId: (organizationId) => set({ organizationId }), }), { name: "SelectedOrganizationStore" }, ), ); export const getSelectedOrganizationIdFromStore = (): string | null => - useSelectedOrganizationStore.getState().orgId; + useSelectedOrganizationStore.getState().organizationId;