Skip to content

feat(dashboard): new proxies page and nav restructure#1362

Merged
ibigbug merged 4 commits into
masterfrom
feat/proxies-page
May 2, 2026
Merged

feat(dashboard): new proxies page and nav restructure#1362
ibigbug merged 4 commits into
masterfrom
feat/proxies-page

Conversation

@ibigbug
Copy link
Copy Markdown
Member

@ibigbug ibigbug commented May 2, 2026

Changes

New /proxies page (ProxyList.tsx)

Replaces the old /providers page. Shows:

  • Static config proxies in a flat grid with type badge + latency
  • Provider proxies grouped by provider with per-proxy latency test buttons
  • /providers kept as a compatibility alias so existing bookmarks still work

Per-proxy latency testing

  • Static proxies: GET /proxies/{name}/delay
  • Provider proxies: GET /providers/proxies/{provider}/{name}/healthcheck
  • Latency cache keys namespaced as {provider}::{proxy} to avoid collisions when two providers have same-named proxies

Fixed selector proxy jumping glitch

Removed invalidateQueries from onSettled of selectMutation in Proxies.tsx and ProxyGroups.tsx. The optimistic update now sticks without being overwritten by an immediate refetch.

Rule providers moved to Rules page

Rule provider section (with match tester + update button) now lives at the bottom of Rules.tsx instead of the old Providers page. RuleProviderRulesPanel extracted to module level to prevent state loss on re-renders.

Nav updated

New order: Overview / Flows / Connections / Proxies / Rules / DNS / Logs / Settings

Summary by CodeRabbit

  • New Features

    • Added a consolidated Proxy List page combining static and provider proxies with per-proxy latency tests, provider “Test All” (batched) and provider update actions.
    • Rules page now shows Rule Providers with expand/collapse panels, a tester input, and per-provider update.
  • UI Enhancements

    • Updated top-navigation icons and routing so proxies and providers render the consolidated proxy list.
  • Bug Fixes

    • Improved proxy-selection error handling to force proxy list refresh after failures.
  • Chores

    • Added a helper to fetch provider-specific proxy latency.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 027f9306-0172-48b5-8785-e61f78399311

📥 Commits

Reviewing files that changed from the base of the PR and between 90b3d84 and c492aba.

📒 Files selected for processing (2)
  • clash-dashboard/src/pages/ProxyList.tsx
  • clash-dashboard/src/pages/Rules.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • clash-dashboard/src/pages/Rules.tsx
  • clash-dashboard/src/pages/ProxyList.tsx

📝 Walkthrough

Walkthrough

Routing now renders ProxyList for both /proxies and /providers; nav icons and ordering were adjusted; a provider-aware proxy delay API helper (getProviderProxyDelay) was added; proxy-selection mutations now invalidate ['proxies'] on error; a new ProxyList page implements per-proxy/provider latency testing and provider update flows; Rules gained a Rule Providers section with tester and update flows.

Changes

Proxy & Provider UI + Querying

