Views Module Technical Specifications

Save filtered list views per-user, build analytics dashboards from any collection, enforce per-user defaults, and govern access via collection-level rules — wired into every collection's auto-generated CRUD.

System Metrics

Saved view loads

< 50ms (stored configuration, not computed)

Default enforcement

Atomic, race-condition-free — never more than one default at a time

Access checks

Index-optimized lookups by owner, by collection, and by default status

Dashboard layout

Opaque JSON, validated as an object on write — no server-side widget resolution

Filter operators

30+ via QueryBuilder — equals, gte, lt, in, not_in, contains, contains_any, etc.

Architecture Overview

The Views module has two concerns:

  1. Saved Views — Filter, sort, column, grouping, and view-type configurations for any collection
  2. Dashboard Workspaces — Grid-based widget layouts assembled from any module's widgets

Both are built on Aeion's standard auto-generated CRUD for their underlying data, plus a small set of view-specific API routes layered on top — set-default, and listing views scoped to a particular collection.

Because both saved views and dashboards ride on the platform's standard collection infrastructure rather than a bespoke storage layer, they inherit tenant isolation, access control, indexing, backup/restore, and audit history for free — the same guarantees every other collection in Aeion OS gets.

Data Model

FieldPurpose
`name`Display name shown in the Saved Views dropdown
`collectionSlug`Which collection this view applies to (e.g., `deals`, `orders`)
`filter`The filter configuration, using Aeion's standard operator syntax
`sort`Sort field and direction
`columns`Ordered list of visible columns
`viewType``list`, `kanban`, `calendar`, `gallery`, or `gantt`
`grouping`Group-by field and aggregation rules
`conditionalFormatting`Color-coding rules
`isDefault`Whether this is the current user's default view for the collection
`isShared`Whether teammates can see and apply this view
`createdBy`The view's owner

Auto-Default Enforcement

Saved Views — One Default Per Collection Per User

Whenever you mark a saved view as your default, Aeion automatically unsets your previous default for that same collection as part of the same save — there's no separate cleanup step and no window where two views both claim to be the default.

What this prevents: You have 3 saved views for the "deals" collection. You click "Set as default" on a fourth. Without this enforcement, you could end up with 4 defaults — ambiguous which one loads. With it, the 4th becomes default and the previous 3 are automatically unset.

Race condition handling: If two browser tabs simultaneously set different views as default, the result is still deterministic — whichever write commits last wins, and it's guaranteed to be the only default. No manual locking or conflict resolution required on your end.

Dashboards — One Default Per User

Dashboards work the same way, except the default is scoped per-user across all dashboards rather than per-collection — you have one default home dashboard, not one per data source.

Access Control

Saved views and dashboards use identical, straightforward access rules:

  • Read: your own views, plus anything shared with the team
  • Create: any authenticated user
  • Update / Delete: the owner, or a tenant admin

Shared views are read-only for non-owners — other users see them but can't edit or delete them. They can, however, duplicate a shared view into their own copy and modify that freely.

Admin override: Admins can update or delete any view in their tenant — used for cleanup when users leave the organization.

API Routes

All view-specific routes require authentication and supplement the standard auto-generated CRUD with view-specific operations.

GET /views/for-collection/:slug

Lists saved views for a specific collection — the requesting user's own views plus any shared views, sorted by creation date.

Use case: Opening a collection fetches saved views for that collection and populates the Saved Views dropdown with "Your views" and "Shared views."

PATCH /views/:id/set-default

Sets a view as default and automatically unsets any previous default for that collection. The request is rejected with a 404 if the view doesn't exist and a 403 if you don't own it — you can only set your own views as default.

Dashboard CRUD Routes

Same pattern for dashboards:

  • GET /views/dashboards/list — user's own + shared
  • POST /views/dashboards — create
  • PATCH /views/dashboards/:id — update (owner or admin)
  • DELETE /views/dashboards/:id — delete (owner or admin)

Frontend Behavior

The Saved Views control appears in the toolbar of every collection in the admin. It handles:

  • Applying a view — instantly swaps filter, sort, columns, view type, grouping, and conditional formatting
  • Saving current state — captures the working configuration as a new named view
  • Rename / Delete / Set Default — available inline for any view you own
  • Sharing — toggling "Shared" makes a view visible to the rest of the tenant

States your team will recognize:

  • Default view selected: star icon on the default view, loads automatically on open
  • Unsaved custom state: an "Unsaved Changes" indicator with an active Save button
  • Shared views: shown with a "shared" badge; non-owner views are read-only
  • Rename mode: inline edit field replaces the view name
  • Empty state: "No saved views yet" with a prominent "Save Current View" action

Every field of a saved view — its filter, sort, columns, view type, grouping, and conditional formatting — is captured on save and restored exactly when the view is applied again, including from other devices and sessions.

Request/Response Shape

Every Saved Views API request accepts and validates the same fields you can set in the UI:

typescript { name: string; // required collectionSlug: string; // required viewType?: "list" | "kanban" | "calendar" | "gallery" | "gantt"; filter?: Record<string, unknown>; sort?: string; columns?: string[]; grouping?: Record<string, unknown>; conditionalFormatting?: Record<string, unknown>; isDefault?: boolean; isShared?: boolean; }

The same shape applies to dashboards, substituting layout for the view-specific fields. Validation happens on every write, so malformed filters or unknown view types are rejected with a clear error rather than silently stored.

Dashboard Layout Storage

A dashboard's layout is stored as a single JSON field — the Views module doesn't lock it to a specific grid or widget-reference schema. Every write is still validated as a JSON object, but the shape of positions, sizing, and per-widget configuration is left to whatever's building on top of it.

Why schema-less: locking layout to one rigid interface would mean a migration every time a new widget type or grid convention ships. Leaving it schema-less means the storage layer never has to change — only the code that reads and writes it does. This is the same trade-off Saved Views makes with filter, grouping, and conditionalFormatting, which are also stored as unstructured JSON.

Filter Operators Reference

OperatorExampleMeaning
`equals``{ status: { equals: "active" } }`Exact match
`not_equals``{ status: { not_equals: "archived" } }`Not equal
`contains``{ name: { contains: "acme" } }`Case-insensitive substring
`starts_with``{ email: { starts_with: "admin@" } }`Prefix match
`ends_with``{ email: { ends_with: "@acme.com" } }`Suffix match
`gt` / `gte``{ value: { gte: 50000 } }`Greater than(equal)
`lt` / `lte``{ closeDate: { lt: "2026-07-01" } }`Less than(equal)
`in``{ stage: { in: ["Negotiation", "Proposal"] } }`Match any in array
`not_in``{ status: { not_in: ["Archived"] } }`Exclude array values
`exists``{ archivedAt: { exists: true } }`Field is/isn't null
`contains_any``{ tags: { contains_any: ["enterprise"] } }`Array contains any of the values
`or``{ or: [{ a: 1 }, { b: 2 }] }`OR group

Architecture Decisions

Storage

Standard platform collections — Views are data, not logic — inherits tenancy, backup, and access control for free

Default enforcement

Atomic auto-unset on write — Single source of truth — never more than one default at a time, no race conditions

Access control

Owner-or-shared read, owner-or-admin write — Simple, predictable sharing model across every collection

Layout storage

Flexible, schema-less widget configuration — New widget types and layouts ship without migrations

Frontend state

Optimistic UI updates with automatic rollback — Views and dashboards feel instant, even before the server confirms

View types

5 values: list/kanban/calendar/gallery/gantt — Covers the large majority of data exploration needs

Column storage

Ordered list of field names — Order-preserving, easy to export and reapply

Views Is the Personalization Layer