HotCRM Logo

List Views

How to configure record list views in HotCRM using *.view.ts files

List Views

List views define how record tables are displayed in HotCRM. They control column layout, filtering, sorting, pagination, bulk actions, and visual styling. Every list view is defined in a *.view.ts file.

File Convention

packages/{package}/src/{object_name}.view.ts

Example from the codebase:

PackageView FilePurpose
CRMaccount.view.tsMultiple list views for the Account object

A single *.view.ts file typically exports multiple named views for the same object, giving users different perspectives on the data.

Basic View Structure

A list view declares the object, visible columns, sort order, and optional filters:

// packages/crm/src/account.view.ts

export const AllAccountsView = {
  name: 'all_accounts',
  label: 'All Accounts',
  object: 'account',

  columns: [
    { field: 'name', width: 250, sortable: true, link: true },
    { field: 'type', width: 120, sortable: true },
    { field: 'industry', width: 150, sortable: true },
    { field: 'annual_revenue', width: 150, sortable: true, align: 'right' },
    { field: 'phone', width: 150 },
    { field: 'owner', width: 150 },
    { field: 'created_date', width: 150, sortable: true, format: 'YYYY-MM-DD' },
  ],

  sort: [{ field: 'name', direction: 'asc' }],

  bulkActions: ['delete', 'update_owner', 'export'],
  inlineEdit: true,

  pagination: {
    pageSize: 25,
    options: [10, 25, 50, 100],
  },
};

export default AllAccountsView;

Column Configuration

Each column defines how a field is displayed in the table:

{
  field: 'name',           // Field API name from the object definition
  width: 250,              // Column width in pixels
  sortable: true,          // Allow sorting by this column
  link: true,              // Render as clickable link to the record detail
  align: 'right',          // Text alignment: 'left' (default), 'center', 'right'
  format: 'YYYY-MM-DD',   // Display format (for dates, currencies, etc.)
}

Filtered Views

Views can pre-filter data to show only relevant records:

My Records View

export const MyAccountsView = {
  name: 'my_accounts',
  label: 'My Accounts',
  object: 'account',

  filters: [
    { field: 'owner', operator: '=', value: '${currentUser.id}' },
  ],

  columns: [
    { field: 'name', width: 250, link: true },
    { field: 'type', width: 120 },
    { field: 'industry', width: 150 },
    { field: 'last_activity_date', width: 150 },
    { field: 'annual_revenue', width: 150, align: 'right' },
  ],

  sort: [{ field: 'last_activity_date', direction: 'desc' }],
};

Value-Based Filter

export const EnterpriseAccountsView = {
  name: 'enterprise_accounts',
  label: 'Enterprise Accounts',
  object: 'account',

  filters: [
    { field: 'annual_revenue', operator: '>', value: 10000000 },
    { field: 'type', operator: '=', value: 'customer' },
  ],

  columns: [
    { field: 'name', width: 250, link: true },
    { field: 'annual_revenue', width: 150, align: 'right' },
    { field: 'employees', width: 100 },
    { field: 'industry', width: 150 },
    { field: 'rating', width: 100 },
    { field: 'owner', width: 150 },
  ],

  sort: [{ field: 'annual_revenue', direction: 'desc' }],
};

Date-Based Filter

export const RecentlyCreatedView = {
  name: 'recently_created_accounts',
  label: 'Recently Created',
  object: 'account',

  filters: [
    { field: 'created_date', operator: '>=', value: 'LAST_30_DAYS' },
  ],

  columns: [
    { field: 'name', width: 250, link: true },
    { field: 'type', width: 120 },
    { field: 'owner', width: 150 },
    { field: 'created_by', width: 150 },
    { field: 'created_date', width: 150 },
  ],

  sort: [{ field: 'created_date', direction: 'desc' }],
};

Multi-Value Filter

export const HotAccountsView = {
  name: 'hot_accounts',
  label: 'Hot Accounts',
  object: 'account',

  filters: [
    { field: 'rating', operator: '=', value: 'Hot' },
    { field: 'type', operator: 'IN', value: ['customer', 'prospect'] },
  ],

  columns: [
    { field: 'name', width: 250, link: true },
    { field: 'annual_revenue', width: 150, align: 'right' },
    { field: 'industry', width: 150 },
    { field: 'last_activity_date', width: 150 },
    { field: 'owner', width: 150 },
  ],

  sort: [{ field: 'annual_revenue', direction: 'desc' }],
};

Filter Operators

List view filters support the following operators:

