Skip to content

Preserve request log filters on detail navigation#1723

Merged
looplj merged 2 commits into
looplj:unstablefrom
zhaozhaozz:fix/request-log-filter-state-1722
May 28, 2026
Merged

Preserve request log filters on detail navigation#1723
looplj merged 2 commits into
looplj:unstablefrom
zhaozhaozz:fix/request-log-filter-state-1722

Conversation

@zhaozhaozz

@zhaozhaozz zhaozhaozz commented May 26, 2026

Copy link
Copy Markdown
Contributor

背景

请求日志页面支持按状态、来源、渠道、API Key、模型 ID 和日期范围过滤请求记录。当前这些过滤条件主要保存在请求日志列表组件的 React state 中。

这会导致一个用户可见的问题:用户设置过滤条件后,打开某条请求详情页,再从详情页返回列表时,列表组件已经卸载并重新挂载,原来的过滤状态会恢复为空。用户需要重新设置过滤条件,排查请求时的上下文会丢失。

Fixes #1722

修改内容

1. 将请求日志过滤状态迁移到 URL search params

frontend/src/features/requests/index.tsx 新增了请求日志过滤参数的解析和写入逻辑,将以下过滤条件从组件内存状态迁移到 TanStack Router 的 search params:

  • 状态过滤:status
  • 来源过滤:source
  • 渠道过滤:channel
  • API Key 过滤:apiKey
  • 模型 ID 过滤:modelID
  • 创建时间范围:createdAtFromcreatedAtTo
  • 创建时间的起止时分秒:createdAtStartTimecreatedAtEndTime

列表页现在会从当前 URL search 中解析过滤条件,并据此生成 GraphQL 查询的 where 条件。

2. 过滤变化时同步更新 URL,并重置分页游标

过滤条件变化后,现在会写回当前路由的 search params,同时清理分页游标相关参数:

  • startCursor
  • endCursor
  • cursorDirection
  • cursorHistory

这样可以避免一个旧分页游标继续作用在新的过滤条件上,导致列表位置或查询结果不符合用户预期。

3. 将请求日志表格过滤状态改为受控状态

frontend/src/features/requests/components/requests-table.tsx 现在由父级传入 URL 派生出的过滤状态,并用这些状态控制 TanStack Table 的 columnFilters

表格过滤变化时,通过统一的 onFiltersChange 回传完整过滤状态,而不是分别调用多个单独的过滤更新回调。这样可以减少多个过滤器连续更新时互相覆盖或状态不同步的风险。

4. 详情跳转保留完整 search params

请求日志列表中的“查看详情”按钮,以及抽屉中的“查看详情”入口,现在会使用列表页传入的详情跳转回调。

跳转到详情页时会保留当前完整 search params,因此详情页 URL 中包含进入详情前的列表过滤状态。

5. 详情页返回保留过滤状态

frontend/src/features/requests/components/request-detail-page.tsx 的返回按钮现在会读取详情页当前的 search params,并带着这些参数返回 /project/requests

因此无论用户使用详情页返回按钮,还是使用浏览器后退,只要进入详情页时携带了过滤 search params,返回列表后都能恢复原来的过滤状态。

6. 重置过滤改为一次性清理请求日志过滤参数

工具栏的重置过滤按钮现在通过父级传入的 onResetFilters 一次性清理所有请求日志过滤 search params。

这样可以避免先清表格过滤、再清日期范围带来的多次路由更新竞态,保证表格过滤状态和日期范围状态一起恢复为空。

7. 明确请求日志路由的 search 类型

frontend/src/routes/_authenticated/project/requests/index.tsxfrontend/src/routes/_authenticated/project/requests/$requestId.tsx 新增 validateSearch,显式声明请求日志列表页和详情页接受 Record<string, unknown> search params。

这样可以移除请求日志列表页和详情页导航中的 as any,保留 TanStack Router 的类型检查能力。

8. 优化日期范围时间参数序列化

日期范围的时间参数现在只会在存在对应日期时写入 URL:

  • createdAtFrom 时才可能写入 createdAtStartTime
  • createdAtTo 时才可能写入 createdAtEndTime

同时,默认时间会从 URL 中省略:

  • 默认开始时间 00:00:00
  • 默认结束时间 23:59:59

