diff --git a/ui-dashboard/src/app/pool/[poolId]/__tests__/page.test.tsx b/ui-dashboard/src/app/pool/[poolId]/__tests__/page.test.tsx index 6e7a550..a466b5b 100644 --- a/ui-dashboard/src/app/pool/[poolId]/__tests__/page.test.tsx +++ b/ui-dashboard/src/app/pool/[poolId]/__tests__/page.test.tsx @@ -21,7 +21,13 @@ vi.mock("@/components/network-provider", () => ({ hasuraUrl: "https://example.com/v1/graphql", hasuraSecret: "", explorerBaseUrl: "https://celoscan.io", - tokenSymbols: {}, + tokenSymbols: { + "0xt0": "GBPm", + "0xt1": "USDm", + "0xgbp": "GBPm", + "0xusd": "USDm", + "0xeur": "EURm", + }, addressLabels: {}, local: false, hasVirtualPools: false, @@ -156,7 +162,13 @@ describe("Pool detail LPs tab", () => { }); const html = renderToStaticMarkup(); - expect(html).toContain("Net LP Tokens"); + expect(html).toContain("GBPm"); + expect(html).toContain("USDm"); + expect(html).toContain("Total Value"); + expect(html).toContain("Share"); + expect(html).toContain("0.00 GBPm"); + expect(html).toContain("0.00 USDm"); + expect(html).toContain("$0.00"); expect(html).toContain("0xa"); expect(html).toContain("0xb"); expect(html.indexOf("0xa")).toBeLessThan(html.indexOf("0xb")); @@ -165,6 +177,75 @@ describe("Pool detail LPs tab", () => { ); }); + it("hides USD-specific columns when the pool has no USDm side", () => { + mockUseGQL.mockImplementation((query: string | null) => { + if (!query) return gqlResult(undefined); + if (query.includes("PoolDetailWithHealth")) { + return gqlResult({ + Pool: [ + { + ...BASE_POOL, + token0: "0xgbp", + token1: "0xeur", + }, + ], + }); + } + if (query.includes("TradingLimits")) + return gqlResult({ TradingLimit: [] }); + if (query.includes("PoolDeployment")) { + return gqlResult({ FactoryDeployment: [] }); + } + if (query.includes("PoolLpPositions")) { + return gqlResult({ + LiquidityPosition: [ + { id: "1", poolId: "0xpool", address: "0xb", netLiquidity: "100" }, + ], + }); + } + return gqlResult(undefined); + }); + + const html = renderToStaticMarkup(); + expect(html).not.toContain("Total Value"); + expect(html).not.toContain("≈ $"); + }); + + it("hides USD-specific columns when oracle price is missing", () => { + mockUseGQL.mockImplementation((query: string | null) => { + if (!query) return gqlResult(undefined); + if (query.includes("PoolDetailWithHealth")) { + return gqlResult({ + Pool: [ + { + ...BASE_POOL, + token0: "0xgbp", + token1: "0xusd", + oraclePrice: "0", + }, + ], + }); + } + if (query.includes("TradingLimits")) + return gqlResult({ TradingLimit: [] }); + if (query.includes("PoolDeployment")) { + return gqlResult({ FactoryDeployment: [] }); + } + if (query.includes("PoolLpPositions")) { + return gqlResult({ + LiquidityPosition: [ + { id: "1", poolId: "0xpool", address: "0xb", netLiquidity: "100" }, + ], + }); + } + return gqlResult(undefined); + }); + + const html = renderToStaticMarkup(); + expect(html).not.toContain("Total Value"); + expect(html).not.toContain("≈ $"); + }); + it("shows a migration message when LiquidityPosition schema is unavailable", () => { mockUseGQL.mockImplementation((query: string | null) => { if (!query) return gqlResult(undefined); diff --git a/ui-dashboard/src/app/pool/[poolId]/page.test.tsx b/ui-dashboard/src/app/pool/[poolId]/page.test.tsx index 320b1be..fab2ab2 100644 --- a/ui-dashboard/src/app/pool/[poolId]/page.test.tsx +++ b/ui-dashboard/src/app/pool/[poolId]/page.test.tsx @@ -307,6 +307,7 @@ function renderInteractive(params: Record = {}) { describe("Pool detail tab search", () => { it("hydrates swaps search from URL and matches full addresses via labels/raw values", () => { const html = renderWithParams({ + tab: "swaps", swapsQ: "0xsender000000000000000000000000000000000001", }); expect(html).toContain( @@ -317,7 +318,7 @@ describe("Pool detail tab search", () => { }); it("shows swaps no-match state from URL-backed search", () => { - const html = renderWithParams({ swapsQ: "not-found" }); + const html = renderWithParams({ tab: "swaps", swapsQ: "not-found" }); expect(html).toContain("No swaps match your search."); }); @@ -333,21 +334,6 @@ describe("Pool detail tab search", () => { expect(html).toContain("No reserve updates match your search."); }); - it("matches rebalances rows by resolved label", () => { - const html = renderWithParams({ tab: "rebalances", rebalancesQ: "keeper" }); - expect(html).toContain('value="keeper"'); - expect(html).toContain("Keeper Bot"); - expect(html).not.toContain("No rebalances match your search."); - }); - - it("shows rebalances no-match state", () => { - const html = renderWithParams({ - tab: "rebalances", - rebalancesQ: "missing-bot", - }); - expect(html).toContain("No rebalances match your search."); - }); - it("matches liquidity rows by kind or sender label", () => { const html = renderWithParams({ tab: "liquidity", liquidityQ: "lp desk" }); expect(html).toContain('value="lp desk"'); @@ -376,7 +362,7 @@ describe("Pool detail tab search", () => { }); it("preserves newer url params when a debounced search commit fires later", () => { - const container = renderInteractive(); + const container = renderInteractive({ tab: "swaps" }); const input = container.querySelector( 'input[aria-label="Search swaps"]', ) as HTMLInputElement; diff --git a/ui-dashboard/src/app/pool/[poolId]/page.tsx b/ui-dashboard/src/app/pool/[poolId]/page.tsx index 88ae51e..f7ce779 100644 --- a/ui-dashboard/src/app/pool/[poolId]/page.tsx +++ b/ui-dashboard/src/app/pool/[poolId]/page.tsx @@ -78,24 +78,22 @@ export default function PoolDetailPage() { // --------------------------------------------------------------------------- const TABS = [ + "providers", "swaps", "reserves", - "rebalances", "liquidity", "oracle", - "providers", "ols", ] as const; type Tab = (typeof TABS)[number]; type SearchableTab = Extract< Tab, - "swaps" | "reserves" | "rebalances" | "liquidity" | "oracle" + "swaps" | "reserves" | "liquidity" | "oracle" >; const SEARCH_PARAM_BY_TAB: Record = { swaps: "swapsQ", reserves: "reservesQ", - rebalances: "rebalancesQ", liquidity: "liquidityQ", oracle: "oracleQ", }; @@ -104,7 +102,6 @@ function isSearchableTab(tab: Tab): tab is SearchableTab { return ( tab === "swaps" || tab === "reserves" || - tab === "rebalances" || tab === "liquidity" || tab === "oracle" ); @@ -126,7 +123,9 @@ function matchesRowSearch( } function getTabLabel(tab: Tab) { - return tab === "providers" ? "LPs" : tab; + if (tab === "providers") return "LPs"; + if (tab === "ols") return "OLS"; + return tab; } export function getDebtTokenSideLabel( @@ -170,11 +169,10 @@ function PoolDetail() { const decodedId = decodeURIComponent(poolId); const rawTab = searchParams.get("tab"); - const tab: Tab = TABS.includes(rawTab as Tab) ? (rawTab as Tab) : "swaps"; + const requestedTab: Tab = TABS.includes(rawTab as Tab) + ? (rawTab as Tab) + : "providers"; const limit = Number(searchParams.get("limit") ?? "25"); - const activeSearch = isSearchableTab(tab) - ? (searchParams.get(SEARCH_PARAM_BY_TAB[tab]) ?? "") - : ""; const getCurrentParams = useCallback(() => { if (typeof window !== "undefined") { @@ -197,7 +195,7 @@ function PoolDetail() { const setURL = useCallback( (t: Tab, lim: number) => { const p = getCurrentParams(); - if (t !== "swaps") p.set("tab", t); + if (t !== "providers") p.set("tab", t); else p.delete("tab"); if (lim !== 25) p.set("limit", String(lim)); else p.delete("limit"); @@ -251,6 +249,18 @@ function PoolDetail() { }>(POOL_DEPLOYMENT, { poolId: decodedId }); const deployTxHash = deployData?.FactoryDeployment?.[0]?.txHash; + const { data: olsData } = useGQL<{ + OlsPool: OlsPool[]; + }>(OLS_POOL, { poolId: decodedId }); + const hasOlsPool = selectActiveOlsPool(olsData?.OlsPool) !== null; + const visibleTabs = TABS.filter((t) => t !== "ols" || hasOlsPool); + const tab = visibleTabs.includes(requestedTab) + ? requestedTab + : (visibleTabs[0] ?? "providers"); + const activeSearch = isSearchableTab(tab) + ? (searchParams.get(SEARCH_PARAM_BY_TAB[tab]) ?? "") + : ""; + return (