mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
* fix(providers): stop quota card re-sorting Codex/GLM bars by remaining % (#6687) QuotaCardExpanded.tsx unconditionally re-sorted quotas by remaining percentage via sortQuotasByRemaining(), discarding the deterministic CODEX_QUOTA_ORDER/GLM_QUOTA_ORDER window order quotaParsing.ts's sortCodexOrder()/sortGlmOrder() had already established. A new hasFixedQuotaOrder() + resolveQuotaDisplayOrder() skip the re-sort for providers with a fixed window order (codex, glm family), threading providerId from QuotaCard.tsx through to the display layer. Regression guard: tests/unit/quota-card-expanded-fixed-order-6687.test.ts * chore(6722): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first)
This commit is contained in:
committed by
GitHub
parent
8338d6a5e7
commit
c9f43bab85
1
changelog.d/fixes/6722-quota-card-fixed-order.md
Normal file
1
changelog.d/fixes/6722-quota-card-fixed-order.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** the provider quota card's weekly/session bars re-sorted by remaining percentage instead of staying in a fixed, deterministic order ([#6687](https://github.com/diegosouzapw/OmniRoute/issues/6687)) — `QuotaCardExpanded.tsx`'s `sortQuotasByRemaining()` (added in #5977) was applied unconditionally via `useMemo(() => sortQuotasByRemaining(quotas), [quotas])`, undoing the deterministic `CODEX_QUOTA_ORDER`/`GLM_QUOTA_ORDER` window order `quotaParsing.ts`'s `sortCodexOrder()`/`sortGlmOrder()` (added in #6336) already established for Codex and the GLM family — since #6336 never touched `QuotaCardExpanded.tsx`, the two orderings never composed, so e.g. a Codex `session` window with less headroom than `weekly` rendered after it instead of staying first. A new `hasFixedQuotaOrder()` (`quotaParsing.ts`) and `resolveQuotaDisplayOrder()` (`QuotaCardExpanded.tsx`) now skip the remaining-% re-sort for providers with a fixed window order, threading `providerId` from `QuotaCard.tsx` through to the display layer; every other provider still gets the remaining-% sort. Regression guard: `tests/unit/quota-card-expanded-fixed-order-6687.test.ts`.
|
||||
@@ -110,6 +110,7 @@ export default function QuotaCard({
|
||||
/>
|
||||
<QuotaCardExpanded
|
||||
quotas={quotas}
|
||||
providerId={connection.provider}
|
||||
loading={loading}
|
||||
error={error}
|
||||
message={quota?.message ?? null}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "../utils";
|
||||
import QuotaMiniBar from "../QuotaMiniBar";
|
||||
import { translateUsageOrFallback, type UsageTranslationValues } from "../i18nFallback";
|
||||
import { hasFixedQuotaOrder } from "../quotaParsing";
|
||||
|
||||
const CURRENCY_SYMBOLS: Record<string, string> = {
|
||||
USD: "$",
|
||||
@@ -31,6 +32,18 @@ export function sortQuotasByRemaining(quotas: any[]): any[] {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure helper — resolves the display order for a provider's quotas.
|
||||
* Providers with a deterministic fixed-window order (codex, glm family — see
|
||||
* quotaParsing.ts's sortCodexOrder()/sortGlmOrder()) keep the order
|
||||
* parseQuotaData() already established. Every other provider still gets the
|
||||
* remaining-percentage sort. Fixes #6687 (bars re-sorted by % undid the fixed
|
||||
* session/weekly order).
|
||||
*/
|
||||
export function resolveQuotaDisplayOrder(providerId: string | undefined, quotas: any[]): any[] {
|
||||
return hasFixedQuotaOrder(providerId) ? [...quotas] : sortQuotasByRemaining(quotas);
|
||||
}
|
||||
|
||||
/** Pure helper — slices the sorted quotas down to the visible window. */
|
||||
export function getVisibleQuotas(sortedQuotas: any[], expanded: boolean): any[] {
|
||||
return expanded ? sortedQuotas : sortedQuotas.slice(0, DEFAULT_VISIBLE_ROWS);
|
||||
@@ -38,6 +51,7 @@ export function getVisibleQuotas(sortedQuotas: any[], expanded: boolean): any[]
|
||||
|
||||
interface Props {
|
||||
quotas: any[];
|
||||
providerId?: string;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
message?: string | null;
|
||||
@@ -155,6 +169,7 @@ function QuotaDetailRow({ q }: { q: any }) {
|
||||
|
||||
export default function QuotaCardExpanded({
|
||||
quotas,
|
||||
providerId,
|
||||
loading,
|
||||
error,
|
||||
message,
|
||||
@@ -174,7 +189,10 @@ export default function QuotaCardExpanded({
|
||||
translateUsageOrFallback(t, key, fallback, values);
|
||||
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const sortedQuotas = useMemo(() => sortQuotasByRemaining(quotas), [quotas]);
|
||||
const sortedQuotas = useMemo(
|
||||
() => resolveQuotaDisplayOrder(providerId, quotas),
|
||||
[quotas, providerId]
|
||||
);
|
||||
const visibleQuotas = useMemo(
|
||||
() => getVisibleQuotas(sortedQuotas, expanded),
|
||||
[sortedQuotas, expanded]
|
||||
|
||||
@@ -9,6 +9,17 @@ const CODEX_QUOTA_ORDER: Record<string, number> = {
|
||||
gpt_5_3_codex_spark_weekly: 3,
|
||||
banked_reset_credits: 4,
|
||||
};
|
||||
const GLM_FAMILY_PROVIDERS = ["glm", "glm-cn", "glmt", "opencode-go"];
|
||||
|
||||
/**
|
||||
* Providers whose quotas already get a deterministic fixed-window order from
|
||||
* sortGlmOrder()/sortCodexOrder() below. Display layers (e.g. QuotaCardExpanded)
|
||||
* must not re-sort these by remaining percentage, or they undo this order (#6687).
|
||||
*/
|
||||
export function hasFixedQuotaOrder(providerId: string | undefined): boolean {
|
||||
const id = String(providerId || "").toLowerCase();
|
||||
return id === "codex" || GLM_FAMILY_PROVIDERS.includes(id);
|
||||
}
|
||||
|
||||
function quotaEntries(data: any): Array<[string, any]> {
|
||||
return data?.quotas && typeof data.quotas === "object" ? Object.entries(data.quotas) : [];
|
||||
@@ -190,7 +201,7 @@ function sortProviderModelOrder(provider: string, quotas: any[]) {
|
||||
}
|
||||
|
||||
function sortGlmOrder(providerId: string, quotas: any[]) {
|
||||
if (!["glm", "glm-cn", "glmt", "opencode-go"].includes(providerId)) return;
|
||||
if (!GLM_FAMILY_PROVIDERS.includes(providerId)) return;
|
||||
quotas.sort((a, b) => (GLM_QUOTA_ORDER[a.name] ?? 99) - (GLM_QUOTA_ORDER[b.name] ?? 99));
|
||||
}
|
||||
|
||||
|
||||
74
tests/unit/quota-card-expanded-fixed-order-6687.test.ts
Normal file
74
tests/unit/quota-card-expanded-fixed-order-6687.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { parseQuotaData } from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/quotaParsing";
|
||||
import { resolveQuotaDisplayOrder } from "@/app/(dashboard)/dashboard/usage/components/ProviderLimits/parts/QuotaCardExpanded";
|
||||
|
||||
interface TestQuota {
|
||||
name: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// Regression for #6687: quotaParsing.ts applies a deterministic CODEX_QUOTA_ORDER
|
||||
// (session:0, weekly:1, ...) via sortCodexOrder(). QuotaCardExpanded.tsx used to
|
||||
// unconditionally re-sort the already-ordered array by remaining percentage via
|
||||
// sortQuotasByRemaining(), discarding the fixed window order. resolveQuotaDisplayOrder()
|
||||
// is the fix: it keeps parseQuotaData()'s order for providers with a fixed window
|
||||
// order (codex, glm family) and only applies the remaining-% sort elsewhere.
|
||||
|
||||
test("#6687 codex: display order stays session-then-weekly regardless of remaining %", () => {
|
||||
const rawCodexData = {
|
||||
quotas: {
|
||||
session: { used: 90, total: 100, remainingPercentage: 10, resetAt: null },
|
||||
weekly: { used: 10, total: 100, remainingPercentage: 90, resetAt: null },
|
||||
},
|
||||
};
|
||||
|
||||
const parsed = parseQuotaData("codex", rawCodexData);
|
||||
// Sanity check: quotaParsing.ts's sortCodexOrder should order session before weekly.
|
||||
assert.deepEqual(
|
||||
parsed.map((q: TestQuota) => q.name),
|
||||
["session", "weekly"]
|
||||
);
|
||||
|
||||
// The display layer must NOT undo the fixed order, even though weekly
|
||||
// (90% remaining) would sort before session (10% remaining) by percentage.
|
||||
const displayed = resolveQuotaDisplayOrder("codex", parsed);
|
||||
assert.deepEqual(
|
||||
displayed.map((q: TestQuota) => q.name),
|
||||
["session", "weekly"]
|
||||
);
|
||||
});
|
||||
|
||||
test("#6687 glm family: display order stays session-then-weekly regardless of remaining %", () => {
|
||||
const rawGlmData = {
|
||||
quotas: {
|
||||
session: { used: 95, total: 100, remainingPercentage: 5, resetAt: null },
|
||||
weekly: { used: 5, total: 100, remainingPercentage: 95, resetAt: null },
|
||||
},
|
||||
};
|
||||
|
||||
const parsed = parseQuotaData("glm", rawGlmData);
|
||||
assert.deepEqual(
|
||||
parsed.map((q: TestQuota) => q.name),
|
||||
["session", "weekly"]
|
||||
);
|
||||
|
||||
const displayed = resolveQuotaDisplayOrder("glm", parsed);
|
||||
assert.deepEqual(
|
||||
displayed.map((q: TestQuota) => q.name),
|
||||
["session", "weekly"]
|
||||
);
|
||||
});
|
||||
|
||||
test("#6687 non-fixed-order providers still sort by remaining percentage descending", () => {
|
||||
const quotas = [
|
||||
{ name: "low", remainingPercentage: 10 },
|
||||
{ name: "high", remainingPercentage: 90 },
|
||||
];
|
||||
|
||||
const displayed = resolveQuotaDisplayOrder("openai", quotas);
|
||||
assert.deepEqual(
|
||||
displayed.map((q: TestQuota) => q.name),
|
||||
["high", "low"]
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user