Frontend: unify loading and market-data-down banners (trade reference; opt-in E2E) #215
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#215
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Unify loading and market-data-down (indexer outage) UX across the frontend so retail surfaces share one vocabulary, component pattern, and test strategy. Today
/tradeis the reference implementation (#164, #165, #174, #180); other routes still use ad-hoc skeletons, dev-facing “Indexer unavailable” blocks withVITE_INDEXER_URL, or no outage affordance at all. E2E for trade outage remains opt-in (E2E_INDEXER_OUTAGE=1), so CI does not guard cross-page consistency.Tracked in gap analysis:
gaps/GAP_1780023683.md§4 (Loading / indexer down — Partial).Current codebase
Trade route (reference — do not regress)
TradePage.tsxtrade-indexer-outage-bannerwhendetectTradeIndexerOutage(indexerPairQuery, tradesQuery);TradePairSwitchStatuswhile workspace queries fetch; workspace skeleton while factory pairs loadtradeIndexerOutage.tsisIndexerUnavailableErrorindexerErrors.tsindexerTradeOutageCopy.tsTradePairSwitchStatus.tsxtrade-pair-switch-loading— polite loading during pair switchTradeMarketDataUnavailableNotice.tsxtrade-book-unavailable-*,trade-tape-unavailable,trade-chart-unavailableOrderBookPanel.tsx,TradeRecentTradesSection.tsx,PriceChart.tsxtradePairWorkspaceFetching.tsdocs/frontend.md§ Trade page — indexer outage bannerE2E:
e2e/trade-indexer-outage.spec.ts— skipped unlessE2E_INDEXER_OUTAGE=1(indexer stopped manually). Not in default CI.Unit:
TradePage.test.tsx,indexerTradeOutageCopy.test.ts,tradeIndexerOutage.test.ts,PriceChart.test.tsx.Elsewhere (inconsistent)
ChartsPage.tsxloading, skeletonsINDEXER_URL/VITE_INDEXER_URL— violates #174ChartsPage.test.tsxasserts env URL text; integration test expects no indexer-down bannerTraderPage.tsxPoolPage.tsxRetryErrorembedsINDEXER_URLin messageProtocolPage.tsx/limits— outage behavior unclear vs/tradeLcdConnectivityBanner.tsxClassification helpers (existing)
isIndexerUnavailableError/isIndexerPairNotFoundError— reuse everywhere; do not treat 404 as outage.isLcdConnectivityError— wallet/chain path only; do not merge with indexer outage UI.Why this is needed
/tradehas explicittrade-pair-switch-loading(#180); other pages use unrelated skeleton/spinner patterns with no sharedrole="status"/aria-livecontract.gaps/GAP_1780023683.md§5.2).Constraints and guardrails
RetryError/PairNotFoundLinkNotice, not global outage banner (#177).LcdConnectivityBanner/LcdQueryGateseparate; do not label chain RPC failures as “market data service.”VITE_INDEXER_URL, hostnames, or “indexer” in user-visible strings on production routes (#174). Dev-only diagnostics belong behindimport.meta.env.DEVor docs, not primary banners.placeholderData: keepPreviousDataon pair-keyed indexer reads (#180)..cursor/rules/playwright-workers.mdc.Relevant files
Implement / extend
frontend-dapp/src/utils/indexerTradeOutageCopy.ts(generalize or add sibling e.g.marketDataServiceCopy.ts)frontend-dapp/src/utils/indexerErrors.ts,frontend-dapp/src/utils/tradeIndexerOutage.ts(possibly rename/generalize detection)MarketDataServiceBanner.tsx,MarketDataLoadingStatus.tsx(names TBD)frontend-dapp/src/components/trade/TradeMarketDataUnavailableNotice.tsx(promote tocomponents/commonif reused)frontend-dapp/src/pages/ChartsPage.tsx,TraderPage.tsx,PoolPage.tsx,ProtocolPage.tsx,LimitOrdersPage.tsx(audit)frontend-dapp/e2e/trade-indexer-outage.spec.ts(+ new specs or shared helpers)docs/frontend.md,skills/AGENTS_FRONTEND_USER_ERRORS.md,skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.mdTests to update
frontend-dapp/src/pages/ChartsPage.test.tsx(today expectsVITE_INDEXER_URLin banner)frontend-dapp/src/pages/ChartsPage.integration.test.tsxfrontend-dapp/src/pages/TradePage.test.tsx(regression anchor)Recommended direction
Extract shared primitives
MarketDataServiceOutageBanner— props:title,lead,tail?,onRetry?,data-testid(trade keepstrade-indexer-outage-banner).MarketDataLoadingStatus— unifyTradePairSwitchStatusstyling/ARIA for reuse (pair switch, charts pair select, pool list refresh).Page rollout (phased)
isIndexerUnavailableError; adddata-testidhooks.INDEXER_URLfromRetryErroruser strings./limits— align book/chart outage with trade when shared components mount indexer reads.Detection
isIndexerUnavailableErrorat query boundaries overpairsQuery.isErroralone (Charts today treats any pairs error as “indexer unavailable,” including logical errors).E2E strategy (pick one, document in
docs/testing.md)E2E_INDEXER_OUTAGE=1but add Vitest coverage for Charts/Trader banners (mocked transport errors).frontend-e2e-indexer-outagethat starts stack, stops indexer, runs outage specs only.charts-indexer-outage.spec.tsfor at least one non-trade route.Docs/skills: One section in
docs/frontend.md— “Market data loading & outage (global)” — link trade-specific subsections.Related closed work (do not reopen semantics): #164, #165, #174, #180.
Acceptance criteria
data-testids listed in existing tests.VITE_INDEXER_URL/INDEXER_URLin retail DOM.isIndexerUnavailableErrordistinguishes outage from 404 / logical errors on updated pages.docs/frontend.md+ relevantskills/AGENTS_*updated.Test plan (functional paths)
TradePage.test.tsxgetPair502trade-indexer-outage-banner+ panel testids; no env URLsgetTrades502trade-tape-unavailable; book/chart panels per #165terra1…trade-pair-switch-loadingthen clearsChartsPage.test.tsxVITE_INDEXER_URLtextChartsPage.integration.test.tsxgetTrader502RetryError“not found”, not outage bannergetPairs502lcd-connectivity-banneronlyE2E_INDEXER_OUTAGE=1trade-indexer-outage.spec.ts/chartsRun:
cd frontend-dapp && npm run test:run; integration:npm run test:integration(charts); E2E: document env indocs/testing.md.Test plan (attack vectors / misuse)
VITE_INDEXER_URL,127.0.0.1, raw indexer host in retail nodes (#174)keepPreviousData(#180)detectTradeIndexerOutage; panels degradedgetErrorMessage/ humanize path does not render raw HTML in bannersVerification criteria
npm run test:rungreen infrontend-dapp.npm run test:integrationgreen when indexer + postgres available (charts job).make qa-up→ open/trade,/charts,/trader/<addr>— loading states visible during refresh; stop indexer → retail banners on trade + at least Charts/Trader; no env URLs in UI.VITE_LCD_URL) →lcd-connectivity-banneronly; wording distinct from market data.E2E_INDEXER_OUTAGE=1 npx playwright test e2e/trade-indexer-outage.spec.tspasses with indexer stopped.rg 'VITE_INDEXER_URL|Indexer unavailable' frontend-dapp/src/pages --glob '*.tsx'returns no matches in retail banner branches (DEV-only blocks exempt if explicitly scoped).Out of scope (follow-ups)
marked as related to #164
marked as related to #165
marked as related to #174
marked as related to #180
mentioned in commit
e4f8aaea2dImplementation summary (merged to
main@e4f8aae)Unified market-data service loading/outage UX across retail routes per #215, using
/tradeas the regression anchor.What changed
MarketDataServiceOutageBanner,MarketDataLoadingStatus,detectMarketDataOutage,marketDataServiceCopy.ts(trade panel copy remains inindexerTradeOutageCopy.ts).charts-market-data-outage-banner,trader-market-data-outage-banner, etc.) withisIndexerUnavailableError— noVITE_INDEXER_URL/ hostnames in DOM.trade-indexer-outage-banner,trade-pair-switch-loading, and existing trade panel testids/copy unchanged in behavior.npm run test:rungreen (642 tests). Opt-in E2E:e2e/charts-indexer-outage.spec.tsalongside existingtrade-indexer-outage.spec.ts(E2E_INDEXER_OUTAGE=1).skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md, gap row ingaps/GAP_1780023683.md.Verification checklist
cd frontend-dapp && npm run test:run— all greenmake qa-up→/trade,/charts,/trader/<addr>,/pool,/protocol— loading states during refresh; no env URLs in outage bannerslcd-connectivity-bannerwording distinct from market-data bannerE2E_INDEXER_OUTAGE=1 npx playwright test e2e/trade-indexer-outage.spec.ts e2e/charts-indexer-outage.spec.ts(indexer stopped)@brouie — please verify the checklist above on your environment when convenient. Leaving this issue open until sign-off.
mentioned in issue #218
marked as related to #218
marked as related to #219
mentioned in issue #219
Verification complete (issue #215)
Verified on
main@13fef65using worktree branchverify/issue-215(no code changes required — implementation already merged).What was checked
MarketDataServiceOutageBanner,MarketDataLoadingStatus,marketDataServiceCopy.ts,marketDataOutage.ts/detectMarketDataOutage; trade retainstrade-indexer-outage-banner,trade-pair-switch-loading, and paneltrade-*-unavailabletestids.frontend-dapp/src/pages/*.tsxshows noIndexer unavailable/VITE_INDEXER_URLin retail banner branches.docs/frontend.md§ Market data loading & outage (global),skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md, cross-links in trade/limits sections andAGENTS_E2E_INDEXER_OUTAGE.md.Automated results
cd frontend-dapp && npm run test:runChartsPage.integration.test.tsx(withinnpm run test:integration)VITE_INDEXER_URL/Indexer unavailablein page TSX banners)E2E_INDEXER_OUTAGE=1, indexer stopped)Note: Full
npm run test:integrationalso runs GitLab #166 limit-order pool-ref tests; 2 failures here (resolvePairDecimalsForLimitPriceRefFromChain→null) are unrelated to #215 (fixture/LCD pair env).limits-indexer-outage.spec.tstimed out when reusing a dev server withoutVITE_PLAYWRIGHT_E2E(risk modal); tracked under #218 — not a #215 regression.Manual (LocalTerra + indexer stopped/started)
/chartswith indexer down:charts-market-data-outage-bannervisible, retail copy, no env URLs in DOM./trade/<pair>healthy with indexer up: no outage banner; chart/book load.Checklist for follow-up QA
make test-e2e-indexer-outage(CI jobfrontend-e2e-indexer-outage) on a clean deploy/trader/<addr>with indexer stopped —trader-market-data-outage-bannerlcd-connectivity-banner(distinct copy from market data)make test-charts-integrationif limit-order integration env is configuredClosing as all #215 acceptance and verification criteria are satisfied on
main.mentioned in issue #547
mentioned in issue #548
mentioned in issue #550
mentioned in issue #571
mentioned in issue #572
mentioned in issue #576
mentioned in issue #653
mentioned in issue #657
marked as related to #657
mentioned in issue #665
mentioned in issue #666
marked as related to #666
mentioned in issue #680