这样可以避免孤立的 time 参数,也能减少默认日期范围场景下的 URL 噪音。

对外表现

修改后,请求日志页面的用户可见行为如下:

  1. 用户在请求日志页面设置过滤条件后,URL 会反映当前过滤状态。
  2. 从过滤后的列表打开请求详情页,再返回列表时,过滤条件仍然保留。
  3. 使用浏览器后退或前进时,列表过滤状态会跟随浏览器历史恢复。
  4. 刷新带过滤参数的请求日志页面时,页面会根据 URL 恢复过滤条件。
  5. 点击重置过滤后,所有请求日志过滤条件会一起清空,并回到第一页查询。
  6. 日期范围使用默认时间时,URL 不再额外显示默认时间参数。

作用

这个修改让请求日志页面的过滤状态从“组件生命周期内有效”变成“路由状态可恢复”。它改善了请求排查流程中的上下文保持能力,用户可以在列表和详情之间切换而不丢失筛选条件。

同时,过滤状态进入 URL 后,也便于复制当前过滤后的页面地址进行复现或协作排查。

关于分页游标的说明

详情页 URL 仍然保留完整列表 search,其中可能包含分页游标。这样做是为了保留既有行为:用户从请求日志第 2 页或更后面的页面打开详情页后,返回时仍能回到原来的列表位置。

如果只携带过滤 key,详情页 URL 会更短,但会改变现有分页恢复行为,用户可能从非第一页打开详情后返回到过滤后的第一页。因此本 PR 只在过滤条件变化时清理旧游标,详情导航仍保留完整列表上下文。

风险与兼容性

  • 不涉及后端接口变更。
  • 不改变 GraphQL 查询字段,只改变前端生成查询条件的状态来源。
  • URL 中会出现请求日志过滤参数,但这些参数只表示用户已选择的过滤条件,不包含请求正文或响应内容。
  • 主要风险是后续新增过滤字段时,需要同步维护 search params 的解析、写入和清理逻辑。

验证

  • ./node_modules/.bin/tsc --noEmit --pretty false
  • ./node_modules/.bin/prettier --check src/features/requests/index.tsx src/features/requests/components/index.ts src/features/requests/components/requests-table.tsx src/features/requests/components/data-table-toolbar.tsx src/features/requests/components/requests-columns.tsx src/features/requests/components/request-detail-page.tsx src/routes/_authenticated/project/requests/index.tsx 'src/routes/_authenticated/project/requests/$requestId.tsx'
  • git diff --check
  • 浏览器页面验证(本地前后端服务):模型 ID、状态、来源过滤后,查看详情、详情页返回按钮、浏览器后退均保留过滤状态。

@greptile-apps

greptile-apps Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves request log filter state from component useState into TanStack Router URL search params, so filters survive the list → detail → back navigation cycle. The RequestsTable is refactored to a fully controlled filter pattern (all columnFilters are derived from URL via useMemo), filter changes are consolidated into a single onFiltersChange callback that atomically updates the URL and resets pagination cursors, and both the "View Detail" button and the detail page "Back" button now carry the full search params.

  • Filter state (status, source, channel, apiKey, modelID, date range) is serialized into URL search params via updateRequestSearch, using replace: true so browser history is not polluted on every filter interaction.
  • Both /project/requests and /project/requests/$requestId now opt in to pass-through validateSearch so arbitrary params (including filter + cursor state) flow through the detail route unchanged.
  • The handleResetFilters path clears all filter params and cursor params in a single navigate call, eliminating the previous risk of two sequential updates racing each other.

Confidence Score: 5/5

Safe to merge — this is a pure frontend state-lifting change with no backend contract changes, no schema mutations, and the core URL read/write logic is atomic and correctly resets pagination on every filter change.

The filter state migration to URL params is well-structured: a single navigate call atomically updates both filter keys and clears cursor params, usePaginationSearch continues to read cursor state exclusively from the URL so there are no conflicting sources of truth, and the useMemo-controlled columnFilters pattern correctly feeds TanStack Table without any risk of stale local state. The only observation is a style-level concern about per-keystroke URL writes for the model ID text input.

No files require special attention — frontend/src/features/requests/index.tsx is the most complex changed file but the logic is sound.

Important Files Changed

