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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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