mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
fix(ui): keep team create org and models selections during org refetch
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
4ba8517134
commit
b907bab816
2 changed files with 78 additions and 12 deletions
|
|
@ -1488,3 +1488,71 @@ describe("Teams - the exact bytes the create call sends", () => {
|
|||
expect(teamCreateCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Teams - create team modal keeps user selections", () => {
|
||||
const orgs = () => [
|
||||
{ organization_id: "org-1", organization_alias: "Org One", models: [], members: [] },
|
||||
{ organization_id: "org-2", organization_alias: "Org Two", models: [], members: [] },
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(fetchAvailableModelsForTeamOrKey).mockResolvedValue(["gpt-4"]);
|
||||
vi.mocked(fetchMCPAccessGroups).mockResolvedValue([]);
|
||||
vi.mocked(getGuardrailsList).mockResolvedValue({ guardrails: [] });
|
||||
mockUseOrganizations.mockReturnValue({ data: orgs() });
|
||||
});
|
||||
|
||||
const openCreateModal = async () => {
|
||||
const queryClient = createQueryClient();
|
||||
const wrap = (children: React.ReactElement) => (
|
||||
<NuqsTestingAdapter hasMemory>
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
</NuqsTestingAdapter>
|
||||
);
|
||||
const view = render(wrap(<Teams accessToken="test-token" userID="user-123" userRole="Admin" />));
|
||||
act(() => {
|
||||
fireEvent.click(screen.getAllByRole("button", { name: /create team/i })[0]);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText(/team name/i)).toBeInTheDocument();
|
||||
});
|
||||
return {
|
||||
rerenderTeams: () => view.rerender(wrap(<Teams accessToken="test-token" userID="user-123" userRole="Admin" />)),
|
||||
};
|
||||
};
|
||||
|
||||
const selectOrganization = async (label: string) => {
|
||||
const user = userEvent.setup();
|
||||
const orgCombobox = screen.getByRole("combobox", { name: /organization/i });
|
||||
await user.click(orgCombobox);
|
||||
await user.click(await screen.findByText(label));
|
||||
return orgCombobox;
|
||||
};
|
||||
|
||||
it("keeps the selected organization and models when the organizations list refetches", async () => {
|
||||
const { rerenderTeams } = await openCreateModal();
|
||||
|
||||
const orgCombobox = await selectOrganization("Org One");
|
||||
fireEvent.change(screen.getByTestId("create-team-models-select"), { target: { value: "gpt-4" } });
|
||||
|
||||
mockUseOrganizations.mockReturnValue({ data: orgs() });
|
||||
act(() => {
|
||||
rerenderTeams();
|
||||
});
|
||||
|
||||
expect(orgCombobox).toHaveValue("Org One");
|
||||
expect(screen.getByTestId("create-team-models-select")).toHaveValue("gpt-4");
|
||||
});
|
||||
|
||||
it("clears the selected models when the user picks a different organization", async () => {
|
||||
await openCreateModal();
|
||||
|
||||
await selectOrganization("Org One");
|
||||
fireEvent.change(screen.getByTestId("create-team-models-select"), { target: { value: "gpt-4" } });
|
||||
expect(screen.getByTestId("create-team-models-select")).toHaveValue("gpt-4");
|
||||
|
||||
await selectOrganization("Org Two");
|
||||
expect(screen.getByTestId("create-team-models-select")).toHaveValue("");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { useZodForm } from "@/lib/forms/useZodForm";
|
|||
import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filter/TagsInput";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { ChevronDown, Plus, Users } from "lucide-react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { z } from "zod/v4";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { PageHeader } from "@/components/shared/PageHeader";
|
||||
|
|
@ -264,13 +264,10 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
|
|||
? `Default: ${getBudgetDurationLabel(defaultBudgetDuration)} (${defaultBudgetDuration})`
|
||||
: "n/a";
|
||||
|
||||
useEffect(() => {
|
||||
form.setValue("models", []);
|
||||
}, [currentOrgForCreateTeam, userModels]);
|
||||
|
||||
// Handle organization preselection when modal opens
|
||||
const wasTeamModalVisible = useRef(false);
|
||||
useEffect(() => {
|
||||
if (isTeamModalVisible) {
|
||||
if (isTeamModalVisible && !wasTeamModalVisible.current) {
|
||||
const adminOrgs = getAdminOrganizations(userRole, userID, organizations);
|
||||
|
||||
// Org admins must scope a team to an org, so with exactly one we preselect it.
|
||||
|
|
@ -284,6 +281,7 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
|
|||
setCurrentOrgForCreateTeam(currentOrg);
|
||||
}
|
||||
}
|
||||
wasTeamModalVisible.current = isTeamModalVisible;
|
||||
}, [isTeamModalVisible, isOrgAdmin, userRole, userID, organizations, currentOrg]);
|
||||
|
||||
// Add this useEffect to fetch guardrails
|
||||
|
|
@ -686,6 +684,11 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
|
|||
const adminOrgs = getAdminOrganizations(userRole, userID, organizations);
|
||||
const isSingleOrg = adminOrgs.length === 1;
|
||||
const hasNoOrgs = adminOrgs.length === 0;
|
||||
const handleCreateTeamOrgChange = (next: string, onChange: (value: string | null) => void) => {
|
||||
onChange(next === "" ? null : next);
|
||||
setCurrentOrgForCreateTeam(adminOrgs.find((org) => org.organization_id === next) ?? null);
|
||||
form.setValue("models", []);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -721,12 +724,7 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
|
|||
hasNoOrgs ? "No organizations available" : "Search or select an Organization"
|
||||
}
|
||||
emptyText="No organizations available"
|
||||
onValueChange={(next) => {
|
||||
onChange(next === "" ? null : next);
|
||||
setCurrentOrgForCreateTeam(
|
||||
adminOrgs.find((org) => org.organization_id === next) ?? null,
|
||||
);
|
||||
}}
|
||||
onValueChange={(next) => handleCreateTeamOrgChange(next, onChange)}
|
||||
/>
|
||||
)}
|
||||
</FormField>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue