mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
hotfix(frontend): org naming consistency (#12164)
This commit is contained in:
@@ -1,9 +1,14 @@
|
|||||||
import { within, screen, render } from "@testing-library/react";
|
import { within, screen, render } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
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 { QueryClientProvider, QueryClient } from "@tanstack/react-query";
|
||||||
import { organizationService } from "#/api/organization-service/organization-service.api";
|
import { organizationService } from "#/api/organization-service/organization-service.api";
|
||||||
import { InviteOrganizationMemberModal } from "#/components/features/org/invite-organization-member-modal";
|
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?: {
|
const renderInviteOrganizationMemberModal = (config?: {
|
||||||
onClose: () => void;
|
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", () => {
|
describe("InviteOrganizationMemberModal", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
useSelectedOrganizationStore.setState({ organizationId: "1" });
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
useSelectedOrganizationStore.setState({ organizationId: null });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call onClose the modal when the close button is clicked", async () => {
|
it("should call onClose the modal when the close button is clicked", async () => {
|
||||||
|
|||||||
@@ -3,35 +3,35 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { useSelectedOrganizationStore } from "#/stores/selected-organization-store";
|
import { useSelectedOrganizationStore } from "#/stores/selected-organization-store";
|
||||||
|
|
||||||
describe("useSelectedOrganizationStore", () => {
|
describe("useSelectedOrganizationStore", () => {
|
||||||
it("should have null as initial orgId", () => {
|
it("should have null as initial organizationId", () => {
|
||||||
const { result } = renderHook(() => useSelectedOrganizationStore());
|
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());
|
const { result } = renderHook(() => useSelectedOrganizationStore());
|
||||||
|
|
||||||
act(() => {
|
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());
|
const { result } = renderHook(() => useSelectedOrganizationStore());
|
||||||
|
|
||||||
act(() => {
|
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(() => {
|
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", () => {
|
it("should share state across multiple hook instances", () => {
|
||||||
@@ -43,9 +43,9 @@ describe("useSelectedOrganizationStore", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
act(() => {
|
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");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function SettingsNavigation({
|
|||||||
onCloseMobileMenu,
|
onCloseMobileMenu,
|
||||||
navigationItems,
|
navigationItems,
|
||||||
}: SettingsNavigationProps) {
|
}: SettingsNavigationProps) {
|
||||||
const { orgId, setOrgId } = useSelectedOrganizationId();
|
const { organizationId, setOrganizationId } = useSelectedOrganizationId();
|
||||||
const { data: organizations } = useOrganizations();
|
const { data: organizations } = useOrganizations();
|
||||||
const { data: me } = useMe();
|
const { data: me } = useMe();
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ export function SettingsNavigation({
|
|||||||
testId="org-select"
|
testId="org-select"
|
||||||
name="organization"
|
name="organization"
|
||||||
placeholder="Please select an organization"
|
placeholder="Please select an organization"
|
||||||
selectedKey={orgId || ""}
|
selectedKey={organizationId || ""}
|
||||||
items={
|
items={
|
||||||
organizations?.map((org) => ({
|
organizations?.map((org) => ({
|
||||||
key: org.id,
|
key: org.id,
|
||||||
@@ -65,9 +65,9 @@ export function SettingsNavigation({
|
|||||||
}
|
}
|
||||||
onSelectionChange={(org) => {
|
onSelectionChange={(org) => {
|
||||||
if (org) {
|
if (org) {
|
||||||
setOrgId(org.toString());
|
setOrganizationId(org.toString());
|
||||||
} else {
|
} else {
|
||||||
setOrgId(null);
|
setOrganizationId(null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -96,7 +96,7 @@ export function SettingsNavigation({
|
|||||||
if (
|
if (
|
||||||
(navItem.to === "/settings/org-members" ||
|
(navItem.to === "/settings/org-members" ||
|
||||||
navItem.to === "/settings/org") &&
|
navItem.to === "/settings/org") &&
|
||||||
(isUser || !orgId)
|
(isUser || !organizationId)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ interface UserContextMenuProps {
|
|||||||
export function UserContextMenu({ type, onClose }: UserContextMenuProps) {
|
export function UserContextMenu({ type, onClose }: UserContextMenuProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { orgId, setOrgId } = useSelectedOrganizationId();
|
const { organizationId, setOrganizationId } = useSelectedOrganizationId();
|
||||||
const { data: organizations } = useOrganizations();
|
const { data: organizations } = useOrganizations();
|
||||||
const { mutate: logout } = useLogout();
|
const { mutate: logout } = useLogout();
|
||||||
const ref = useClickOutsideElement<HTMLDivElement>(onClose);
|
const ref = useClickOutsideElement<HTMLDivElement>(onClose);
|
||||||
@@ -100,7 +100,7 @@ export function UserContextMenu({ type, onClose }: UserContextMenuProps) {
|
|||||||
testId="org-selector"
|
testId="org-selector"
|
||||||
name="organization"
|
name="organization"
|
||||||
placeholder="Please select an organization"
|
placeholder="Please select an organization"
|
||||||
selectedKey={orgId || "personal"}
|
selectedKey={organizationId || "personal"}
|
||||||
items={[
|
items={[
|
||||||
{ key: "personal", label: "Personal Account" },
|
{ key: "personal", label: "Personal Account" },
|
||||||
...(organizations?.map((org) => ({
|
...(organizations?.map((org) => ({
|
||||||
@@ -110,11 +110,11 @@ export function UserContextMenu({ type, onClose }: UserContextMenuProps) {
|
|||||||
]}
|
]}
|
||||||
onSelectionChange={(org) => {
|
onSelectionChange={(org) => {
|
||||||
if (org === "personal") {
|
if (org === "personal") {
|
||||||
setOrgId(null);
|
setOrganizationId(null);
|
||||||
} else if (org) {
|
} else if (org) {
|
||||||
setOrgId(org.toString());
|
setOrganizationId(org.toString());
|
||||||
} else {
|
} else {
|
||||||
setOrgId(null);
|
setOrganizationId(null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import { useSelectedOrganizationStore } from "#/stores/selected-organization-sto
|
|||||||
|
|
||||||
export const useSelectedOrganizationId = () => {
|
export const useSelectedOrganizationId = () => {
|
||||||
const revalidator = useRevalidator();
|
const revalidator = useRevalidator();
|
||||||
const { orgId, setOrgId: setOrgIdStore } = useSelectedOrganizationStore();
|
const { organizationId, setOrganizationId: setOrganizationIdStore } =
|
||||||
|
useSelectedOrganizationStore();
|
||||||
|
|
||||||
const setOrgId = (newOrgId: string | null) => {
|
const setOrganizationId = (newOrganizationId: string | null) => {
|
||||||
setOrgIdStore(newOrgId);
|
setOrganizationIdStore(newOrganizationId);
|
||||||
// Revalidate route to ensure the latest orgId is used.
|
// Revalidate route to ensure the latest orgId is used.
|
||||||
// This is useful for redirecting the user away from admin-only org pages.
|
// This is useful for redirecting the user away from admin-only org pages.
|
||||||
revalidator.revalidate();
|
revalidator.revalidate();
|
||||||
};
|
};
|
||||||
|
|
||||||
return { orgId, setOrgId };
|
return { organizationId, setOrganizationId };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
|||||||
export const useDeleteOrganization = () => {
|
export const useDeleteOrganization = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { orgId, setOrgId } = useSelectedOrganizationId();
|
const { organizationId, setOrganizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: () => {
|
mutationFn: () => {
|
||||||
if (!orgId) throw new Error("Organization ID is required");
|
if (!organizationId) throw new Error("Organization ID is required");
|
||||||
return organizationService.deleteOrganization({ orgId });
|
return organizationService.deleteOrganization({ orgId: organizationId });
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["organizations"] });
|
queryClient.invalidateQueries({ queryKey: ["organizations"] });
|
||||||
setOrgId(null);
|
setOrganizationId(null);
|
||||||
navigate("/");
|
navigate("/");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
|||||||
|
|
||||||
export const useInviteMembersBatch = () => {
|
export const useInviteMembersBatch = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: ({ emails }: { emails: string[] }) =>
|
mutationFn: ({ emails }: { emails: string[] }) =>
|
||||||
organizationService.inviteMembers({ orgId: orgId!, emails }),
|
organizationService.inviteMembers({ orgId: organizationId!, emails }),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["organizations", "members", orgId],
|
queryKey: ["organizations", "members", organizationId],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
|||||||
|
|
||||||
export const useRemoveMember = () => {
|
export const useRemoveMember = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: ({ userId }: { userId: string }) =>
|
mutationFn: ({ userId }: { userId: string }) =>
|
||||||
organizationService.removeMember({ orgId: orgId!, userId }),
|
organizationService.removeMember({ orgId: organizationId!, userId }),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["organizations", "members", orgId],
|
queryKey: ["organizations", "members", organizationId],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
|||||||
|
|
||||||
export const useUpdateMemberRole = () => {
|
export const useUpdateMemberRole = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
@@ -15,11 +15,11 @@ export const useUpdateMemberRole = () => {
|
|||||||
userId: string;
|
userId: string;
|
||||||
role: OrganizationUserRole;
|
role: OrganizationUserRole;
|
||||||
}) => {
|
}) => {
|
||||||
if (!orgId) {
|
if (!organizationId) {
|
||||||
throw new Error("Organization ID is required to update member role");
|
throw new Error("Organization ID is required to update member role");
|
||||||
}
|
}
|
||||||
return organizationService.updateMember({
|
return organizationService.updateMember({
|
||||||
orgId,
|
orgId: organizationId,
|
||||||
userId,
|
userId,
|
||||||
role,
|
role,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,15 +4,20 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
|||||||
|
|
||||||
export const useUpdateOrganization = () => {
|
export const useUpdateOrganization = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (name: string) => {
|
mutationFn: (name: string) => {
|
||||||
if (!orgId) throw new Error("Organization ID is required");
|
if (!organizationId) throw new Error("Organization ID is required");
|
||||||
return organizationService.updateOrganization({ orgId, name });
|
return organizationService.updateOrganization({
|
||||||
|
orgId: organizationId,
|
||||||
|
name,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["organizations", orgId] });
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["organizations", organizationId],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
|||||||
|
|
||||||
export const useMe = () => {
|
export const useMe = () => {
|
||||||
const { data: config } = useConfig();
|
const { data: config } = useConfig();
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
const isSaas = config?.APP_MODE === "saas";
|
const isSaas = config?.APP_MODE === "saas";
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["organizations", orgId, "me"],
|
queryKey: ["organizations", organizationId, "me"],
|
||||||
queryFn: () => organizationService.getMe({ orgId: orgId! }),
|
queryFn: () => organizationService.getMe({ orgId: organizationId! }),
|
||||||
enabled: isSaas && !!orgId,
|
enabled: isSaas && !!organizationId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { organizationService } from "#/api/organization-service/organization-ser
|
|||||||
import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
||||||
|
|
||||||
export const useOrganizationMembers = () => {
|
export const useOrganizationMembers = () => {
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["organizations", "members", orgId],
|
queryKey: ["organizations", "members", organizationId],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
organizationService.getOrganizationMembers({ orgId: orgId! }),
|
organizationService.getOrganizationMembers({ orgId: organizationId! }),
|
||||||
enabled: !!orgId,
|
enabled: !!organizationId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import { organizationService } from "#/api/organization-service/organization-ser
|
|||||||
import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
||||||
|
|
||||||
export const useOrganizationPaymentInfo = () => {
|
export const useOrganizationPaymentInfo = () => {
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["organizations", orgId, "payment"],
|
queryKey: ["organizations", organizationId, "payment"],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
organizationService.getOrganizationPaymentInfo({ orgId: orgId! }),
|
organizationService.getOrganizationPaymentInfo({
|
||||||
enabled: !!orgId,
|
orgId: organizationId!,
|
||||||
|
}),
|
||||||
|
enabled: !!organizationId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { organizationService } from "#/api/organization-service/organization-ser
|
|||||||
import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
import { useSelectedOrganizationId } from "#/context/use-selected-organization";
|
||||||
|
|
||||||
export const useOrganization = () => {
|
export const useOrganization = () => {
|
||||||
const { orgId } = useSelectedOrganizationId();
|
const { organizationId } = useSelectedOrganizationId();
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["organizations", orgId],
|
queryKey: ["organizations", organizationId],
|
||||||
queryFn: () => organizationService.getOrganization({ orgId: orgId! }),
|
queryFn: () =>
|
||||||
enabled: !!orgId,
|
organizationService.getOrganization({ orgId: organizationId! }),
|
||||||
|
enabled: !!organizationId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,29 +2,29 @@ import { create } from "zustand";
|
|||||||
import { devtools } from "zustand/middleware";
|
import { devtools } from "zustand/middleware";
|
||||||
|
|
||||||
interface SelectedOrganizationState {
|
interface SelectedOrganizationState {
|
||||||
orgId: string | null;
|
organizationId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectedOrganizationActions {
|
interface SelectedOrganizationActions {
|
||||||
setOrgId: (orgId: string | null) => void;
|
setOrganizationId: (orgId: string | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectedOrganizationStore = SelectedOrganizationState &
|
type SelectedOrganizationStore = SelectedOrganizationState &
|
||||||
SelectedOrganizationActions;
|
SelectedOrganizationActions;
|
||||||
|
|
||||||
const initialState: SelectedOrganizationState = {
|
const initialState: SelectedOrganizationState = {
|
||||||
orgId: null,
|
organizationId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectedOrganizationStore = create<SelectedOrganizationStore>()(
|
export const useSelectedOrganizationStore = create<SelectedOrganizationStore>()(
|
||||||
devtools(
|
devtools(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
...initialState,
|
...initialState,
|
||||||
setOrgId: (orgId) => set({ orgId }),
|
setOrganizationId: (organizationId) => set({ organizationId }),
|
||||||
}),
|
}),
|
||||||
{ name: "SelectedOrganizationStore" },
|
{ name: "SelectedOrganizationStore" },
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getSelectedOrganizationIdFromStore = (): string | null =>
|
export const getSelectedOrganizationIdFromStore = (): string | null =>
|
||||||
useSelectedOrganizationStore.getState().orgId;
|
useSelectedOrganizationStore.getState().organizationId;
|
||||||
|
|||||||
Reference in New Issue
Block a user