Skip to content
Merged
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 @@ -52,7 +52,7 @@
* The team selector and filtering have been removed so that all keys are shown.
*/

export function VirtualKeysTable({ teams, organizations, onSortChange, currentSort }: VirtualKeysTableProps) {

Check warning on line 55 in ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Function 'VirtualKeysTable' has a complexity of 23. Maximum allowed is 20
const { data: fetchedOrganizations } = useOrganizations();
const resolvedOrganizations = fetchedOrganizations ?? organizations ?? [];
const [selectedKey, setSelectedKey] = useState<KeyResponse | null>(null);
Expand Down Expand Up @@ -96,6 +96,8 @@

// Use the filter logic hook

const keyList = useMemo(() => keys?.keys ?? [], [keys]);

const {
filters,
filteredKeys,
Expand All @@ -105,7 +107,7 @@
handleFilterChange,
handleFilterReset,
} = useFilterLogic({
keys: keys?.keys || [],
keys: keyList,
teams,
organizations,
});
Expand Down Expand Up @@ -584,7 +586,7 @@
},
},
],
[teams, resolvedOrganizations],

Check warning on line 589 in ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

React Hook useMemo has a missing dependency: 'expandedAccordions'. Either include it or remove the dependency array
);

const filterOptions: FilterOption[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
};

const defaultProps = {
keys: [mockKey] as any[],

Check warning on line 23 in ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
teams: [],
organizations: [],
};

const makeApiResponse = (overrides: { keys?: any[]; total_count?: number; total_pages?: number } = {}) => ({

Check warning on line 28 in ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
keys: overrides.keys ?? [mockKey],
total_count: overrides.total_count ?? 1,
current_page: 1,
Expand Down Expand Up @@ -149,6 +149,23 @@
expect(result.current.filteredTotalCount).toBeNull();
});

it("should not enter an infinite update loop when keys is a fresh array reference on every render", () => {
const sourceKeys = [mockKey];
let renderCount = 0;

const { result } = renderHook(() => {
renderCount += 1;
const value = useFilterLogic({ keys: [...sourceKeys], teams: [], organizations: [] });
if (renderCount > 25) {
throw new Error(`useFilterLogic re-rendered ${renderCount} times; setFilteredKeys is looping`);
}
return value;
});

expect(result.current.filteredKeys).toEqual([mockKey]);
expect(renderCount).toBeLessThanOrEqual(25);
});

it("should not trigger a debounced search when skipDebounce is true", async () => {
const { result } = renderHook(() => useFilterLogic(defaultProps));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
const [filteredKeys, setFilteredKeys] = useState<KeyResponse[]>(keys);
const [filteredTotalCount, setFilteredTotalCount] = useState<number | null>(null);
const lastSearchTimestamp = useRef(0);
const debouncedSearch = useCallback(

Check warning on line 44 in ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead
debounce(async (filters: FilterState) => {
if (!accessToken) {
return;
Expand Down Expand Up @@ -99,7 +99,9 @@
result = result.filter((key) => (key.organization_id ?? key.org_id) === filters["Organization ID"]);
}

setFilteredKeys(result);
setFilteredKeys((prev) =>
prev.length === result.length && prev.every((key, index) => key === result[index]) ? prev : result,
);
}, [keys, filters]);

// Fetch all data for filters when component mounts
Expand Down
Loading