Skip to content

fix: use parseAsSafeArrayOf for dashboard array query params and parseAsBoolean for missing_cost_only - #4796

Closed
impoiler wants to merge 1 commit into
graphite-base/4796from
06-30-fix_handle_special_characters_in_dashboard_filters
Closed

fix: use parseAsSafeArrayOf for dashboard array query params and parseAsBoolean for missing_cost_only#4796
impoiler wants to merge 1 commit into
graphite-base/4796from
06-30-fix_handle_special_characters_in_dashboard_filters

Conversation

@impoiler

Copy link
Copy Markdown
Contributor

Summary

Dashboard filter query parameters that previously stored arrays as comma-separated strings are now stored as proper typed arrays using parseAsSafeArrayOf. The missing_cost_only flag is also now stored as a native boolean rather than the string "true"/"false".

Changes

  • Replaced parseAsString with parseAsSafeArrayOf for all array-valued filter URL params (providers, models, virtual_key_ids, selected_key_ids, objects, status, routing_rule_ids, routing_engine_used, stop_reasons, user_ids, team_ids, customer_ids, business_unit_ids, aliases), eliminating the need to manually split/join CSV strings.
  • Replaced parseAsString with parseAsBoolean for missing_cost_only, removing the === "true" string comparison.
  • Removed the local parseCsvParam helper and all intermediate useMemo variables that existed solely to parse CSV strings into arrays. Filter values from urlState are now used directly.
  • Removed the .join(",") calls in the setFilters callback and the urlState.period redundant assignment.
  • MCP tool name and server label params (mcp_tool_names, mcp_server_labels) remain as strings for now, with their array conversion handled by a targeted useMemo.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Apply filters on the dashboard (providers, models, virtual keys, statuses, etc.) and verify:

  1. The URL reflects the selected filters correctly as array-style query params.
  2. Refreshing the page or sharing the URL restores the same filter state.
  3. The missing_cost_only toggle persists correctly as a boolean in the URL.
  4. Clearing filters resets the URL params to their defaults.
cd ui
pnpm i || npm i
pnpm build || npm run build

Screenshots/Recordings

N/A

Breaking changes

  • Yes
  • No

Existing bookmarked or shared dashboard URLs that use comma-separated string query params for filters will no longer be parsed correctly. Users with saved URLs containing these params will need to reapply their filters.

Related issues

N/A

Security considerations

None.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 61991859-a59f-4b5f-aaaa-bde9c8db5707

📥 Commits

Reviewing files that changed from the base of the PR and between d7ea1a2 and c61b851.

📒 Files selected for processing (1)
  • ui/app/workspace/dashboard/page.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-30-fix_handle_special_characters_in_dashboard_filters

Comment @coderabbitai help to get the list of available commands.

@impoiler impoiler self-assigned this Jun 30, 2026
@impoiler
impoiler force-pushed the 06-30-fix_handle_special_characters_in_dashboard_filters branch from 036ab0c to e9f78b8 Compare June 30, 2026 11:55
@impoiler
impoiler force-pushed the 06-30-feat_remove_is_deprecated_models_from_api_responses branch 2 times, most recently from 36b4848 to c1470a8 Compare June 30, 2026 17:10
@impoiler
impoiler force-pushed the 06-30-fix_handle_special_characters_in_dashboard_filters branch from e9f78b8 to 9ff2e78 Compare June 30, 2026 17:10
@impoiler
impoiler force-pushed the 06-30-fix_handle_special_characters_in_dashboard_filters branch from 9ff2e78 to c61b851 Compare July 1, 2026 06:27
@impoiler
impoiler force-pushed the 06-30-feat_remove_is_deprecated_models_from_api_responses branch from c1470a8 to d7ea1a2 Compare July 1, 2026 06:27
@impoiler
impoiler marked this pull request as ready for review July 1, 2026 06:48
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

This is a focused refactor of URL query param parsing in the dashboard page. The core filter-to-URL and URL-to-filter round-trip logic is functionally equivalent for all values writable through the UI, and all data-testid attributes are preserved.

