feat(quota): sync ?provider= URL param with provider filter for bookmarkable deep links (#4395)
This commit is contained in:
1 parent
92c7bdd5bc
commit
65826d95f2
2 files changed
+77
-1
No files matched your search
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
||||
import { useSearchParams, useRouter, usePathname } from "next/navigation";
|
||||
import ProviderIcon from "@/shared/components/ProviderIcon";
|
||||
import QuotaTable from "./QuotaTable";
|
||||
import Toggle from "@/shared/components/Toggle";
|
||||
@@ -152,6 +153,9 @@ function formatTimeRemaining(value) {
|
||||
|
||||
export default function ProviderLimits() {
|
||||
const { copied, copy } = useCopyToClipboard();
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const [connections, setConnections] = useState([]);
|
||||
const [quotaData, setQuotaData] = useState({});
|
||||
const [loading, setLoading] = useState({});
|
||||
@@ -171,7 +175,26 @@ export default function ProviderLimits() {
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [selectedConnection, setSelectedConnection] = useState(null);
|
||||
const [proxyPools, setProxyPools] = useState([]);
|
||||
const [providerFilter, setProviderFilter] = useState("all");
|
||||
// Initialize providerFilter from URL ?provider= param so the page
|
||||
// can be bookmarked / deep-linked to a specific provider (#4217).
|
||||
const [providerFilter, _setProviderFilter] = useState(
|
||||
() => searchParams?.get("provider") || "all"
|
||||
);
|
||||
// Wrapper: keeps URL in sync with the selected provider so the view can be
|
||||
// bookmarked. Replaces the URL without adding to browser history.
|
||||
const setProviderFilter = useCallback((value) => {
|
||||
_setProviderFilter(value);
|
||||
try {
|
||||
const params = new URLSearchParams(searchParams?.toString() || "");
|
||||
if (value === "all") {
|
||||
params.delete("provider");
|
||||
} else {
|
||||
params.set("provider", value);
|
||||
}
|
||||
const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname;
|
||||
router.replace(newUrl, { scroll: false });
|
||||
} catch { /* non-fatal: URL sync is best-effort */ }
|
||||
}, [pathname, router, searchParams]);
|
||||
const [providerOptions, setProviderOptions] = useState([]);
|
||||
const [accountFilter, setAccountFilter] = useState("all");
|
||||
const [quotaSortMode, setQuotaSortMode] = useState("default");
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Tests for #4217 — Quota Tracker URL ?provider= parameter support.
|
||||
*
|
||||
* Before this fix, visiting /dashboard/quota?provider=codex ignored the query
|
||||
* parameter and always defaulted to "All providers". The dropdown selection
|
||||
* updated only local state and did not update the URL.
|
||||
*
|
||||
* Fix: ProviderLimits initializes providerFilter from useSearchParams and
|
||||
* wraps setProviderFilter to call router.replace when the filter changes.
|
||||
*
|
||||
* Because ProviderLimits is a JSX file we cannot import it in Vitest without
|
||||
* a full Next.js setup. We verify the source text instead.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const src = fs.readFileSync(
|
||||
path.resolve("../src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js"),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
describe("ProviderLimits — URL ?provider= sync (#4217)", () => {
|
||||
it("imports useSearchParams from next/navigation", () => {
|
||||
expect(src).toContain("useSearchParams");
|
||||
expect(src).toContain("next/navigation");
|
||||
});
|
||||
|
||||
it("imports useRouter from next/navigation", () => {
|
||||
expect(src).toContain("useRouter");
|
||||
});
|
||||
|
||||
it("imports usePathname from next/navigation", () => {
|
||||
expect(src).toContain("usePathname");
|
||||
});
|
||||
|
||||
it("initializes providerFilter from searchParams.get('provider')", () => {
|
||||
expect(src).toMatch(/searchParams.*get.*provider/);
|
||||
});
|
||||
|
||||
it("calls router.replace when provider filter changes", () => {
|
||||
expect(src).toContain("router.replace");
|
||||
});
|
||||
|
||||
it("removes ?provider from URL when 'all' is selected", () => {
|
||||
expect(src).toContain("params.delete");
|
||||
});
|
||||
|
||||
it("sets ?provider in URL when a specific provider is selected", () => {
|
||||
expect(src).toContain('params.set("provider"');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user