Docyrus Data View Config Design
A data view is a saved, named configuration over a single data source's records. It does not change the schema or the data — it controls how a grid/gallery surface (TanStack Table + react-querybuilder) presents that data source: which columns show, in what order, how rows are filtered/sorted/grouped/colored, and how paging/inline-editing/forms behave. Views back the "tab strip" / view picker in Docyrus list pages (useDocyrusDataViewSelect).
Author views with docyrus studio *-data-view, then validate (read back) and test (confirm the saved JSON unpacks to the intended grid state). An unvalidated view is not done.
For the schema being viewed see docyrus-data-source-design; for the React grid that renders views see docyrus-data-grid-page-design; for full CLI flag reference see docyrus-cli-app.
Critical model: opaque JSON blobs with packed sub-keys
The backend stores a view as a flat row (tenant_app_config_data_view) with four opaque jsonb columns — columns, filters, sort, color_rules — plus scalar columns (name, description, icon, color, is_default, sort_order, quick_filter_fields, archived, tenant_app_id). The backend does not validate the inner shape of the four jsonb blobs (the DTO only checks they are objects). The grid UI packs/unpacks them with specific sub-keys — if you write the wrong sub-keys the view saves fine but renders as empty defaults.
The single most important fact: the stored sub-key names differ from the TanStack/UI property names. e.g. visible-columns is stored as columns.visibility (not columnVisibility); color rules are color_rules.row / color_rules.cell (not rowColorRules). Get these exact key names from references/view-config-fields.md — do not guess them.
columns → { visibility, order, pinning, grouping, rowHeight, displayMode,
pagingEnabled, pagingMode, pageSize, inlineEditingEnabled,
readOnlyColumns, columnOptions, formId }
filters → { columnFilters, filterQuery }
sort → { sorting }
color_rules → { row, cell }
Workflow
-
Confirm auth, app, and data source. A view belongs to a data source inside an app.
docyrus auth who --json docyrus apps list --json # find appSlug docyrus studio list-fields --appSlug <app> --dataSourceSlug <ds> --jsonThe
list-fieldsoutput gives the column ids / field slugs you reference invisibility,order,grouping,sorting, filters, and color rules. For enum/select/status filter values you need enum row UUIDs —docyrus studio list-enums --appSlug <app> --dataSourceSlug <ds> --fieldSlug <f> --json. If there is no session, stop and ask the user to rundocyrus auth login. -
Decide the view's intent. Per view settle: name (tab label), which columns are visible and in what order, default sort, any filter (simple column filters and/or a query), grouping, row height, display mode (table or gallery), color rules, paging, inline editing, whether it is the default view. Sketch this for the user when ambiguous. If the data source has a status/single-select field, default to status-based views — see Status fields → design views per status.
-
Build the payload. Prefer
--from-file ./view.jsonfor anything beyond a trivial view — the blobs are nested JSON and awkward inline. Use the per-blob convenience flags (--columns,--filters,--sort,--colorRules,--quickFilterFieldstake JSON strings;--name,--description,--icon,--color,--isDefault,--sortOrderare scalars) only for small views. See references/view-config-fields.md for every key's exact name, type, and accepted values, and references/workflow-examples.md for ready-to-adapt payloads. -
Create the view. See Create.
-
Validate. Re-read with
get-data-viewand confirm every blob has the intended sub-keys with the right column ids / enum UUIDs. See Validate. -
Test. Confirm the saved JSON round-trips to the grid state you intended (and, if possible, that it filters/sorts real records correctly). See Test.
Status fields → design views per status
A status field (status / single-select / "stage"-style enum) is a strong signal to design status-based views. The existence of such a field means the records move through stages, and the most useful tab strip on a list page is one that splits records by stage. When a data source has a status field, default to creating a set of status views (plus a default "All" view) unless the user wants otherwise.
How many views depends on how many enum options the status field has:
- 3–4 options → one view per option. e.g. status
New,Active,Won,Lost→ four viewsNew/Active/Won/Lost, each filteringstatus = <enum uuid>. - More than ~4 options → group related statuses into fewer, meaningful views. One tab per value becomes an unusable strip. Group them by stage of life, e.g.
New,In Progress,On Hold→ an "Open" view;Done,Cancelled→ a "Closed" view. Pick group names the user's domain would recognise. - Usually also keep an "All" view (no status filter) and mark the most-used view (often "All" or "Open") as
is_default.
Each status view is an ordinary view whose filters blob targets the status field by enum row UUID, never the label (read UUIDs from docyrus studio list-enums ... --fieldSlug status --json):
- Single status: one
filters.filterQueryrule with=(or acolumnFiltersentry). - Grouped (multi-status) view: a
filters.filterQueryrule usinginwith an array of UUIDs.
Grouped "Open" view payload (filter only — merge with the rest of your view config):
{
"name": "Open",
"icon": "circle-dot",
"sort_order": 2,
"filters": {
"columnFilters": [],
"filterQuery": {
"combinator": "and",
"rules": [
{ "field": "status", "operator": "in",
"value": ["<new-uuid>", "<in-progress-uuid>", "<on-hold-uuid>"] }
]
}
}
}
These status views are exactly what backs the view-type / status tab strip planned during project planning — keep one view per visible tab.
Studio data-view command cheat-sheet
Selectors — pass exactly one of each pair; the CLI resolves the other and routes to /v1/apps/<appSlug>/data-sources/<dataSourceSlug>/views:
--appId | --appSlug, --dataSourceId | --dataSourceSlug. Mutations also accept --data '<json>' or --from-file ./view.json (JSON only); explicit per-blob flags are merged over the file/data payload. Append --json for machine output.
docyrus studio list-data-views --appSlug crm --dataSourceSlug contacts --json
docyrus studio get-data-view --appSlug crm --dataSourceSlug contacts --viewId <id> --json
docyrus studio create-data-view --appSlug crm --dataSourceSlug contacts --from-file ./view.json --json
docyrus studio update-data-view --appSlug crm --dataSourceSlug contacts --viewId <id> --from-file ./view.json --json
docyrus studio delete-data-view --appSlug crm --dataSourceSlug contacts --viewId <id> --json
list-data-views/create-data-viewaccept an optional--tenantAppId(maps totenant_app_id) to scope a view to a different app than the data source's owner. Most views omit it.update-data-viewadditionally accepts--archived(soft-delete / hide from the tab strip).archivedis not the same as deleting — prefer--archived trueoverdelete-data-viewwhen you just want to retire a view.create-data-viewrequiresname. Everything else is optional; an omitted blob means "grid defaults" for that aspect.
Create a data view
Minimal (just a named view — inherits all grid defaults):
docyrus studio create-data-view --appSlug crm --dataSourceSlug contacts \
--name "All Contacts" --isDefault true --sortOrder 1 --json
Configured (recommended via file — see references/workflow-examples.md for full examples):
docyrus studio create-data-view --appSlug crm --dataSourceSlug contacts \
--from-file ./active-leads-view.json --json
Critical rules
- Stored sub-keys ≠ UI property names. Visible columns →
columns.visibility, order →columns.order, pinning →columns.pinning, grouping →columns.grouping, color rules →color_rules.row/color_rules.cell, sort →sort.sorting, filters →filters.columnFilters/filters.filterQuery. Wrong key = silently ignored (view renders as defaults). Full list in references/view-config-fields.md. - Blobs are not validated server-side. The DTO only asserts each of
columns/filters/sort/color_rulesis an object. Malformed inner JSON is accepted and then ignored by the UI — so always validate by reading back and checking the unpacked shape; the create call succeeding proves nothing about correctness. - Column ids are field slugs (plus built-ins like
id,name,autonumber_id). Get them fromlist-fields. A typo'd column id invisibility/order/sortingis dropped silently. - Enum/relation filter values are UUIDs, never labels (same rule as records). Read them from
list-enums/ the related data source before writing afiltersblob that targets a select/status/relation field. columns.visibilityis an allow/deny map, not a column list. It is{ "<colId>": true|false }; columns absent from the map use their default visibility. To hide a column set itfalse; to force-show settrue. Usecolumns.orderto control sequence.- Many features live inside the
columnsblob, not their own column: paging (pagingEnabled/pagingMode/pageSize), inline editing (inlineEditingEnabled/readOnlyColumns), per-column options (columnOptions), and the bound form (formId). This is so the backend needs no schema change — see references/view-config-fields.md. is_defaultis per data source. Marking a view default does not auto-unset other views server-side; if exactly-one-default matters, clear the previous default'sis_defaultyourself. The UI fallback chain is: persisted localStorage selection →is_defaultview → first view.quick_filter_fieldsis a top-leveluuid[]column (field ids), not part of any blob.- Validate then test, every time. Read back the view and confirm the unpacked shape; delete throwaway test views you create.
Validate
After authoring, confirm the saved view is exactly what you intended:
docyrus studio get-data-view --appSlug <app> --dataSourceSlug <ds> --viewId <id> --json— confirm scalars (name,is_default,sort_order,icon,color,archived) and that each blob holds the expected sub-keys with valid column ids / enum UUIDs.- Cross-check every column id in
columns.visibility/order/grouping, everysort.sorting[].id, every filter field, and everycolor_rulesformula reference againstlist-fieldsoutput — drop anything that does not resolve. docyrus studio list-data-views ... --json— confirm the view appears, ordering (sort_order), and that at most oneis_defaultis set if that was the intent.
Per-field "what correct looks like" checklist is in references/workflow-examples.md.
Test
Prove the view behaves as intended:
- Round-trip check — re-read the view and unpack each blob using the key map in references/view-config-fields.md; confirm it yields the visible columns / order / sort / filter you designed.
- Filter sanity — run the equivalent query against real records to confirm the filter selects the right rows, e.g.
docyrus ds list <app> <ds> --columns "..." --filter "..."(see docyrus-cli-app), ordocyrus dsqlfor complex filters. The view'sfilters.filterQuerymirrors react-querybuilder; the column filters mirror TanStackcolumnFilters. - Clean up —
docyrus studio delete-data-view ... --viewId <id>for throwaway views (or--archived trueto retire without deleting).
Full test playbook is in references/workflow-examples.md.
References
- references/view-config-fields.md — The authoritative field reference: every scalar column, the exact packed sub-keys of
columns/filters/sort/color_rules, their types, accepted values (display modes, row heights, paging modes), the UI-name↔stored-key mapping, and gotchas. Read this before writing any non-trivial view payload. - references/workflow-examples.md — End-to-end worked examples (default view, filtered+sorted+grouped view, color-coded view, gallery view, paging + inline-editing view, form-bound view) as copy-adaptable JSON payloads, plus the validation checklist and test playbook.