The MCP tool/server label memos silently changed semantics from comma-splitting to whole-string wrapping, which would misfire on any saved URL carrying multiple comma-separated MCP filter values. This cannot be triggered via the current UI, but the behavioral difference exists and old bookmarked multi-value MCP URLs would silently produce wrong filter output.

ui/app/workspace/dashboard/page.tsx — specifically the selectedMcpToolNames/selectedMcpServerLabels memos and the setFilters callback

Important Files Changed

Filename Overview
ui/app/workspace/dashboard/page.tsx Array filter params migrated from CSV strings to typed arrays via parseAsSafeArrayOf; missing_cost_only uses parseAsBoolean; period override bug in setFilters inadvertently fixed; mcp_tool_names/mcp_server_labels memo semantics changed (wrapping vs splitting)

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    URL["URL Query String"] --> nuqs["nuqs useQueryStates"]

    subgraph "Array params (new)"
        nuqs -->|"parseAsSafeArrayOf\n(parseAsArrayOf + encodeURIComponent)"| arrState["urlState.providers[]\nurlState.models[]\nurlState.virtual_key_ids[]\n... (11 more)"]
    end

    subgraph "Boolean param (new)"
        nuqs -->|"parseAsBoolean"| boolState["urlState.missing_cost_only\n(native boolean)"]
    end

    subgraph "String params (unchanged)"
        nuqs -->|"parseAsString"| strState["urlState.mcp_tool_names\nurlState.mcp_server_labels"]
        strState -->|"wrap in array via useMemo"| mcpArr["selectedMcpToolNames[]\nselectedMcpServerLabels[]"]
    end

    arrState --> filtersMemo["filters useMemo\n(LogFilters)"]
    boolState --> filtersMemo
    mcpArr --> mcpFiltersMemo["mcpFilters useMemo\n(MCPToolLogFilters)"]
    arrState -->|"status, virtual_key_ids"| mcpFiltersMemo

    filtersMemo --> LogsFilterSidebar
    filtersMemo --> TabViews
    mcpFiltersMemo --> MCPTabView

    LogsFilterSidebar -->|"setFilters callback\n(arrays written directly)"| nuqs
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    URL["URL Query String"] --> nuqs["nuqs useQueryStates"]

    subgraph "Array params (new)"
        nuqs -->|"parseAsSafeArrayOf\n(parseAsArrayOf + encodeURIComponent)"| arrState["urlState.providers[]\nurlState.models[]\nurlState.virtual_key_ids[]\n... (11 more)"]
    end

    subgraph "Boolean param (new)"
        nuqs -->|"parseAsBoolean"| boolState["urlState.missing_cost_only\n(native boolean)"]
    end

    subgraph "String params (unchanged)"
        nuqs -->|"parseAsString"| strState["urlState.mcp_tool_names\nurlState.mcp_server_labels"]
        strState -->|"wrap in array via useMemo"| mcpArr["selectedMcpToolNames[]\nselectedMcpServerLabels[]"]
    end

    arrState --> filtersMemo["filters useMemo\n(LogFilters)"]
    boolState --> filtersMemo
    mcpArr --> mcpFiltersMemo["mcpFilters useMemo\n(MCPToolLogFilters)"]
    arrState -->|"status, virtual_key_ids"| mcpFiltersMemo

    filtersMemo --> LogsFilterSidebar
    filtersMemo --> TabViews
    mcpFiltersMemo --> MCPTabView

    LogsFilterSidebar -->|"setFilters callback\n(arrays written directly)"| nuqs
Loading

Reviews (1): Last reviewed commit: "fix: handle special characters in dashbo..." | Re-trigger Greptile

Comment thread ui/app/workspace/dashboard/page.tsx
Comment thread ui/app/workspace/dashboard/page.tsx
@impoiler
impoiler changed the base branch from 06-30-feat_remove_is_deprecated_models_from_api_responses to graphite-base/4796 July 1, 2026 06:57
@impoiler impoiler closed this Jul 1, 2026
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.

1 participant