Skip to content

Views Module: Unified Admin List System #593

@lane711

Description

@lane711

Overview

Implement a Views module that serves as the foundation for all admin list pages. Instead of separate hardcoded implementations for content list, collections list, users list, etc., Views provides a unified system for querying and displaying data in tables.

This is inspired by Drupal's Views module but simplified for a headless CMS context.

Core Concept

Views are saved queries with configurable table display. Users can:

  1. Create a view by selecting a base table/collection
  2. Choose which fields to display as columns
  3. Set default filters and sorts
  4. Optionally expose filters for users to adjust
  5. Save and reuse the view

Architecture

┌─────────────────────────────────────────────────┐
│                 ViewRenderer                     │
│  (single component renders any view as table)   │
└─────────────────────────────────────────────────┘
          │              │              │
    ┌─────┴─────┐  ┌─────┴─────┐  ┌─────┴─────┐
    │  System   │  │  System   │  │  Custom   │
    │  Views    │  │  Views    │  │  Views    │
    │(content,  │  │(users,    │  │  (user    │
    │collections)│ │ media)    │  │ created)  │
    └───────────┘  └───────────┘  └───────────┘

System views = Built-in, non-deletable, power existing admin pages
Custom views = User-created for their collections

Benefits

  • DRY - One table renderer, one filter form builder, one pagination component
  • Customizable - Users can modify default views (add columns, change filters)
  • Consistent UX - Same patterns across all list pages
  • Less code - Remove duplicated list template implementations
  • Extensible - New list pages are just view configurations

Data Model

views Table

CREATE TABLE views (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL UNIQUE,
  display_name TEXT NOT NULL,
  description TEXT,

  -- Query source
  base_table TEXT NOT NULL,           -- 'content', 'collections', 'users', 'media'
  collection_id TEXT,                 -- For content views, which collection (NULL = all)

  -- What fields to show (JSON array)
  columns TEXT NOT NULL,

  -- Default filters (JSON - QueryFilter format)
  default_filters TEXT,

  -- Default sort (JSON array)
  default_sort TEXT,

  -- Which filters users can adjust (JSON array)
  exposed_filters TEXT,

  -- Row actions (JSON array) - edit, delete, duplicate, etc.
  row_actions TEXT,

  -- Bulk actions enabled (JSON array)
  bulk_actions TEXT,

  -- Pagination
  items_per_page INTEGER DEFAULT 20,

  -- System view flag (cannot be deleted)
  is_system INTEGER DEFAULT 0,

  -- Metadata
  is_active INTEGER DEFAULT 1,
  created_by TEXT REFERENCES users(id),
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL
);

Column Configuration

interface ViewColumn {
  field: string           // "data.title", "status", "created_at", "author.email"
  label: string           // Display label
  sortable?: boolean      // Allow sorting by this column
  width?: string          // CSS width
  renderer?: string       // Custom renderer: 'status_badge', 'date', 'author_avatar', 'link'
}

Exposed Filter Configuration

interface ExposedFilter {
  field: string
  label: string
  type: 'text' | 'select' | 'status' | 'date' | 'date_range'
  options?: { value: string; label: string }[]
}

System Views (Pre-installed)

1. Content List View

Replaces current /admin/content implementation

{
  "name": "system_content",
  "display_name": "All Content",
  "base_table": "content",
  "collection_id": null,
  "columns": [
    { "field": "title", "label": "Title", "sortable": true, "renderer": "link" },
    { "field": "collection.display_name", "label": "Collection", "sortable": true },
    { "field": "status", "label": "Status", "renderer": "status_badge" },
    { "field": "author.name", "label": "Author", "renderer": "author_avatar" },
    { "field": "updated_at", "label": "Updated", "sortable": true, "renderer": "date" }
  ],
  "exposed_filters": [
    { "field": "collection_id", "label": "Collection", "type": "select" },
    { "field": "status", "label": "Status", "type": "status" },
    { "field": "search", "label": "Search", "type": "text" }
  ],
  "default_sort": [{ "field": "updated_at", "order": "desc" }],
  "row_actions": ["edit", "duplicate", "delete"],
  "bulk_actions": ["publish", "unpublish", "delete"],
  "is_system": true
}

2. Collections List View

Replaces current /admin/collections

3. Users List View

Replaces current /admin/users

4. Media Library View

Replaces current /admin/media

Implementation Tasks

Phase 1: Core Infrastructure

  • Add views table to schema.ts
  • Create migration
  • Add Zod validation schemas and TypeScript types
  • Create ViewService class (CRUD + query execution)
  • Create ViewQueryBuilder (extends/uses QueryFilterBuilder)

Phase 2: Admin UI Components

  • Create ViewRenderer component (renders any view as table)
  • Create ExposedFilterForm component
  • Create RowActions component
  • Create BulkActions component
  • Create cell renderers (status_badge, date, link, author_avatar)

Phase 3: Admin Routes & Templates

  • Create /admin/views routes
  • Views list template
  • View builder form template
  • View results template

Phase 4: System Views

  • Create system view seed data
  • Wire up /admin/content to use system content view
  • Wire up /admin/collections to use system collections view
  • Wire up /admin/users to use system users view
  • Wire up /admin/media to use system media view

Phase 5: Cleanup

  • Remove old hardcoded list templates
  • Remove old list-specific route handlers
  • Update admin sidebar menu

Files to Create

packages/core/src/
├── db/schema.ts                              # Add views table
├── services/view-service.ts                  # View CRUD & query execution
├── services/view-query-builder.ts            # SQL generation
├── routes/admin-views.ts                     # Admin routes
├── templates/
│   ├── components/
│   │   ├── view-renderer.template.ts         # Main table renderer
│   │   ├── view-exposed-filters.template.ts  # Filter form
│   │   ├── view-row-actions.template.ts      # Per-row actions
│   │   ├── view-bulk-actions.template.ts     # Bulk action bar
│   │   └── view-cell-renderers.template.ts   # Status badge, date, etc.
│   └── pages/
│       ├── admin-views-list.template.ts      # List of views
│       └── admin-views-form.template.ts      # Create/edit view
├── seeds/system-views.ts                     # Pre-installed system views

Files to Modify

packages/core/src/
├── app.ts                                    # Register views routes
├── templates/layouts/admin-layout-catalyst.template.ts  # Add Views menu item

Files to Eventually Remove

packages/core/src/templates/pages/
├── admin-content-list.template.ts            # Replaced by ViewRenderer
├── admin-collections-list.template.ts        # Replaced by ViewRenderer
├── admin-users-list.template.ts              # Replaced by ViewRenderer

Out of Scope for v1

  • Multiple display formats (grid, cards) - table only
  • Field formatters beyond basic (date, status_badge, link)
  • Complex relationships beyond author
  • Aggregation (COUNT, SUM)
  • View cloning/export/import
  • Access control per view
  • Caching layer

Success Criteria

v1 is complete when:

  1. ✅ Views can be created/edited/deleted from admin
  2. ✅ Custom views work for any collection
  3. ✅ System content view powers /admin/content
  4. ✅ Exposed filters work with HTMX live updates
  5. ✅ Row actions (edit, delete) work
  6. ✅ Bulk actions work
  7. ✅ Pagination works
  8. ✅ Old content list template is removed

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions