mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
[Fix] UI - Logs: Fix table not updating with custom time range and pagination issues
Fix two bugs in the logs table with backend filters (e.g., Key Alias): 1. Bug 1 - Table doesn't update with custom time range: When Key Alias filter was active and user selected a custom time range, the main query would refetch (network request visible) but backendFilteredLogs would stay stale because the performSearch effect only watched [sortBy, sortOrder, currentPage]. Added startTime, endTime, isCustomDate to the effect deps. 2. Bug 2 - Pagination shows wrong results: fetchKeyHashForAlias incorrectly had currentPage (log page) in its deps, causing it to search the wrong page of the key list and trigger unnecessary effect re-runs. Removed currentPage from deps and always pass page 1 for key alias lookup. Also added debouncedSearch.cancel() in the effect to prevent race conditions when pagination happens within 300ms of filter application. Added tests verifying that time range changes trigger refetch when backend filters are active. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
33d49e92cb
commit
f6eea31739
3 changed files with 64 additions and 4 deletions
|
|
@ -259,7 +259,7 @@ export default function SpendLogsTable({
|
|||
if (!accessToken) return;
|
||||
|
||||
try {
|
||||
const response = await keyListCall(accessToken, null, null, keyAlias, null, null, currentPage, pageSize);
|
||||
const response = await keyListCall(accessToken, null, null, keyAlias, null, null, 1, pageSize);
|
||||
|
||||
const selectedKey = response.keys.find((key: any) => key.key_alias === keyAlias);
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ export default function SpendLogsTable({
|
|||
console.error("Error fetching key hash for alias:", error);
|
||||
}
|
||||
},
|
||||
[accessToken, currentPage, pageSize],
|
||||
[accessToken, pageSize],
|
||||
);
|
||||
|
||||
const handleFilterReset = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -570,6 +570,63 @@ describe("useLogFilterLogic", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("should refetch when startTime changes and backend filters are active", async () => {
|
||||
vi.mocked(uiSpendLogsCall).mockResolvedValue(
|
||||
createPaginatedResponse([createLogEntry()]),
|
||||
);
|
||||
const logs = createPaginatedResponse([createLogEntry()]);
|
||||
const { result, rerender } = renderHook(
|
||||
(props: { startTime?: string }) =>
|
||||
useLogFilterLogic({ ...defaultProps, logs, ...props }),
|
||||
{ wrapper, initialProps: { startTime: "2025-01-01T00:00:00Z" } },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.handleFilterChange({ "Key Alias": "alias-1" });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(uiSpendLogsCall).toHaveBeenCalledTimes(1), {
|
||||
timeout: 500,
|
||||
});
|
||||
|
||||
rerender({ startTime: "2025-01-02T00:00:00Z" });
|
||||
|
||||
await waitFor(() => expect(uiSpendLogsCall).toHaveBeenCalledTimes(2), {
|
||||
timeout: 500,
|
||||
});
|
||||
expect(uiSpendLogsCall).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
start_date: "2025-01-02 00:00:00",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should refetch when isCustomDate changes and backend filters are active", async () => {
|
||||
vi.mocked(uiSpendLogsCall).mockResolvedValue(
|
||||
createPaginatedResponse([createLogEntry()]),
|
||||
);
|
||||
const logs = createPaginatedResponse([createLogEntry()]);
|
||||
const { result, rerender } = renderHook(
|
||||
(props: { isCustomDate?: boolean }) =>
|
||||
useLogFilterLogic({ ...defaultProps, logs, ...props }),
|
||||
{ wrapper, initialProps: { isCustomDate: false } },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.handleFilterChange({ "Key Alias": "alias-1" });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(uiSpendLogsCall).toHaveBeenCalledTimes(1), {
|
||||
timeout: 500,
|
||||
});
|
||||
|
||||
rerender({ isCustomDate: true });
|
||||
|
||||
await waitFor(() => expect(uiSpendLogsCall).toHaveBeenCalledTimes(2), {
|
||||
timeout: 500,
|
||||
});
|
||||
});
|
||||
|
||||
it("should not call setCurrentPage when handleFilterChange receives identical filters", async () => {
|
||||
const setCurrentPage = vi.fn();
|
||||
const logs = createPaginatedResponse([createLogEntry()]);
|
||||
|
|
|
|||
|
|
@ -158,12 +158,15 @@ export function useLogFilterLogic({
|
|||
[filters],
|
||||
);
|
||||
|
||||
// Refetch when sort or page changes (backend filters use their own fetch, not the main query)
|
||||
// Refetch when sort, page, or time range changes (backend filters use their own fetch, not the main query)
|
||||
useEffect(() => {
|
||||
if (hasBackendFilters && accessToken) {
|
||||
// Cancel any pending debounced search to prevent it from overwriting this page's results
|
||||
debouncedSearch.cancel();
|
||||
performSearch(filters, currentPage);
|
||||
}
|
||||
}, [sortBy, sortOrder, currentPage]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sortBy, sortOrder, currentPage, startTime, endTime, isCustomDate]);
|
||||
|
||||
// Compute client-side filtered logs directly from incoming logs and filters
|
||||
const clientDerivedFilteredLogs: PaginatedResponse = useMemo(() => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue