Entity filters

Every entity list includes a collapsible filter form that generates type-aware queries. Filters are stored in the URL, previewed as removable badges, and translated to Prisma where clauses on the backend.

Filter inputs by field type

Field typeFilter inputBackend query
TextText inputcontains (case-insensitive)
IntegerRange input (min / max)gte / lte
DecimalRange input (min / max)gte / lte
DateDate range pickergte / lte
DatetimeDatetime range pickergte / lte
BooleanYes / No / Clear selectExact boolean match
EnumeratorSingle-select dropdownExact value match
Enumerator multipleMulti-select dropdownhasSome (matches any selected value)
TagsMulti-select inputhasSome
Files / ImagesNot filterable
Relationship (one)Autocomplete inputFilter by related entity ID
Relationship (many)Multi autocomplete inputsome with ID in list

Audit filters

Every entity list includes built-in audit filters regardless of field configuration:

  • Created by — member autocomplete
  • Updated by — member autocomplete
  • Created at — datetime range
  • Updated at — datetime range
  • Archived — toggle to show archived records (for archivable entities)

By default, archived records are hidden. Setting the archived filter to "Yes" shows only archived items.

Filter preview

Active filters display as badges above the table. Each badge shows the field label and formatted value. Clicking the X on a badge removes that individual filter without affecting others.

Value formatting adapts to the field type:

  • Range values: shown as > 100 (open-ended) or 100 – 500 (bounded)
  • Date ranges: shown as > 2024-01-01 or 2024-01-01 – 2024-02-01
  • Enumerators: display the translated label, not the raw value
  • Relationships: display the related entity's label
  • Booleans: display "Yes" or "No"

URL state

Filter state is serialized into URL query parameters. This means filtered views are bookmarkable, shareable, and preserved on page refresh. The browser back button restores the previous filter state.

When a filter changes, the page index resets to 0 so results always start from the first page.

Backend processing

The backend receives filters as query parameters and builds a Prisma where clause:

  1. All filter conditions are combined with AND logic
  2. Organization scoping is always applied (via RLS)
  3. Archived records are excluded by default
  4. Each field type maps to the appropriate Prisma operator (contains, gte, lte, hasSome, some, etc.)

The filter schema is validated with Zod before any database query runs, rejecting malformed input.

Disabling filters

You can disable filtering for specific fields or relationships in your entity schema:

  • Fields — set filter: false in the field's properties
  • Relationships — set filterA: false or filterB: false on the relationship

Disabled filters won't appear in the filter form or be accepted by the backend schema.

Key files

FilePurpose
src/features/{entity}/components/{Entity}ListFilter.tsxFilter form with all field-specific inputs
src/shared/components/dataTable/DataTableFilterPreview.tsxBadge display of active filters
src/shared/components/dataTable/dataTableFilterRenders.tsxFormatting functions for filter preview values
src/shared/lib/isFilterEmpty.tsUtility to check if all filter values are empty
backend/src/features/{entity}/{entity}Schemas.tsZod filter input schema
backend/src/features/{entity}/controllers/{entity}FindManyController.tsFilter-to-Prisma query translation