Hooks

useDocyrusDataViewSelect

Fetch data source fields and saved views from Docyrus and wire them into DataGridViewSelect with a single hook.

Installation

pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-data-view-select
Required Packages(4 packages)
pnpm add @docyrus/app-utils @docyrus/api-client @tanstack/react-query react-querybuilder

This hook is distributed as source. It requires an authenticated RestApiClient from @docyrus/api-client and a QueryClientProvider from @tanstack/react-query somewhere above your component tree.

Overview

useDocyrusDataViewSelect loads a data source's fields and saved data views from the Docyrus backend via @docyrus/app-utils, adapts them to the shapes DataGridViewSelect expects, and returns a ready-to-spread props bag. The hook:

  • Fetches the data source with expand=enums so enum-backed fields (status, enum, select, systemEnum) come back with their options attached and are mapped to FullField.values for the filter editor.
  • Wires create, update, and delete to createDataViewClient with automatic view-list invalidation.
  • Tracks the active view as controlled state and persists the last selection per user, scoped by app + data source (+ app id if present). Defaults to localStorage; activeViewStorage: 'session' scopes it per tab (threaded automatically from persistState by the data-listing hooks).

Usage

'use client';

import { useDocyrusDataViewSelect } from '@/hooks/use-docyrus-data-view-select';
import { DataGridViewSelect } from '@/components/docyrus/data-grid-view-select';
import { useReactTable } from '@tanstack/react-table';

export function ContactsGridHeader({ client, table }) {
  const { gridViewSelectProps, isLoading } = useDocyrusDataViewSelect({
    client,
    appSlug: 'crm',
    dataSourceSlug: 'contacts'
  });

  if (isLoading) return null;

  return (
    <DataGridViewSelect
      table={table}
      variant="horizontal-tabs"
      editable
      {...gridViewSelectProps} />
  );
}

The hook does not accept table — spread the returned props onto DataGridViewSelect and pass table separately so TanStack Table generics are preserved.

API Reference

Parameters

OptionTypeDefaultDescription
clientRestApiClient—Authenticated Docyrus API client.
appSlugstring—Slug of the app the data source belongs to.
dataSourceSlugstring—Slug of the data source.
appIdstring—Optional app id filter for views scoped to a different app than the data source owner.
overrideFieldsFullField[]—Replace the computed query-builder fields entirely.
mapField(field, defaultMapped) => FullField | null—Per-field transform run after default mapping. Return null to drop a field from filter UI.
staleTimenumber30_000TanStack Query staleTime applied to both the data source and views queries.
enabledbooleantrueDisable queries while other state is still loading.
dataSourceDataSource | null—Pre-resolved schema. When provided, the hook skips the getBySlug metadata fetch and reads fields / metadata from this object — the field list, relation targets, and the returned dataSource all come from it. See DB-free metadata.
enableDataViewsbooleantrueFetch and manage saved data views (the /views endpoint). Set false for data sources without a view-configuration backend (e.g. core/tenant system data sources) so the hook never calls /views; the tab strip then shows only systemViews (if any).
dataSourceExpandstring | false'enums'expand query param for the schema fetch (so select/status fields carry option metadata). Pass false/'' to omit expand for backends that don't support it. Ignored when dataSource is injected.
persistActiveViewbooleantruePersist the last-selected view per user. Set to false to disable.
persistKeystringautoOverride the auto-generated storage key (default: docyrus:data-grid-view:<appSlug>:<dataSourceSlug>[:<appId>]).
activeViewStorage'session' | 'local''local'Storage backend for the persisted active-view id. The data-listing hooks (useDocyrusDataGrid / useDocyrusDataTable / useDocyrusKanban) thread their persistState.storage here automatically, so the active view follows the same session/local scope as the rest of the persisted view parameters.
defaultRowGroupingColumnstring—Forwarded to DataGridViewSelect as the default row-grouping column for views that don't already specify a grouping. Pair with useDocyrusDataGrid which actually applies it to the table.

Return Value