Layer / File(s) Summary
Routing
clash-dashboard/src/App.tsx
Adds Route path="proxies" element={<ProxyList />} and wires /providers to render ProxyList instead of Providers.
Navigation
clash-dashboard/src/components/Layout.tsx
Updates lucide-react imports and reassigns/reorders nav item icons (/flowsGitBranch, /connectionsActivity, /proxiesShield, reorder /logs//dns).
API Additions
clash-dashboard/src/lib/api.ts
Adds exported getProviderProxyDelay(providerName, proxyName, url, timeout) calling /providers/proxies/{providerName}/proxies/{proxyName}/delay.
Page UI Primitives
clash-dashboard/src/pages/ProxyList.tsx
Adds ProxyCard, ProviderSection, helpers and constants (GROUP_TYPES, getLastDelay, getLatencyColor, etc.).
Page Data & Control Flow
clash-dashboard/src/pages/ProxyList.tsx
Introduces ProxyList component: queries ['proxies'] (30s) and ['providers'] (60s), derives staticProxies/providers, maintains latencyMap/testingProxies/testingProviders/updatingProviders, implements per-proxy getProxyDelay and provider getProviderProxyDelay tests, provider "Test All" (batched concurrency of 5) and "Update" (calls updateProxyProvider) handlers, and invalidates providers after provider actions.
Rendering & States
clash-dashboard/src/pages/ProxyList.tsx
Renders loading/error states, "Config Proxies" grid (excluding provider duplicates), provider sections (filtered/sorted), per-proxy latency badges and test buttons, and provider-level controls/disabled states.
Mutation Invalidation
clash-dashboard/src/components/ProxyGroups.tsx, clash-dashboard/src/pages/Proxies.tsx
selectMutation.onError now restores ctx.previous if present and then calls queryClient.invalidateQueries({ queryKey: ['proxies'] }); prior onSettled invalidation removed.

Rules — Rule Providers Section

Layer / File(s) Summary
API & Query Client Use
clash-dashboard/src/pages/Rules.tsx
Imports getRuleProviders, updateRuleProvider, getRuleProviderRules, matchRuleProvider and uses useQueryClient.
Internal Panel
clash-dashboard/src/pages/Rules.tsx
Adds RuleProviderRulesPanel with tester input state; unconditionally queries getRuleProviderRules; conditionally runs matchRuleProvider only when trimmed tester input is non-empty; enumerates rules only when provider behavior === 'classical'.
Page Data & Actions
clash-dashboard/src/pages/Rules.tsx
Rules() now queries rule-providers (60s), tracks expandedRuleProviders and updatingRuleProviders, adds runRuleUpdate to call updateRuleProvider then invalidate rule-providers, filters/sorts providers, and computes behavior-based UI.
Rendering
clash-dashboard/src/pages/Rules.tsx
Renders provider list header, per-provider collapsible rows with metadata, Update button with in-flight state, and expanded RuleProviderRulesPanel content or informational message for non-enumerable behaviors.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as Browser/UI
    participant QueryClient
    participant API as Backend API

    User->>UI: Click "Test Proxy"
    activate UI
    UI->>UI: set testingProxies state, disable button
    UI->>API: getProxyDelay(proxyName, url, timeout)
    activate API
    API-->>UI: { delay: number }
    deactivate API
    UI->>UI: update latencyMap, clear testing state
    UI->>QueryClient: (no query invalidation on success)
    deactivate UI
Loading
sequenceDiagram
    participant User
    participant UI as Browser/UI
    participant QueryClient
    participant API as Backend API

    User->>UI: Click "Test All" for provider
    activate UI
    UI->>UI: set testingProviders state, disable button
    loop per provider proxy (batched)
        UI->>API: getProviderProxyDelay(providerName, proxyName, url, timeout)
        API-->>UI: { delay: number }
        UI->>UI: update latencyMap
    end
    UI->>QueryClient: invalidateQueries(['providers'])
    QueryClient->>API: fetch providers
    API-->>QueryClient: providers data
    UI->>UI: clear testingProviders, re-render
    deactivate UI
Loading
sequenceDiagram
    participant User
    participant UI as Browser/UI
    participant QueryClient
    participant API as Backend API

    User->>UI: Click "Update" on rule provider
    activate UI
    UI->>UI: set updatingRuleProviders state
    UI->>API: updateRuleProvider(providerName)
    API-->>UI: Success
    UI->>QueryClient: invalidateQueries(['rule-providers'])
    QueryClient->>API: fetch rule-providers
    API-->>QueryClient: updated providers
    UI->>UI: clear updating state, re-render
    deactivate UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hop through lists of proxies and names,
I ping the routes and chase their tiny frames;
Providers hum and show their measured beat,
Rules unfold, updates click—so neat.
A twitch, a hop, the dashboard fresh and sweet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(dashboard): new proxies page and nav restructure' accurately captures the main changes: the addition of a new ProxyList page and navigation restructuring with updated icons and routes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/proxies-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 4/8 reviews remaining, refill in 27 minutes and 24 seconds.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clash-dashboard/src/App.tsx`:
- Line 29: Add a compatibility alias route so requests to "/providers" still
resolve to the same component: keep the existing Route with path="proxies" and
add a second Route with path="providers" that also renders <ProxyList /> (i.e.,
add a Route with path="providers" element={<ProxyList />} alongside the Route
path="proxies"). This preserves bookmarks and shared links while rolling out the
new URL.

In `@clash-dashboard/src/pages/ProxyList.tsx`:
- Around line 231-238: The testAllInProvider function double-runs provider
checks: it individually calls testProviderProxy for each proxy and then
immediately calls healthcheckProvider which repeats probes server-side; remove
the redundant call to healthcheckProvider() (or, alternatively, skip per-proxy
testProviderProxy calls and only call healthcheckProvider()) and keep the
existing queryClient.invalidateQueries({ queryKey: ['providers'] }) to refresh
histories—update testAllInProvider to pick one path (recommended: keep per-proxy
testProviderProxy calls and delete the healthcheckProvider() invocation) so no
duplicate /delay requests are sent.
- Around line 157-162: The latency cache is keyed only by proxy.name which
collides across providers; change all reads and writes to use a namespaced key
like `${provider.name}::${proxy.name}` so values are unique per provider. Update
the ProxyCard prop latency (replace latencyMap[proxy.name]) to
latencyMap[`${provider.name}::${proxy.name}`], ensure wherever latency is set
(e.g., in onTestProxy or its helper that updates latencyMap) uses the same
namespaced key, and confirm testingProxies usage remains consistent with the
namespaced key (`testingProxies.has(\`${provider.name}::${proxy.name}\`)`).
Ensure any other lookups/sets of latencyMap or related caches use the same
composite key so cards from different providers don't overwrite each other.

In `@clash-dashboard/src/pages/Rules.tsx`:
- Around line 65-73: The runRuleUpdate function currently swallows errors from
updateRuleProvider; add a catch block to handle failures, call
setUpdatingRuleProviders to clear the loading state in finally (keep existing
finally) and inside the catch use the app's user-feedback mechanism (e.g.,
toast/errorSnackbar or setState error) to surface the error message and
optionally log it; specifically wrap await updateRuleProvider(provider.name) in
try/catch (or add a .catch) and on error call your notification helper with the
error and include provider.name, then still call queryClient.invalidateQueries
on success as now and retain the finally that removes provider.name from the
Set.
- Around line 84-184: The RuleProviderRulesPanel component is defined inside
Rules which causes remounts and lost state; extract RuleProviderRulesPanel to a
top-level component (either above the Rules function or into its own file), keep
its prop signature ({ name, behavior }) and the internal state vars (matchInput,
matchTarget) and useQuery hooks (queryKey ['rule-provider-rules', name],
['rule-provider-match', name, matchTarget]) unchanged, then update the Rules
component to import/use this top-level RuleProviderRulesPanel and pass through
the same name and behavior props; if moved to a separate file, export default or
named-export it and update the import in the file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b1ecded3-cc13-4293-ad36-661724d33519

