Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/wiki/progress.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Progress
updated: 2026-08-08
updated: 2026-08-10
type: ledger
---

Expand All @@ -10,6 +10,7 @@ Stage ledger. One row per stage; keep entries short. Verifier = exact fresh comm

| Stage | Status | Verifier | Review | Next |
|---|---|---|---|---|
| Preserve Index DTF section in cmd-k navigation | human-review-required (base ebcd6febe) | RED: proposal/rebalance both landed overview; GREEN 2/2 · gate typecheck/lint/880 unit · live Ctrl+K proposal→governance + rebalance→auctions | independent Intent + Engineering Risk PASS, no findings; shared route-selection behavior requires Engineer review | merge only after Engineer review; wiki-lint blocked by pre-existing stale design-system page |
| vote modal: address-length title overflowed the dialog | done (base 6854a370b) | lint · typecheck · test:run · e2e helper units · smoke 58 · new `vote-modal-long-title` spec desktop+mobile green · RED-verified (reverted the index-dtf modal fix → checkbox right edge 943 vs dialog 849.9) | product/correctness: self — copy + layout only, no tx path touched | — |
| Fix DTF settings confirm button | human-review-required (base 6854a370b) | RED: rounded seeded distribution blocked mandate confirm; GREEN: unit 878 incl. mapper 27/27 · focused E2E 5/5 · typecheck · lint | Dark HOLD on untested mapping → 27 exhaustive mapper tests → Dark PASS; Light PASS; CodeRabbit 2 Minor → resolved (test IDs + editable confirmed state) | PR #1084 open; Engineer review required before merge; wiki-lint blocked by pre-existing stale design-system page |
| vlRSR self-appreciating vaults: drawer shares/redeem + rate line · governance card · portfolio · earn rate-corrected + APY | human-review-required (base d0427a7cf; SDK local-linked) | gate green: typecheck+lint+847 unit · 72 helper · smoke 58 · drawer spec 2/2 · live BSC visual light+dark incl. earn TVL 261.5M RSR = totalAssets | Dark + Light on both diffs; all blockers fixed; details in git | PR #1072 open on SDK 0.5.1 (published, pinned exact; direct sdk dep dropped). Companions: dtf-interface#29, reserve-api#236 (deploy w/ daos CDN purge). **Engineer review**: withdraw→redeem for ALL vaults, governance card, api token.price×rate. [plan](../plans/vlrsr-self-appreciating-vaults.md) |
Expand Down
113 changes: 74 additions & 39 deletions src/components/command-menu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import useIndexDTFList from '@/hooks/useIndexDTFList'
import useTokenList from '@/hooks/useTokenList'
import { getFolioRoute, getTokenRoute } from '@/utils'
import { CHAIN_TAGS } from '@/utils/constants'
import { CHAIN_TAGS, ROUTES } from '@/utils/constants'
import { msg } from '@lingui/core/macro'
import { Trans, useLingui } from '@lingui/react/macro'
import type { MessageDescriptor } from '@lingui/core'
import { atom, useAtom } from 'jotai'
import { Search } from 'lucide-react'
import { useEffect, useMemo, useRef } from 'react'
import Skeleton from 'react-loading-skeleton'
import { Link, useNavigate } from 'react-router-dom'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import ChainLogo from '../icons/ChainLogo'
import TokenLogo from '../token-logo'
import {
Expand Down Expand Up @@ -67,7 +67,28 @@
index: msg`Index DTFs`,
yield: msg`Yield DTFs`,
}
const SECTIONS = ['index', 'yield']
const SECTIONS = ['index', 'yield'] as const
type DTFType = (typeof SECTIONS)[number]

const DTF_ROUTES: Record<DTFType, Set<string>> = {
index: new Set([
ROUTES.OVERVIEW,
ROUTES.MANAGE,
ROUTES.FACTSHEET,
ROUTES.ISSUANCE,
ROUTES.AUCTIONS,
ROUTES.SETTINGS,
ROUTES.GOVERNANCE,
]),
yield: new Set([
ROUTES.OVERVIEW,
ROUTES.ISSUANCE,
ROUTES.STAKING,
ROUTES.AUCTIONS,
ROUTES.SETTINGS,
ROUTES.GOVERNANCE,
]),
}

export const searchMenuOpenAtom = atom(false)

Expand All @@ -82,12 +103,13 @@
const [open, setOpen] = useAtom(searchMenuOpenAtom)
const listRef = useRef<HTMLDivElement | null>(null)
const navigate = useNavigate()
const { pathname } = useLocation()

useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
setOpen((open) => !open)

Check warning on line 112 in src/components/command-menu/index.tsx

View workflow job for this annotation

GitHub Actions / check

react-hooks(exhaustive-deps)

