Files
OmniRoute/src/lib/freeProviderRankingsUsage.ts
Dizzle ee4e37154d feat(rankings): show what each provider actually served (#11546)
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip.
- Focused tests, run with the correct vitest config (tests/unit/ui/*.tsx needs `--config vitest.config.ts`, not node:test — my invocation error, not the PR's): free-provider-rankings-page-usage.test.tsx + free-provider-rankings-page-authtype-6915.test.tsx — 9/9 pass; freeProviderRankings-usage-display.test.ts — pass
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: one flagged line in this file (91:5, react-hooks/set-state-in-effect on the mount-time fetchRankings() call) confirmed pre-existing and untouched by this diff — this PR's changes are confined to the fetch body's URL params and the new table column

Thanks for closing a real trust gap — an ELO-only ranking calling a 100%-error provider "healthy" is exactly the kind of thing a reliability column should catch, and the no-data-vs-0% distinction is the right call.
2026-08-25 16:31:11 -03:00

88 lines
2.5 KiB
TypeScript

/**
* freeProviderRankingsUsage.ts — Pure presentation helper for the usage
* reliability shown on the Free Provider Rankings page.
*
* Split out of `freeProviderRankings.ts` for the same reason as
* `freeProviderRankingsAuthType.ts`: that module imports DB-touching code at
* module scope, so a client component can only take types from it. This
* module has zero imports beyond a shared type.
*
* The helper stays locale-free on purpose — it decides *what* is honest to
* show, the component decides how to word it.
*/
import type { ProviderUsage } from "./freeProviderRankings";
export type UsageTone = "good" | "fair" | "poor" | "unknown";
export interface UsageDisplay {
/**
* `none` — the provider served nothing in the window, or usage was not requested.
* `insufficient` — it served too few calls for a rate to mean anything.
* `rate` — a success rate can be stated.
*/
kind: "none" | "insufficient" | "rate";
/** Success rate in percent, rounded; `null` for every kind but `rate`. */
percent: number | null;
requests: number;
successes: number;
windowHours: number;
tone: UsageTone;
}
const EMPTY: UsageDisplay = {
kind: "none",
percent: null,
requests: 0,
successes: 0,
windowHours: 0,
tone: "unknown",
};
/**
* A provider that answers every call with an error must not read as healthy,
* and a provider nobody called must not read as broken. Anything the sample is
* too small to support comes back as `insufficient`, never as a rate — the API
* already refuses to compute one below its own threshold (`successRate: null`).
*/
export function formatUsageReliability(usage?: ProviderUsage): UsageDisplay {
if (!usage) return EMPTY;
const { requests, successes, windowHours, successRate } = usage;
if (successRate === null) {
return {
kind: requests > 0 ? "insufficient" : "none",
percent: null,
requests,
successes,
windowHours,
tone: "unknown",
};
}
const percent = Math.round(successRate * 100);
return {
kind: "rate",
percent,
requests,
successes,
windowHours,
tone: successRate >= 0.95 ? "good" : successRate >= 0.8 ? "fair" : "poor",
};
}
/** Tailwind text colour per tone, mirroring the score colours already on the page. */
export function usageToneClass(tone: UsageTone): string {
switch (tone) {
case "good":
return "text-green-400";
case "fair":
return "text-yellow-400";
case "poor":
return "text-orange-400";
default:
return "text-text-muted";
}
}