📥 Commits

Reviewing files that changed from the base of the PR and between 39f86a2 and a66d82d.

📒 Files selected for processing (7)
  • clash-dashboard/src/App.tsx
  • clash-dashboard/src/components/Layout.tsx
  • clash-dashboard/src/components/ProxyGroups.tsx
  • clash-dashboard/src/lib/api.ts
  • clash-dashboard/src/pages/Proxies.tsx
  • clash-dashboard/src/pages/ProxyList.tsx
  • clash-dashboard/src/pages/Rules.tsx

Comment thread clash-dashboard/src/App.tsx
Comment thread clash-dashboard/src/pages/ProxyList.tsx
Comment thread clash-dashboard/src/pages/Rules.tsx
Comment thread clash-dashboard/src/pages/Rules.tsx Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

ibigbug and others added 2 commits May 2, 2026 02:31
- Add new /proxies route (ProxyList.tsx) showing static config proxies
  and provider proxies grouped by provider name
- Add per-proxy latency test button using correct API endpoints:
  /proxies/{name}/delay for static, /providers/proxies/{prov}/proxies/{name}/delay for provider proxies
- Add getProviderProxyDelay() to api.ts
- Fix selector jump glitch in Proxies.tsx and ProxyGroups.tsx:
  remove invalidateQueries from onSettled, keep it only in onError