Filename Overview
frontend/src/features/requests/index.tsx Core orchestration for URL-based filter state; adds parseRequestSearchFilters, updateRequestSearch, and handleViewDetail. Filter + cursor updates are atomic within a single navigate call, avoiding the previous multi-setState race.
frontend/src/features/requests/components/requests-table.tsx Converts columnFilters from internal useState to a useMemo controlled by URL-derived props; consolidates the five separate filter callbacks into a single onFiltersChange. Controlled pattern is correctly wired.
frontend/src/features/requests/components/request-detail-page.tsx Replaces usePaginationSearch.getSearchParams() with useRouterState to read the full current search; back navigation now carries all filter (and cursor) params back to the list page.
frontend/src/features/requests/components/data-table-toolbar.tsx Removes standalone apiKeyFilter/sourceFilter props and consolidates reset into the new onResetFilters callback; isFiltered logic now correctly covers the date-range-only case.
frontend/src/features/requests/components/requests-columns.tsx Adds onViewDetail option to useRequestsColumns; the details button prefers the callback when present, falling back to navigateWithSearch for standalone usages.
frontend/src/routes/_authenticated/project/requests/index.tsx Adds pass-through validateSearch so all URL search params (filters + cursor state) are preserved and surfaced to the component.
frontend/src/routes/_authenticated/project/requests/$requestId.tsx Adds pass-through validateSearch so filter search params forwarded from the list page are preserved in the detail route URL for back navigation.
frontend/src/features/requests/components/index.ts Re-exports the new RequestTableFilters type alongside RequestsTable.

Sequence Diagram

sequenceDiagram
    participant User
    participant Toolbar as DataTableToolbar
    participant Table as RequestsTable
    participant Content as RequestsContent
    participant Router as TanStack Router (URL)
    participant Detail as RequestDetailPage

    User->>Toolbar: Change filter (e.g. status)
    Toolbar->>Table: onColumnFiltersChange(updater)
    Table->>Table: handleColumnFiltersChange → extract values
    Table->>Content: onFiltersChange(filters)
    Content->>Router: "navigate({ search: draft+clearCursors, replace:true })"
    Router-->>Content: useRouterState update (currentSearch)
    Content->>Content: useMemo → parseRequestSearchFilters
    Content->>Table: new columnFilters props
    Table->>Table: useMemo recomputes columnFilters
    Table-->>User: Table re-renders with filtered data

    User->>Table: Click View Detail
    Table->>Content: onViewDetail(requestId)
    Content->>Router: "navigate({ to: $requestId, search: currentSearch })"
    Router-->>Detail: currentSearch (filters + cursors in URL)

    User->>Detail: Click Back
    Detail->>Router: "navigate({ to: /project/requests, search: currentSearch })"
    Router-->>Content: Filter params restored from URL
    Content->>Content: useMemo → parseRequestSearchFilters
    Content-->>User: List rendered with original filters preserved
Loading

Reviews (2): Last reviewed commit: "Address request log filter review feedba..." | Re-trigger Greptile

Comment thread frontend/src/features/requests/components/request-detail-page.tsx Outdated
Comment thread frontend/src/features/requests/index.tsx Outdated
Comment thread frontend/src/features/requests/index.tsx
@looplj looplj merged commit 9bca874 into looplj:unstable May 28, 2026
ldm0206 added a commit to ldm2060/axonhub that referenced this pull request May 29, 2026
…ge title config, request filter preservation, structured response items

Upstream commits merged:
- fix: model settings dialog overflow (looplj#1716)
- chore: custom page title config after login (looplj#1720)
- feat: preserve request log filters on detail navigation (looplj#1723)
- feat(llm): support responses websocket sessions (looplj#1730)
- chore: add claude-opus-4-7 to claudecode DefaultModels (looplj#1733)
- fix(channels): show real i18n label for single-variant types (looplj#1734)
- fix(llm): accept structured response item arguments (looplj#1728)

Resolved 4 import-path conflicts (looplj -> ldm2060) and adapted
session scope integration in auth middleware. Fixed Windows flaky
WebSocket test for platform-specific socket error messages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
junjiangao pushed a commit to junjiangao/axonhub that referenced this pull request May 30, 2026
* Preserve request log filters on detail navigation

* Address request log filter review feedback

---------

Co-authored-by: userZ <spare.nets-0y@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug/错误]: Request log filters reset after returning from details

2 participants