Skip to content

feat(plans): sync search and status filters to the URL (closes #1086) - #1204

Open
bilhokista wants to merge 1 commit into
Fracverse:masterfrom
bilhokista:feat/1086-plans-url-sync
Open

feat(plans): sync search and status filters to the URL (closes #1086)#1204
bilhokista wants to merge 1 commit into
Fracverse:masterfrom
bilhokista:feat/1086-plans-url-sync

Conversation

@bilhokista

Copy link
Copy Markdown

Closes #1086.

What already existed

The page already held search and statusFilter state and applied both in filtered, so the toolbar was not missing. Two things genuinely were.

No URL sync at all. There was no useSearchParams anywhere in the file, so a filtered view could not be shared or survive a refresh. That is the substance of this issue.

The search matched the wrong fields. It matched p.id and p.token_address. The issue asks for Plan Name and Beneficiary address, neither of which was searched.

Changes

  • Initial search and status read from the query string, so a shared link opens in the same view.
  • Both written back to the URL on change.
  • Search extended to p.title (the Plan Name) and to beneficiary wallet_address, keeping the existing id and token_address matching.
  • filtered moved into a useMemo, since it now walks the beneficiaries array per plan rather than comparing two strings.

Decisions worth reviewing

replace, not push. Typing a search term one character at a time with push would add a history entry per keystroke and effectively break the back button. The URL write is also debounced by 300ms, while the input itself stays immediate — the field does not lag.

Suspense boundary. useSearchParams() opts a route into dynamic rendering, and Next.js fails the build for a statically rendered page that calls it without a boundary. The page body moved into PlansPageContent, wrapped in <Suspense>. This keeps the page building regardless of render mode. If this route is already forced dynamic elsewhere, the wrapper is harmless but can be dropped.

Status matching is case-insensitive on read. ?status=active resolves to the canonical Active from STATUS_FILTERS so the active tab highlights correctly, rather than silently falling back to All.

The first render does not write. Without that guard, mounting would immediately rewrite the URL and strip any other query parameters an inbound link carried.

Beneficiary access is guarded. beneficiaries is typed any[] and is optional, so the lookup uses (p.beneficiaries ?? []).some(...) with optional chaining on wallet_address. A plan with no beneficiaries filters normally instead of throwing.

One mismatch I did not act on

The issue lists status tabs as Active, Inactive, Pending Claim and Disputed. The page's STATUS_FILTERS are All, Active, Pending, Triggered, Completed, and StatusBadge maps those. I kept the existing set rather than renaming statuses, since changing them would touch the badge and likely the backend contract. Happy to follow up if the issue's list is the intended direction.

Verification

Parses clean as TSX via the TypeScript compiler API. I did not run the suite — the repository is ~955 MB and I worked from the individual file rather than a full clone, so please treat CI as the authority.

Behaviour with no query string is unchanged: search starts empty, statusFilter starts All, and nothing is written to the URL until the user interacts.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CrfEY1tvXrbeMDAUzxfuk7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frontend: Build Search and Filter Toolbar for Asset Owner Plans List

1 participant