- Move rule providers section from Providers.tsx to Rules.tsx
- Update nav order: Overview / Flows / Connections / Proxies / Rules / DNS / Logs / Settings
- Remove Providers nav link, add Proxies link pointing to /proxies
- Remove /providers route from App.tsx router config

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add /providers as compatibility alias route (preserves existing bookmarks)
- Namespace latency cache keys as provider::proxy to avoid collisions across
  providers with same-named proxies
- Remove redundant healthcheckProvider() from testAllInProvider (per-proxy
  calls already cover it)
- Add error handling / console.error to runRuleUpdate in Rules.tsx
- Extract RuleProviderRulesPanel to module level to prevent state loss and
  unnecessary unmounts on Rules re-renders

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ibigbug ibigbug force-pushed the feat/proxies-page branch from a66d82d to a30656a Compare May 2, 2026 09:36
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clash-dashboard/src/pages/ProxyList.tsx`:
- Around line 178-188: The proxies/providers fetches lack explicit error
handling; update the useQuery calls for getProxies and getProxyProviders to
destructure isError, error, and refetch (e.g., const { data: proxiesData,
isLoading: proxiesLoading, isError: proxiesError, error: proxiesErrorObj,
refetch: refetchProxies } = useQuery(...)) and in the render paths where
proxiesData/providersData and loading/empty states are handled (including the
provider/proxy list blocks), show a clear error UI when isError is true that
displays the error message (from error or error.message) and a Retry button that
calls refetchProxies/refetchProviders; apply the same pattern to the other
related query usages around the provider rendering logic
(providersData/providersLoading) so API failures render as errors rather than
empty states.

In `@clash-dashboard/src/pages/Rules.tsx`:
- Around line 141-145: The 'rule-providers' useQuery (queryKey
['rule-providers'], queryFn getRuleProviders) doesn't handle failures and lets
the UI fall through to the empty-state; add error handling by leveraging
useQuery's onError and/or reading isError and error from the hook (alongside
ruleIsLoading and ruleData) and render an explicit error state when the query
fails instead of the empty-state; update the rendering logic that currently uses
ruleData/ruleIsLoading to check for ruleIsError/error and show a helpful error
message (or retry action), and apply the same change to the other occurrence of
the 'rule-providers' query referenced elsewhere in this file.
- Around line 302-306: The clickable div that calls
toggleRuleProviderExpanded(provider.name) should be made keyboard-accessible:
replace the div (or modify it) so it is focusable and actionable via keyboard by
using a semantic <button> (or adding role="button" plus tabIndex={0}) and
handling onKeyDown for Enter/Space to call
toggleRuleProviderExpanded(provider.name); also add appropriate ARIA attributes
like aria-expanded and aria-controls tied to provider.name to expose the
expanded/collapsed state to assistive tech. Ensure the element preserves the
existing className and style so layout/appearance remain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e7cccc8-0265-48a3-b602-82e8b9c8eaab

📥 Commits

Reviewing files that changed from the base of the PR and between a66d82d and a30656a.

📒 Files selected for processing (7)
  • clash-dashboard/src/App.tsx
  • clash-dashboard/src/components/Layout.tsx
  • clash-dashboard/src/components/ProxyGroups.tsx
  • clash-dashboard/src/lib/api.ts
  • clash-dashboard/src/pages/Proxies.tsx
  • clash-dashboard/src/pages/ProxyList.tsx
  • clash-dashboard/src/pages/Rules.tsx
✅ Files skipped from review due to trivial changes (1)
  • clash-dashboard/src/lib/api.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • clash-dashboard/src/components/ProxyGroups.tsx
  • clash-dashboard/src/pages/Proxies.tsx

Comment thread clash-dashboard/src/pages/ProxyList.tsx Outdated
Comment thread clash-dashboard/src/pages/Rules.tsx Outdated
Comment thread clash-dashboard/src/pages/Rules.tsx
- Fix latencyMap key bug in testProviderProxy (was using proxy.name
  instead of namespaced key, so latency display was broken)
- Add isError handling for proxies and providers queries with error UI
- Add isError handling for rule-providers query with error UI
- Make rule provider expand/collapse keyboard-accessible (role=button,
  tabIndex, onKeyDown for Enter/Space)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clash-dashboard/src/pages/ProxyList.tsx`:
