feat(rankings): add 'Configured Only' filter to Free Provider Rankings page (#6245)

feat(rankings): add 'Configured Only' filter to Free Provider Rankings (#6245, closes #6150). 9/9 test green. Thanks @Iammilansoni. Integrated into release/v3.8.45.
This commit is contained in:
Milan Soni
2026-07-05 14:16:17 +05:30
committed by GitHub
parent 0e0ca7e26b
commit f26aa16da3
4 changed files with 172 additions and 7 deletions

View File

@@ -16,6 +16,10 @@
- **refactor(dashboard):** extract the onboarding-wizard "Open provider details" link target into a pure, unit-tested `buildProviderDetailsHref(connection)` helper. The wizard already routes by `connection.id` (the node UUID) rather than the provider category slug (#6144/#6145); this hardens that behavior behind a tested helper that guards a missing id/connection. Regression guard: `tests/unit/provider-onboarding-href.test.ts`. ([#6166](https://github.com/diegosouzapw/OmniRoute/pull/6166) — thanks @KooshaPari)
### ✨ New Features
- **feat(rankings):** add a **'Configured Only'** filter to the Free Provider Rankings page, so the table can be narrowed to just the providers you have configured connections for (with an empty-state hint when none are configured). New `en.json` keys and a pure filter helper covered by `tests/unit/free-provider-rankings-configured-filter.test.ts`. ([#6245](https://github.com/diegosouzapw/OmniRoute/pull/6245), closes [#6150](https://github.com/diegosouzapw/OmniRoute/issues/6150) — thanks @Iammilansoni)
---
## [3.8.43] — 2026-07-02

View File

@@ -60,6 +60,8 @@ export default function FreeProviderRankingsPage() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [filter, setFilter] = useState<string>("");
const [configuredOnly, setConfiguredOnly] = useState(false);
const [configuredProviderIds, setConfiguredProviderIds] = useState<Set<string>>(new Set());
const fetchRankings = useCallback(
async (category?: string) => {
@@ -86,6 +88,28 @@ export default function FreeProviderRankingsPage() {
fetchRankings(filter || undefined);
}, [filter, fetchRankings]);
useEffect(() => {
let active = true;
fetch("/api/providers")
.then((res) => (res.ok ? res.json() : { connections: [] }))
.then((data) => {
if (!active) return;
const ids = new Set<string>();
for (const conn of data.connections || []) {
if (conn?.provider) ids.add(conn.provider);
}
setConfiguredProviderIds(ids);
})
.catch(() => {});
return () => {
active = false;
};
}, []);
const displayedRankings = configuredOnly
? rankings.filter((r) => configuredProviderIds.has(r.id))
: rankings;
return (
<div className="flex flex-col gap-6">
{/* Header */}
@@ -111,6 +135,30 @@ export default function FreeProviderRankingsPage() {
{t(opt.labelKey)}
</button>
))}
<div className="ml-auto flex items-center gap-2">
<label
htmlFor="configured-only-toggle"
className="text-sm text-text-muted select-none cursor-pointer"
title={t("configuredOnlyHint")}
>
{t("configuredOnly")}
</label>
<button
id="configured-only-toggle"
role="switch"
aria-checked={configuredOnly}
onClick={() => setConfiguredOnly(!configuredOnly)}
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 ${
configuredOnly ? "bg-violet-500" : "bg-border"
}`}
>
<span
className={`pointer-events-none block h-4 w-4 rounded-full bg-white shadow-lg transition-transform ${
configuredOnly ? "translate-x-4" : "translate-x-0"
}`}
/>
</button>
</div>
</div>
{error && <div className="p-3 rounded-lg bg-red-500/10 text-red-400 text-sm">{error}</div>}
@@ -122,9 +170,9 @@ export default function FreeProviderRankingsPage() {
) : (
<>
{/* Top 3 Podium */}
{rankings.length >= 3 && (
{displayedRankings.length >= 3 && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{rankings.slice(0, 3).map((provider, idx) => (
{displayedRankings.slice(0, 3).map((provider, idx) => (
<Card key={provider.id} className="relative overflow-hidden">
<div
className={`absolute top-0 left-0 right-0 h-1 ${
@@ -168,7 +216,7 @@ export default function FreeProviderRankingsPage() {
)}
{/* Full List */}
{rankings.length > 0 && (
{displayedRankings.length > 0 && (
<Card>
<div className="overflow-x-auto">
<table className="w-full">
@@ -181,10 +229,11 @@ export default function FreeProviderRankingsPage() {
<th className="pb-3 font-medium text-right">{t("colAvgScore")}</th>
<th className="pb-3 font-medium text-right">{t("colModels")}</th>
<th className="pb-3 font-medium text-right">{t("colType")}</th>
<th className="pb-3 font-medium text-right">{t("colConfigured")}</th>
</tr>
</thead>
<tbody>
{rankings.map((provider, idx) => (
{displayedRankings.map((provider, idx) => (
<tr key={provider.id} className="border-b border-border/50 last:border-b-0">
<td className="py-3 text-text-muted font-mono">{idx + 1}</td>
<td className="py-3">
@@ -229,6 +278,17 @@ export default function FreeProviderRankingsPage() {
{provider.category.toUpperCase()}
</span>
</td>
<td className="py-3 text-right">
{configuredProviderIds.has(provider.id) ? (
<span className="text-xs px-2 py-1 rounded bg-green-500/10 text-green-500">
</span>
) : (
<span className="text-xs px-2 py-1 rounded bg-text-muted/10 text-text-muted">
</span>
)}
</td>
</tr>
))}
</tbody>
@@ -237,9 +297,13 @@ export default function FreeProviderRankingsPage() {
</Card>
)}
{rankings.length === 0 && !error && (
{displayedRankings.length === 0 && !error && (
<Card>
<div className="text-center py-12 text-text-muted">{t("emptyState")}</div>
<div className="text-center py-12 text-text-muted">
{configuredOnly && rankings.length > 0
? t("noConfiguredProviders")
: t("emptyState")}
</div>
</Card>
)}
</>

View File

@@ -9030,7 +9030,11 @@
"colScore": "Score",
"colAvgScore": "Avg Score",
"colModels": "Models",
"colType": "Type"
"colType": "Type",
"configuredOnly": "Configured Only",
"configuredOnlyHint": "Show only providers with active connections",
"noConfiguredProviders": "No configured providers found. Add a provider connection first.",
"colConfigured": "Status"
},
"discovery": {
"title": "Provider Discovery",

View File

@@ -0,0 +1,93 @@
/**
* Unit tests for the "Configured Only" filter on the Free Provider Rankings page.
*
* Phase 1 of #6150 — verifies the toggle state, filtering logic, status column,
* cleanup flag, and i18n keys exist in the source code.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { join } from "node:path";
const root = join(import.meta.dirname, "../..");
const read = (p: string) => readFileSync(join(root, p), "utf8");
const pageSrc = read("src/app/(dashboard)/dashboard/free-provider-rankings/page.tsx");
const en = JSON.parse(read("src/i18n/messages/en.json"));
test("page declares configuredOnly state", () => {
assert.ok(pageSrc.includes("useState(false)"), "configuredOnly defaults to false");
assert.ok(pageSrc.includes("setConfiguredOnly"), "setConfiguredOnly setter exists");
});
test("page declares configuredProviderIds state", () => {
assert.ok(pageSrc.includes("configuredProviderIds"), "configuredProviderIds state exists");
assert.ok(pageSrc.includes("Set<string>"), "configuredProviderIds is typed as Set<string>");
});
test("page fetches /api/providers on mount", () => {
assert.ok(pageSrc.includes('fetch("/api/providers")'), "fetches /api/providers");
assert.ok(pageSrc.includes("conn?.provider"), "uses optional chaining for conn.provider");
});
test("useEffect has cleanup flag to prevent stale state updates", () => {
assert.ok(pageSrc.includes("let active = true"), "declares cleanup flag");
assert.ok(pageSrc.includes("if (!active) return"), "guards state update with active flag");
assert.ok(pageSrc.includes("active = false"), "cleanup function sets active to false");
});
test("displayedRankings filters by configuredProviderIds when toggle is on", () => {
assert.ok(pageSrc.includes("displayedRankings"), "displayedRankings derived variable exists");
assert.ok(
pageSrc.includes("configuredProviderIds.has(r.id)"),
"filters rankings by configuredProviderIds.has(r.id)"
);
assert.ok(
pageSrc.includes("configuredOnly\n ? rankings.filter"),
"conditional: when configuredOnly is true, filters rankings"
);
});
test("toggle switch has accessible attributes", () => {
assert.ok(pageSrc.includes('role="switch"'), "toggle has role=switch");
assert.ok(
pageSrc.includes("aria-checked={configuredOnly}"),
"toggle has aria-checked bound to configuredOnly"
);
assert.ok(
pageSrc.includes('htmlFor="configured-only-toggle"'),
"label is linked to toggle via htmlFor"
);
});
test("table has a 'Configured' status column", () => {
assert.ok(pageSrc.includes('t("colConfigured")'), "table header includes colConfigured key");
assert.ok(
pageSrc.includes("configuredProviderIds.has(provider.id)"),
"status column checks configuredProviderIds"
);
});
test("empty state shows noConfiguredProviders when toggle is on", () => {
assert.ok(
pageSrc.includes('t("noConfiguredProviders")'),
"empty state uses noConfiguredProviders i18n key"
);
assert.ok(
pageSrc.includes("configuredOnly && rankings.length > 0"),
"shows noConfiguredProviders only when toggle is on and data exists"
);
});
test("i18n: en.json has all required filter keys", () => {
const keys = en.freeProviderRankingsPage;
assert.ok(keys, "freeProviderRankingsPage namespace exists in en.json");
assert.equal(typeof keys.configuredOnly, "string", "configuredOnly is a string");
assert.equal(typeof keys.configuredOnlyHint, "string", "configuredOnlyHint is a string");
assert.equal(typeof keys.noConfiguredProviders, "string", "noConfiguredProviders is a string");
assert.equal(typeof keys.colConfigured, "string", "colConfigured is a string");
assert.ok(keys.configuredOnly.length > 0, "configuredOnly is non-empty");
assert.ok(keys.configuredOnlyHint.length > 0, "configuredOnlyHint is non-empty");
assert.ok(keys.noConfiguredProviders.length > 0, "noConfiguredProviders is non-empty");
assert.ok(keys.colConfigured.length > 0, "colConfigured is non-empty");
});