OperatorDescriptionExample
=Equals{ field: 'status', operator: '=', value: 'active' }
!=Not equals{ field: 'stage', operator: '!=', value: 'closed_lost' }
>Greater than{ field: 'amount', operator: '>', value: 50000 }
>=Greater or equal{ field: 'score', operator: '>=', value: 70 }
<Less than{ field: 'days_open', operator: '<', value: 30 }
<=Less or equal{ field: 'priority', operator: '<=', value: 2 }
INIn list{ field: 'type', operator: 'IN', value: ['a', 'b'] }
NOT INNot in list{ field: 'status', operator: 'NOT IN', value: ['closed'] }

Date Value Shortcuts

ValueMeaning
LAST_7_DAYSRecords from the last 7 days
LAST_30_DAYSRecords from the last 30 days
LAST_60_DAYSRecords from the last 60 days
THIS_MONTHRecords from the current month
THIS_QUARTERRecords from the current quarter
THIS_YEARRecords from the current year
TODAYToday's records

Row Styling

Apply visual emphasis to highlight important records:

export const HotAccountsView = {
  name: 'hot_accounts',
  label: 'Hot Accounts',
  object: 'account',
  // ...

  // Highlight rows with red accent
  rowStyles: {
    backgroundColor: '#FEF2F2',
    borderLeft: '3px solid #EF4444',
  },
};

export const NeedAttentionView = {
  name: 'accounts_need_attention',
  label: 'Needs Attention',
  object: 'account',
  filters: [
    { field: 'last_activity_date', operator: '<', value: 'LAST_60_DAYS' },
    { field: 'type', operator: '=', value: 'customer' },
  ],
  // ...

  // Warning styling with amber accent
  rowStyles: {
    backgroundColor: '#FFFBEB',
    borderLeft: '3px solid #F59E0B',
  },
};

Real-World Example: Multiple Views in One File

The CRM package exports six views for the Account object in a single file:

// packages/crm/src/account.view.ts

export const AllAccountsView = { /* all accounts, no filters */ };
export const MyAccountsView = { /* current user's accounts */ };
export const EnterpriseAccountsView = { /* revenue > $10M, customer type */ };
export const RecentlyCreatedView = { /* created in last 30 days */ };
export const HotAccountsView = { /* hot rating, red styling */ };
export const NeedAttentionView = { /* inactive 60+ days, amber styling */ };

// Export as a collection
export const AccountListViews = {
  all: AllAccountsView,
  my: MyAccountsView,
  enterprise: EnterpriseAccountsView,
  recent: RecentlyCreatedView,
  hot: HotAccountsView,
  needAttention: NeedAttentionView,
};

export default AccountListViews;

Best Practices

1. Provide Multiple Views Per Object

Give users different perspectives on the same data:

// ✅ Good — multiple useful views
export const AccountListViews = {
  all: AllAccountsView,              // Default: everything
  my: MyAccountsView,               // Personal: my records
  enterprise: EnterpriseAccountsView, // Segment: high-value
  recent: RecentlyCreatedView,       // Time-based: new records
  hot: HotAccountsView,             // Priority: needs action
  needAttention: NeedAttentionView,  // Alert: dormant accounts
};

2. Choose Columns Wisely

Show 5–7 columns that answer "what do I need to know at a glance?":

// ✅ Good — focused columns
columns: [
  { field: 'name', width: 250, link: true },
  { field: 'stage', width: 120 },
  { field: 'amount', width: 150, align: 'right' },
  { field: 'close_date', width: 150 },
  { field: 'owner', width: 150 },
]

// ❌ Bad — too many columns
columns: [
  { field: 'name' }, { field: 'type' }, { field: 'industry' },
  { field: 'phone' }, { field: 'email' }, { field: 'website' },
  { field: 'revenue' }, { field: 'employees' }, { field: 'rating' },
  { field: 'owner' }, { field: 'created' }, { field: 'modified' },
]

3. Enable Sorting on Key Fields

// ✅ Good — sort on actionable fields
{ field: 'close_date', sortable: true }
{ field: 'amount', sortable: true }
{ field: 'created_date', sortable: true }

Make the primary identifier field clickable:

// ✅ Good
{ field: 'name', link: true }

5. Set Sensible Default Sort

// Time-sensitive views: sort by date descending
sort: [{ field: 'created_date', direction: 'desc' }]

// Value-based views: sort by amount descending
sort: [{ field: 'annual_revenue', direction: 'desc' }]

// Alphabetical views: sort by name ascending
sort: [{ field: 'name', direction: 'asc' }]

Next Steps

On this page