- Around line 78-86: The icon-only latency button in ProxyList.tsx (the <button>
that calls onTest and renders the Activity component) lacks a reliable
accessible name; add an aria-label to the button (e.g. aria-label="Test latency"
or, if a proxy/name variable is available, aria-label={`Test latency for
${proxy.name}`}) so screen readers can announce its purpose; keep the existing
title and testing/disabled logic intact and ensure the aria-label is updated if
the button context (proxy) changes.
- Around line 231-237: The testAllInProvider function currently calls
testProviderProxy for every proxy concurrently, causing a burst of /delay
requests; modify testAllInProvider to throttle concurrency by processing proxies
in small batches (or using a concurrency limit) instead of mapping all at once.
Implement this by splitting provider.proxies (or iterating) into chunks of a
configurable batch size (e.g., 5–10) and awaiting Promise.all on each chunk
before proceeding to the next, calling testProviderProxy(provider.name, proxy)
for each item; ensure setTestingProviders and the final
queryClient.invalidateQueries(['providers']) behavior remains the same.
- Around line 293-298: Rendering of static ProxyCard instances doesn't pass the
testing state, so the card's button never shows loading; update the render call
for ProxyCard to pass the testing flag from testingProxies for the same key used
by testStaticProxy (testingProxies[`static::${proxy.name}`]) along with the
existing latency prop (latencyMap[`static::${proxy.name}`]). In other words, add
a prop (e.g., isTesting or testing) to the ProxyCard invocation and feed it
testingProxies[`static::${proxy.name}`] so ProxyCard can disable/show loading
during an in-flight /proxies/{name}/delay request; keep the existing key, proxy,
latency, and onTest props unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c0f2141-dc1c-4857-88a4-98f74f5cec6f

📥 Commits

Reviewing files that changed from the base of the PR and between a30656a and 90b3d84.

📒 Files selected for processing (2)
  • clash-dashboard/src/pages/ProxyList.tsx
  • clash-dashboard/src/pages/Rules.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • clash-dashboard/src/pages/Rules.tsx

Comment thread clash-dashboard/src/pages/ProxyList.tsx
Comment thread clash-dashboard/src/pages/ProxyList.tsx
Comment thread clash-dashboard/src/pages/ProxyList.tsx
- Add aria-label to icon-only latency test button for screen reader support
- Wire static proxy cards into testingProxies so loading state shows
  correctly and prevents overlapping requests on repeated clicks
- Throttle 'Test All' to run in batches of 5 instead of all concurrently
  to avoid request bursts on large providers
- Add aria-expanded to rule provider expand/collapse toggle

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ibigbug ibigbug merged commit 6c88775 into master May 2, 2026
33 of 35 checks passed
@ibigbug ibigbug deleted the feat/proxies-page branch May 2, 2026 10:30
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