PropertyTypeDescription
gridViewSelectPropsPick<DataGridViewSelectProps, ...>Pre-wired props: views, activeViewId, fields, onViewChange, onViewCreate, onViewSave, onViewDelete, disabled.
viewsSavedDataGridView[]Views already mapped from the backend shape.
fieldsFullField[]Fields mapped for react-querybuilder filter editor.
dataSourceDataSource | undefinedThe raw data source metadata response.
activeViewIdstringThe id of the currently active view (empty while loading).
setActiveViewId(viewId: string) => voidProgrammatically switch views. Persisted per user (activeViewStorage backend — localStorage by default).
isLoadingbooleantrue until both queries have resolved.
errorError | nullFirst error from either query.
refetch() => voidRefetch both the data source and views queries.

How It Works

Backend calls

  • Data source (createDataSourceClient) — getBySlug(appSlug, dataSourceSlug, { expand: dataSourceExpand }) loads the data source, its fields, and (with the default 'enums' expansion) enum options in a single request. The enums expansion implies fields. Skipped entirely when dataSource is injected.
  • Views (createDataViewClient) — list({ appId }) loads non-archived views. Mutations use create, update(id, body), and remove(id). Successful mutations invalidate the views query so the tab list refreshes. Skipped when enableDataViews is false.

DB-free metadata

For surfaces that already hold the schema in memory — or system data sources without a metadata / view-configuration route — the hook can run without any schema fetch:

  • dataSource injects the schema; the getBySlug call is skipped and fields, relation targets, and the returned dataSource all read from your object.
  • enableDataViews: false stops the /views request (the tab strip then shows only systemViews).
  • dataSourceExpand: false drops the expand param for backends that reject it (only relevant when the hook does fetch).
const viewSelect = useDocyrusDataViewSelect({
  client,
  appSlug: 'core',
  dataSourceSlug: 'system_users',
  dataSource: systemUsersSchema, // ← no getBySlug
  enableDataViews: false         // ← no /views
});

This is the single integration point that makes useDocyrusDataGrid, useDocyrusDataTable, useDocyrusKanban, useDocyrusDataGallery, and useDocyrusMapView accept the same dataSource / enableDataViews / dataSourceExpand options — they all forward into this hook.

View shape mapping

DataGridViewSelect uses SavedDataGridView (TanStack Table + react-querybuilder types). DataView from the Docyrus backend uses opaque Record<string, unknown> fields. The hook packs and unpacks like this:

DataView fieldSavedDataGridView fields
columnscolumnVisibility, columnOrder, columnPinning, grouping, rowHeight, displayMode
filterscolumnFilters, filterQuery
sortsorting
color_rulesrowColorRules, cellColorRules

Views created by other clients with empty columns still unpack to valid SavedDataGridView objects (empty {} / [] defaults).

Field type mapping

The default DataSourceField → FullField mapping covers common Docyrus field types (field-text, field-number, field-date, field-dateTime, field-status, field-enum, field-multiSelect, etc.). For enum-backed fields, the enums array from the expand=enums response is mapped to FullField.values using each enum's slug as the filter value and name as the label. For field types that need richer filter UI, pass mapField or replace the whole list via overrideFields.

Active view & persistence

The hook owns active view state and passes it as a controlled activeViewId to DataGridViewSelect. On first load:

  1. If persistActiveView is on (default) and a previous selection is in storage for the same app/data source, that view is restored — provided it still exists.
  2. Otherwise the view marked is_default on the backend wins.
  3. Otherwise the first view wins.

User selections update state and write to storage under docyrus:data-grid-view:<appSlug>:<dataSourceSlug>[:<appId>]. The backend defaults to localStorage; pass activeViewStorage: 'session' for per-tab scoping. If the active view is deleted by another client, the hook automatically reselects using the same fallback chain after the next refetch.

When a data-listing hook (useDocyrusDataGrid / useDocyrusDataTable / useDocyrusKanban) is given persistState, it threads activeViewStorage (from persistState.storage) and a unified persistKey (docyrus:view-params:<appSlug>:<dataSourceSlug>[:<appId>]:__active-view__ — appId included when present because saved-view lists are appId-scoped) into this hook automatically — so the active view lives in the same storage backend and key namespace as the persisted view parameters. Explicit activeViewStorage / persistKey options passed by the consumer still win. Without persistState, the historical always-on localStorage behavior is unchanged. The per-user hidden-views list always stays in localStorage — hiding a view is a durable preference, not a per-tab tweak.

Out of scope

  • Hidden views (onViewHide / onViewUnhide). DataView.archived is a destructive soft-delete and shouldn't be conflated with "hidden from tabs".

On this page