Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,111 @@ describe("DefaultUserSettings", () => {

expect(screen.getByText("Edit Settings")).toBeInTheDocument();
});

describe("editable fields", () => {
const teamsOnlySettings = {
values: {
teams: [],
},
field_schema: {
description: "Default user settings",
properties: {
teams: {
type: "array",
description: "Teams",
},
},
},
};

const enterEditMode = async () => {
mockGetInternalUserSettings.mockResolvedValue(teamsOnlySettings);
mockUpdateInternalUserSettings.mockResolvedValue({ settings: {} });

render(<DefaultUserSettings {...defaultProps} />);

await waitFor(() => {
expect(screen.getByText("Edit Settings")).toBeInTheDocument();
});

act(() => {
fireEvent.click(screen.getByText("Edit Settings"));
});
};

const savedPayload = () => mockUpdateInternalUserSettings.mock.calls[0][1] as Record<string, unknown>;

const selectUserRole = async (optionText: string) => {
act(() => {
fireEvent.mouseDown(document.querySelector(".ant-select-selector")!);
});

await waitFor(() => {
expect(document.querySelectorAll(".ant-select-item-option").length).toBeGreaterThan(0);
});

const option = Array.from(document.querySelectorAll(".ant-select-item-option")).find((el) =>
el.textContent?.includes(optionText),
);
expect(option).toBeTruthy();

act(() => {
fireEvent.click(option!);
});
};

it("keeps the stored role and max budget of a team saved without an ID", async () => {
mockGetInternalUserSettings.mockResolvedValue({
...teamsOnlySettings,
values: { teams: [{ team_id: "", max_budget_in_team: 25, user_role: "admin" }] },
});
mockUpdateInternalUserSettings.mockResolvedValue({ settings: {} });

render(<DefaultUserSettings {...defaultProps} />);

await waitFor(() => {
expect(screen.getByText("Edit Settings")).toBeInTheDocument();
});
act(() => {
fireEvent.click(screen.getByText("Edit Settings"));
});

expect(screen.getByPlaceholderText("Enter team ID")).toHaveValue("");
expect(screen.getByPlaceholderText("Optional")).toHaveValue("25.00");
expect(document.querySelector(".ant-select-selection-item")).toHaveTextContent("Admin");
});

it("keeps showing the selected role of a team whose ID has not been typed yet", async () => {
await enterEditMode();

act(() => {
fireEvent.click(screen.getByText("Add Team"));
});
await selectUserRole("Admin");

expect(document.querySelector(".ant-select-selection-item")).toHaveTextContent("Admin");
});

it("keeps the role and max budget of a team whose ID has not been typed yet", async () => {
await enterEditMode();

act(() => {
fireEvent.click(screen.getByText("Add Team"));
});

act(() => {
fireEvent.change(screen.getByPlaceholderText("Optional"), { target: { value: "25" } });
});
await selectUserRole("Admin");

act(() => {
fireEvent.click(screen.getByText("Save Changes"));
});

await waitFor(() => {
expect(mockUpdateInternalUserSettings).toHaveBeenCalled();
});
expect(savedPayload().teams).toEqual([{ team_id: "", max_budget_in_team: 25, user_role: "admin" }]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
userRole,
}) => {
const [loading, setLoading] = useState<boolean>(true);
const [settings, setSettings] = useState<any>(null);

Check warning on line 33 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
const [isEditing, setIsEditing] = useState<boolean>(false);
const [editedValues, setEditedValues] = useState<any>({});

Check warning on line 35 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
const [saving, setSaving] = useState<boolean>(false);
const [availableModels, setAvailableModels] = useState<string[]>([]);
const { Paragraph } = Typography;
Expand Down Expand Up @@ -71,7 +71,7 @@
};

fetchSSOSettings();
}, [accessToken]);

Check warning on line 74 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

React Hook useEffect has missing dependencies: 'userID' and 'userRole'. Either include them or remove the dependency array

const handleSaveSettings = async () => {
if (!accessToken) return;
Expand All @@ -84,7 +84,7 @@
acc[key] = value === "" ? null : value;
return acc;
},
{} as Record<string, any>,

Check warning on line 87 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
);

const updatedSettings = await updateInternalUserSettings(accessToken, processedValues);
Expand All @@ -98,15 +98,15 @@
}
};

const handleTextInputChange = (key: string, value: any) => {

Check warning on line 101 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
setEditedValues((prev: Record<string, any>) => ({

Check warning on line 102 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
...prev,
[key]: value,
}));
};

// Helper function to normalize teams array to consistent format
const normalizeTeams = (teams: any[]): TeamEntry[] => {

Check warning on line 109 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
if (!teams || !Array.isArray(teams)) return [];

return teams.map((team) => {
Expand All @@ -115,7 +115,7 @@
team_id: team,
user_role: "user" as const,
};
} else if (typeof team === "object" && team.team_id) {
} else if (typeof team === "object" && team !== null && "team_id" in team) {
return {
team_id: team.team_id,
max_budget_in_team: team.max_budget_in_team,
Expand All @@ -130,10 +130,10 @@
};

// Teams editor component
const renderTeamsEditor = (teams: any[]) => {

Check warning on line 133 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
const normalizedTeams = normalizeTeams(teams);

const updateTeam = (index: number, field: keyof TeamEntry, value: any) => {

Check warning on line 136 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
const updatedTeams = [...normalizedTeams];
updatedTeams[index] = {
...updatedTeams[index],
Expand Down Expand Up @@ -211,7 +211,7 @@
);
};

const renderEditableField = (key: string, property: any, value: any) => {

Check warning on line 214 in ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
const type = property.type;

if (key === "teams") {
Expand Down
Loading