React Hook useEffect has a missing dependency: 'setOpen'
}
}
document.addEventListener('keydown', down)
Expand Down Expand Up @@ -130,43 +152,39 @@
</CommandEmpty>
{SECTIONS.map((section) => (
<CommandGroup key={section} heading={t(SECTION_TITLES[section])}>
{dtfs[section].map((dtf) => (
<Link
key={dtf.address}
to={
section === 'index'
? getFolioRoute(dtf.address, dtf.chain)
: getTokenRoute(dtf.address, dtf.chain)
}
onClick={() => setOpen(false)}
>
<CommandItem
keywords={dtf.keywords}
value={`${dtf.name} ${dtf.symbol}`}
className="gap-3 cursor-pointer"
onSelect={() => {
navigate(
section === 'index'
? getFolioRoute(dtf.address, dtf.chain)
: getTokenRoute(dtf.address, dtf.chain)
)
setOpen(false)
}}
{dtfs[section].map((dtf) => {
const destination = getDTFRoute(pathname, section, dtf)

return (
<Link
key={dtf.address}
to={destination}
onClick={() => setOpen(false)}
>
<div className="relative">
<TokenLogo src={dtf.icon} size="xl" />
<ChainLogo
chain={dtf.chain}
className="absolute -bottom-1 -right-1 w-4 h-2"
/>
</div>
<div className="break-words max-w-[420px]">
<h4 className="font-semibold ">{dtf.name}</h4>
<span className="text-legend">${dtf.symbol}</span>
</div>
</CommandItem>
</Link>
))}
<CommandItem
keywords={dtf.keywords}
value={`${dtf.name} ${dtf.symbol}`}
className="gap-3 cursor-pointer"
onSelect={() => {
navigate(destination)
setOpen(false)
}}
>
<div className="relative">
<TokenLogo src={dtf.icon} size="xl" />
<ChainLogo
chain={dtf.chain}
className="absolute -bottom-1 -right-1 w-4 h-2"
/>
</div>
<div className="break-words max-w-[420px]">
<h4 className="font-semibold ">{dtf.name}</h4>
<span className="text-legend">${dtf.symbol}</span>
</div>
</CommandItem>
</Link>
)
})}
</CommandGroup>
))}
</CommandList>
Expand All @@ -177,3 +195,20 @@
}

export default CommandMenu

const getDTFRoute = (pathname: string, type: DTFType, dtf: DTF) => {
const segments = pathname.split('/').filter(Boolean)
const dtfRootIndex = segments.findIndex(
(segment) => segment === 'index-dtf' || segment === 'token'
)
const currentRoute =
dtfRootIndex === -1 ? undefined : segments[dtfRootIndex + 2]
const route =
currentRoute && DTF_ROUTES[type].has(currentRoute)
? currentRoute
: ROUTES.OVERVIEW

return type === 'index'
? getFolioRoute(dtf.address, dtf.chain, route)
: getTokenRoute(dtf.address, dtf.chain, route)
}
126 changes: 126 additions & 0 deletions src/components/command-menu/tests/navigation.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { render, screen } from '@/test-utils'
import userEvent from '@testing-library/user-event'
import { MemoryRouter, useLocation } from 'react-router-dom'
import { describe, expect, it, vi } from 'vitest'
import CommandMenu from '..'

window.HTMLElement.prototype.scrollIntoView = vi.fn()

vi.mock('@/components/icons/ChainLogo', () => ({
default: () => <span data-testid="chain-logo" />,
}))

vi.mock('@/components/token-logo', () => ({
default: () => <span data-testid="token-logo" />,
}))

vi.mock('@/hooks/useIndexDTFList', () => ({
default: () => ({
data: [
{
address: '0xTarget',
brand: { icon: '' },
chainId: 8453,
name: 'Target DTF',
symbol: 'TARGET',
},
],
}),
}))

vi.mock('@/hooks/useTokenList', () => ({
default: () => ({
list: [
{
id: '0xYieldTarget',
chain: 8453,
logo: '',
name: 'Target Yield DTF',
symbol: 'YIELD',
},
],
}),
}))

const CurrentPath = () => {
const { pathname } = useLocation()
return <output data-testid="current-path">{pathname}</output>
}

describe('CommandMenu DTF navigation', () => {
it.each([
[
'/ethereum/index-dtf/0xcurrent/governance/proposal/42',
'/base/index-dtf/0xtarget/governance',
],
[
'/ethereum/index-dtf/0xcurrent/auctions/rebalance/7',
'/base/index-dtf/0xtarget/auctions',
],
[
'/ethereum/index-dtf/0xcurrent/staking',
'/base/index-dtf/0xtarget/overview',
],
[
'/ethereum/index-dtf/0xcurrent',
'/base/index-dtf/0xtarget/overview',
],
])(
'keeps the top-level DTF section when switching from %s',
async (currentPath, expectedPath) => {
const user = userEvent.setup()

render(
<MemoryRouter initialEntries={[currentPath]}>
<CommandMenu />
<CurrentPath />
</MemoryRouter>
)

await user.click(screen.getByRole('button', { name: 'Search' }))
await user.click(await screen.findByText('Target DTF'))

expect(screen.getByTestId('current-path')).toHaveTextContent(expectedPath)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)

it.each([
[
'/ethereum/index-dtf/0xcurrent/governance/proposal/42',
'Target Yield DTF',
'/base/token/0xyieldtarget/governance',
],
[
'/ethereum/index-dtf/0xcurrent/manage',
'Target Yield DTF',
'/base/token/0xyieldtarget/overview',
],
[
'/ethereum/token/0xcurrent/governance/proposal/42',
'Target DTF',
'/base/index-dtf/0xtarget/governance',
],
[
'/ethereum/token/0xcurrent/staking',
'Target DTF',
'/base/index-dtf/0xtarget/overview',
],
])(
'keeps only target-supported sections when switching from %s',
async (currentPath, targetName, expectedPath) => {
const user = userEvent.setup()

render(
<MemoryRouter initialEntries={[currentPath]}>
<CommandMenu />
<CurrentPath />
</MemoryRouter>
)

await user.click(screen.getByRole('button', { name: 'Search' }))
await user.click(await screen.findByText(targetName))

expect(screen.getByTestId('current-path')).toHaveTextContent(expectedPath)
}
)
})
Loading