-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Is your feature request related to a problem? Please describe.
List blocks (ticket-list, order-list, invoice-list, product-list, notification-list) manage filter state locally via useState + Formik. When a user applies filters, the URL does not update — meaning filtered views cannot be shared, bookmarked, or linked to from other parts of the application (e.g. clickable status boxes navigating to a pre-filtered list).
Describe the solution you'd like
Implement bidirectional synchronization between filter state and URL query parameters across all list blocks.
Key requirements:
- New
useUrlFiltershook inpackages/ui/src/hooks/that replacesuseState(initialFilters)in list blocks - Serialization utilities for converting filter state to/from URL query params
- Namespace-per-block prefixing (
{ns}_key=value) to avoid collisions when multiple blocks appear on the same page - Repeated keys for multi-select values (e.g.
status=open&status=pending) - Pagination as
{ns}_page=N(1-based, human-readable) - View mode in URL (
{ns}_view=grid), omitted when default (list) - Only non-default filter values written to URL (clean URLs)
router.replacefor filter changes (no browser history pollution)- Dependency injection pattern — hook accepts
searchParamsandonUrlChangeas arguments (no directnext/navigationimports), making it framework-agnostic
Blocks to migrate:
ticket-list(proof of concept)order-listinvoice-listproduct-listnotification-list
Benefits:
- Shareable/bookmarkable filtered views
- Clickable UI elements (e.g. status summary boxes) can navigate to pre-filtered lists
- Backward compatible — blocks without the hook continue working as before
Additional context (if applicable)
Implementation approach
New hook: useUrlFilters (packages/ui/src/hooks/use-url-filters.ts)
Replaces useState(initialFilters) in list blocks. Uses dependency injection — accepts searchParams and onUrlChange as arguments instead of importing from next/navigation directly:
interface UseUrlFiltersOptions<TFilters extends Record<string, string | number | string[]>> {
initialFilters: TFilters;
namespace?: string; // prefix for URL params, e.g. 'ticket'
excludeKeys?: (keyof TFilters)[];
searchParams: URLSearchParams;
onUrlChange: (params: string) => void;
}Serialization rules:
- Simple values:
ticket_status=open - Multi-value:
ticket_status=open&ticket_status=pending(repeated keys) - Pagination:
ticket_page=2(1-based, derived from offset/limit) - View mode:
ticket_view=grid(omitted when defaultlist) - Only non-default values written to URL
New utility functions (packages/ui/src/hooks/use-url-filters.utils.ts):
serializeFiltersToParams()— filter state → URL query stringdeserializeParamsToFilters()— URL query string → filter statefiltersToPage()/pageToOffset()— pagination helpers
Files to create
packages/ui/src/hooks/use-url-filters.ts— the hookpackages/ui/src/hooks/use-url-filters.utils.ts— serialization/deserialization utilitiespackages/ui/src/hooks/use-url-filters.test.ts— unit tests
Files to modify
packages/blocks/ticket-list/src/frontend/TicketList.client.tsxpackages/blocks/order-list/src/frontend/OrderList.client.tsxpackages/blocks/invoice-list/src/frontend/InvoiceList.client.tsxpackages/blocks/product-list/src/frontend/ProductList.client.tsxpackages/blocks/notification-list/src/frontend/NotificationList.client.tsx
Verification checklist
- Filter change updates URL query params
- Pasting URL with filters applies them on mount
- Reset clears URL params
- Pagination reflected in URL (
?ticket_page=2) - View mode reflected in URL (
?ticket_view=grid) - Two blocks on the same page use separate namespaces without collisions
This repo is using Opire - what does it mean? 👇
💵 Everyone can add rewards for this issue commenting
/reward 100 (replace 100 with the amount).🕵️♂️ If someone starts working on this issue to earn the rewards, they can comment
/try to let everyone know!🙌 And when they open the PR, they can comment
/claim #764 either in the PR description or in a PR's comment.🪙 Also, everyone can tip any user commenting
/tip 20 @michnowak (replace 20 with the amount, and @michnowak with the user to tip).📖 If you want to learn more, check out our documentation.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status