Fix UX audit gaps: tablet nav, footer, wallet connect, legacy demotion.
Close the 1024–1279px nav dead zone, align ops/footer labels, split homepage quick links, route successful wallet connect to /wallet with inline errors, add WETH to ops sub-nav, and demote legacy SPA with noindex plus banner. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta http-equiv="refresh" content="0; url=/">
|
||||
<link rel="canonical" href="/">
|
||||
<title>DBIS Explorer</title>
|
||||
@@ -32,7 +33,7 @@
|
||||
<h1>DBIS Explorer</h1>
|
||||
<p>Redirecting to the current Chain 138 explorer…</p>
|
||||
<p><a href="/">Continue to explorer</a></p>
|
||||
<p><a href="/legacy/index.html">Open legacy SPA fallback</a></p>
|
||||
<p class="legacy-fallback"><small>Legacy SPA fallback (deprecated): <a href="/legacy/index.html">/legacy/</a></small></p>
|
||||
</main>
|
||||
<script>
|
||||
if (window.location.pathname === '/index.html') {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
@@ -1217,6 +1218,9 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="legacy-deprecation-banner" role="status" style="position:sticky;top:0;z-index:9999;padding:0.75rem 1rem;background:#7c2d12;color:#ffedd5;font:500 0.875rem/1.4 system-ui,sans-serif;text-align:center;border-bottom:1px solid #ea580c;">
|
||||
Deprecated legacy explorer UI. <a href="/" style="color:#fdba74;text-decoration:underline;">Switch to the current DBIS Explorer</a>.
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function doToggleNav() {
|
||||
|
||||
@@ -35,12 +35,22 @@ export default function Footer() {
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li><Link className={footerLinkClass} href="/search">Search</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/docs">Documentation</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/bridge">Bridge Monitoring</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/liquidity">Liquidity Access</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/routes">Routes</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/operations">Operations Hub</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/blocks">Blocks</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/transactions">Transactions</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/tokens">Tokens</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/addresses">Addresses</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/watchlist">Watchlist</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/access">Account access</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/wallet">Wallet tools</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/operations">Operations hub</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/bridge">Bridge</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/routes">Routes</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/liquidity">Liquidity</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/pools">Pools</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/analytics">Analytics</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/operator">Operator</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/system">System</Link></li>
|
||||
<li><Link className={footerLinkClass} href="/weth">WETH</Link></li>
|
||||
<li><a className={footerLinkClass} href="/privacy.html">Privacy Policy</a></li>
|
||||
<li><a className={footerLinkClass} href="/terms.html">Terms of Service</a></li>
|
||||
<li><a className={footerLinkClass} href="/acknowledgments.html">Acknowledgments</a></li>
|
||||
|
||||
@@ -364,6 +364,7 @@ function UiModeToggle({ mobile = false }: { mobile?: boolean }) {
|
||||
function AccountButton({
|
||||
walletSession,
|
||||
connectingWallet,
|
||||
connectError,
|
||||
onConnect,
|
||||
onCopyAddress,
|
||||
onSwitchWallet,
|
||||
@@ -371,6 +372,7 @@ function AccountButton({
|
||||
}: {
|
||||
walletSession: WalletAccessSession | null
|
||||
connectingWallet: boolean
|
||||
connectError?: string | null
|
||||
onConnect: () => void
|
||||
onCopyAddress: () => void
|
||||
onSwitchWallet: () => void
|
||||
@@ -385,7 +387,7 @@ function AccountButton({
|
||||
},
|
||||
{
|
||||
href: '/wallet',
|
||||
label: 'Settings',
|
||||
label: 'Wallet tools',
|
||||
description: 'Review network, token-list, and wallet configuration guidance.',
|
||||
},
|
||||
{
|
||||
@@ -407,14 +409,21 @@ function AccountButton({
|
||||
|
||||
if (!walletSession) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onConnect}
|
||||
className="inline-flex items-center gap-2 rounded-2xl bg-gray-950 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-black focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:bg-white dark:text-gray-950 dark:hover:bg-gray-100 dark:focus-visible:ring-offset-gray-900"
|
||||
>
|
||||
<span className="inline-flex h-2.5 w-2.5 rounded-full bg-emerald-400" aria-hidden />
|
||||
<span>{connectingWallet ? 'Connecting…' : 'Connect Wallet'}</span>
|
||||
</button>
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onConnect}
|
||||
className="inline-flex items-center gap-2 rounded-2xl bg-gray-950 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-black focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 dark:bg-white dark:text-gray-950 dark:hover:bg-gray-100 dark:focus-visible:ring-offset-gray-900"
|
||||
>
|
||||
<span className="inline-flex h-2.5 w-2.5 rounded-full bg-emerald-400" aria-hidden />
|
||||
<span>{connectingWallet ? 'Connecting…' : 'Connect Wallet'}</span>
|
||||
</button>
|
||||
{connectError ? (
|
||||
<p role="alert" className="max-w-xs text-right text-xs text-red-600 dark:text-red-400">
|
||||
{connectError}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -469,6 +478,7 @@ export default function Navbar() {
|
||||
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false)
|
||||
const [walletSession, setWalletSession] = useState<WalletAccessSession | null>(null)
|
||||
const [connectingWallet, setConnectingWallet] = useState(false)
|
||||
const [walletConnectError, setWalletConnectError] = useState<string | null>(null)
|
||||
const mobilePanelId = useId()
|
||||
|
||||
const isExploreActive =
|
||||
@@ -528,13 +538,14 @@ export default function Navbar() {
|
||||
const handleConnectWallet = async () => {
|
||||
try {
|
||||
setConnectingWallet(true)
|
||||
setWalletConnectError(null)
|
||||
await accessApi.connectWalletSession()
|
||||
router.push('/access')
|
||||
setMobileMenuOpen(false)
|
||||
router.push('/wallet')
|
||||
} catch (error) {
|
||||
console.error('Wallet connect failed', error)
|
||||
router.push('/access')
|
||||
setMobileMenuOpen(false)
|
||||
const message = error instanceof Error ? error.message : 'Wallet connection failed.'
|
||||
setWalletConnectError(message)
|
||||
} finally {
|
||||
setConnectingWallet(false)
|
||||
}
|
||||
@@ -588,13 +599,13 @@ export default function Navbar() {
|
||||
)
|
||||
const operationsItems: MenuItem[] = useMemo(
|
||||
() => [
|
||||
{ href: '/operations', label: 'Operations Hub', description: 'Open the consolidated operator surface for live support workflows.' },
|
||||
{ href: '/bridge', label: 'Bridge Monitoring', description: 'Inspect relay lanes, queue posture, and bridge trace tooling.' },
|
||||
{ href: '/operations', label: 'Operations hub', description: 'Open the consolidated operator surface for live support workflows.' },
|
||||
{ href: '/bridge', label: 'Bridge', description: 'Inspect relay lanes, queue posture, and bridge trace tooling.' },
|
||||
{ href: '/routes', label: 'Routes', description: 'Review live route coverage, same-chain lanes, and bridge paths.' },
|
||||
{ href: '/liquidity', label: 'Liquidity', description: 'Check planner-backed route access and live liquidity posture.' },
|
||||
{ href: '/system', label: 'System', description: 'Inspect topology, RPC capability, and public integration inventory.' },
|
||||
{ href: '/operator', label: 'Operator Surface', description: 'Open planner, route, and relay shortcuts in one public page.' },
|
||||
{ href: '/weth', label: 'WETH References', description: 'Review wrapped-asset references and bridge-oriented WETH context.' },
|
||||
{ href: '/operator', label: 'Operator', description: 'Open planner, route, and relay shortcuts in one public page.' },
|
||||
{ href: '/weth', label: 'WETH', description: 'Review wrapped-asset references and bridge-oriented WETH context.' },
|
||||
{ href: '/chain138-command-center.html', label: 'Command Center', description: 'Open the visual command-center reference.', external: true },
|
||||
],
|
||||
[],
|
||||
@@ -727,12 +738,13 @@ export default function Navbar() {
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div className="ml-auto hidden items-center gap-3 lg:flex">
|
||||
<div className="ml-auto hidden items-center gap-3 xl:flex">
|
||||
<SearchControl active={isSearchActive} onSelect={() => setCommandPaletteOpen(true)} />
|
||||
<UiModeToggle />
|
||||
<AccountButton
|
||||
walletSession={walletSession}
|
||||
connectingWallet={connectingWallet}
|
||||
connectError={walletConnectError}
|
||||
onConnect={() => void handleConnectWallet()}
|
||||
onCopyAddress={() => void handleCopyAddress()}
|
||||
onSwitchWallet={() => void handleSwitchWallet()}
|
||||
@@ -740,7 +752,7 @@ export default function Navbar() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="ml-auto flex items-center gap-2 lg:hidden">
|
||||
<div className="ml-auto flex items-center gap-2 xl:hidden">
|
||||
{walletSession ? (
|
||||
<Link
|
||||
href="/access"
|
||||
@@ -793,7 +805,7 @@ export default function Navbar() {
|
||||
{mobileMenuOpen ? (
|
||||
<div
|
||||
id={mobilePanelId}
|
||||
className="border-t border-gray-200 py-4 dark:border-gray-800 lg:hidden"
|
||||
className="border-t border-gray-200 py-4 dark:border-gray-800 xl:hidden"
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<SearchControl
|
||||
@@ -806,6 +818,12 @@ export default function Navbar() {
|
||||
/>
|
||||
<UiModeToggle mobile />
|
||||
|
||||
{walletConnectError ? (
|
||||
<p role="alert" className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300">
|
||||
{walletConnectError}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div className="grid gap-4">
|
||||
<div>
|
||||
<div className="mb-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500 dark:text-gray-400">
|
||||
|
||||
@@ -36,7 +36,10 @@ export default function ContractVerificationCallout({ address, verified }: Contr
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Native Blockscout UI (LAN):</strong> use contract verification on the VM Blockscout instance when the custom explorer UI does not expose the form.
|
||||
<strong>Explorer contract tab:</strong>{' '}
|
||||
<Link href={`/addresses/${address}`} className="text-primary-600 hover:underline">
|
||||
Open this address and review the Contract tab
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
|
||||
@@ -1003,10 +1003,13 @@ export default function Home({
|
||||
Wallet & MetaMask
|
||||
</Link>
|
||||
<Link href="/routes" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Liquidity & routes
|
||||
Routes
|
||||
</Link>
|
||||
<Link href="/liquidity" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Liquidity
|
||||
</Link>
|
||||
<Link href="/bridge" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Bridge monitoring
|
||||
Bridge
|
||||
</Link>
|
||||
<Link href="/analytics" className="rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-primary-600 hover:border-primary-400 dark:border-gray-800">
|
||||
Analytics
|
||||
|
||||
@@ -332,6 +332,11 @@ export const explorerOperationsSurfaces: ExplorerOperationsSurface[] = [
|
||||
label: 'Liquidity',
|
||||
description: 'PMM access points and planner capabilities.',
|
||||
},
|
||||
{
|
||||
href: '/weth',
|
||||
label: 'WETH',
|
||||
description: 'Wrapped-asset references and bridge context.',
|
||||
},
|
||||
{
|
||||
href: '/pools',
|
||||
label: 'Pools',
|
||||
|
||||
@@ -38,17 +38,26 @@ test.describe('Explorer sprint smoke', () => {
|
||||
await expect(page.getByText(/CCIP route catalog/i).first()).toBeVisible({ timeout: 15000 })
|
||||
})
|
||||
|
||||
test('operations hub shows surface navigation', async ({ page }) => {
|
||||
test('operations hub shows WETH in surface navigation', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1280, height: 720 })
|
||||
await page.goto(`${EXPLORER_URL}/operations`, { waitUntil: 'domcontentloaded', timeout: 30000 })
|
||||
await expect(page.getByRole('navigation', { name: /Operations surfaces/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('link', { name: /Bridge/i }).first()).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('navigation', { name: /Operations surfaces/i }).getByRole('link', { name: /^WETH$/i })).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('footer lists public API endpoints', async ({ page }) => {
|
||||
test('footer lists public API endpoints and explorer surfaces', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/`, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
||||
await expect(page.getByRole('contentinfo').getByText(/Public APIs/i)).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('link', { name: /Blockscout stats/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('link', { name: /Wallet tools/i })).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('contentinfo').getByRole('link', { name: /Account access/i })).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('tablet viewport exposes mobile navigation menu', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1100, height: 800 })
|
||||
await page.goto(`${EXPLORER_URL}/`, { waitUntil: 'domcontentloaded', timeout: 20000 })
|
||||
await expect(page.getByRole('button', { name: /Open navigation menu/i })).toBeVisible({ timeout: 10000 })
|
||||
await page.getByRole('button', { name: /Open navigation menu/i }).click()
|
||||
await expect(page.getByRole('link', { name: /Operations hub/i }).first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('analytics page shows track 3 surface note', async ({ page }) => {
|
||||
@@ -63,9 +72,10 @@ test.describe('Explorer sprint smoke', () => {
|
||||
await expect(page.getByText(/Track 4 public surface/i).first()).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
test('legacy SPA fallback loads', async ({ page }) => {
|
||||
test('legacy SPA fallback loads with deprecation banner', async ({ page }) => {
|
||||
await page.goto(`${EXPLORER_URL}/legacy/index.html`, { waitUntil: 'domcontentloaded', timeout: 30000 })
|
||||
await expect(page).toHaveTitle(/DBIS Explorer/i)
|
||||
await expect(page.getByText(/Deprecated legacy explorer UI/i)).toBeVisible({ timeout: 10000 })
|
||||
await expect(page.getByRole('heading', { name: /Latest Blocks/i })).toBeVisible({ timeout: 15000 })
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user