hotfix(frontend): org naming consistency (#12164)

This commit is contained in:
sp.wack
2025-12-26 19:59:18 +04:00
committed by GitHub
parent 56550cb0a8
commit a0b958b221
15 changed files with 84 additions and 72 deletions

View File

@@ -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 () => {

View File

@@ -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");
});
});

View File

@@ -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;
}

View File

@@ -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<HTMLDivElement>(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);
}
}}
/>

View File

@@ -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 };
};

View File

@@ -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("/");
},
});

View File

@@ -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],
});
},
});

View File

@@ -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],
});
},
});

View File

@@ -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,
});

View File

@@ -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],
});
},
});
};

View File

@@ -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,
});
};

View File

@@ -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,
});
};

View File

@@ -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,
});
};

View File

@@ -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,
});
};

View File

@@ -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<SelectedOrganizationStore>()(
devtools(
(set) => ({
...initialState,
setOrgId: (orgId) => set({ orgId }),
setOrganizationId: (organizationId) => set({ organizationId }),
}),
{ name: "SelectedOrganizationStore" },
),
);
export const getSelectedOrganizationIdFromStore = (): string | null =>
useSelectedOrganizationStore.getState().orgId;
useSelectedOrganizationStore.getState().organizationId;