Admin UI
Entity configuration

Entity Configuration

Each entity can be configured individually to customize how it appears and behaves in the Admin UI.

Configuration Overview

PropertyTypeDescription
collectionTitleFunctionCollection name (plural)
itemTitleFunctionItem display text
fieldsobjectField configurations
indexobjectIndex page configuration
newobjectNew item page configuration
editobjectEdit page configuration
showobjectShow page configuration
assocFromobjectAssociation table configuration

Basic Example

{
  entities: {
    User: {
      collectionTitle: () => 'Users',
      itemTitle: (entityUI, item) => `${item.firstName} ${item.lastName}`,
      fields: {
        email: { label: 'Email Address' },
        password: false, // Hide this field
        createdAt: { label: 'Created' }
      },
      index: {
        fields: ['email', 'firstName', 'lastName', 'createdAt']
      },
      show: {
        fields: ['email', 'firstName', 'lastName', 'createdAt'],
        content: [
          {
            title: 'Recent Activity',
            content: async ({ item }) => `<p>Last login: ${item.lastLogin}</p>`
          }
        ]
      },
      edit: {
        fields: ['email', 'firstName', 'lastName']
      }
    }
  }
}

collectionTitle

collectionTitle?: (entityUI: EntityUI) => string
TypeDefaultDescription
FunctionHumanized entity name (plural)Title for the collection/list view

Example

{
  User: {
    collectionTitle: () => 'System Users'
  }
}

itemTitle

itemTitle?: (entityUI: EntityUI, item: any) => string
TypeDefaultDescription
FunctionEntity name + IDDisplay name for individual items

Used in page titles, breadcrumbs, and links.

Example

{
  User: {
    itemTitle: (entityUI, item) => `${item.firstName} ${item.lastName} (${item.email})`
  },
  Product: {
    itemTitle: (entityUI, item) => item.name
  }
}

fields

fields?: { [name: string]: FieldUIConfiguration | false }
TypeDefaultDescription
objectauto-generatedPer-field configuration

Set a field to false to hide it completely.

See Field configuration for detailed field options.

Example

{
  User: {
    fields: {
      email: { label: 'Email Address' },
      password: false, // Hide password field
      role: {
        label: 'User Role',
        hint: 'Select the user role'
      }
    }
  }
}

index

index?: {
  fields?: string[];
  actions?: ActionConfig;
}
TypeDefaultDescription
objectall fieldsIndex page configuration

fields

Array of field names to display in the table.

actions

Custom actions shown in the index page.

Example

{
  User: {
    index: {
      fields: ['email', 'firstName', 'lastName', 'role', 'createdAt'],
      actions: {
        export: {
          label: 'Export Users',
          action: async ({ entityUI, principal }) => {
            // Export logic
            return { redirect: '/download/users.csv' };
          }
        }
      }
    }
  }
}

new

new?: {
  fields?: string[];
  actions?: ActionConfig;
}
TypeDefaultDescription
objectall editable fieldsNew item page configuration

Example

{
  User: {
    new: {
      fields: ['email', 'firstName', 'lastName', 'role']
      // password will be auto-generated or sent via email
    }
  }
}

edit

edit?: {
  fields?: string[];
  actions?: ActionConfig;
}
TypeDefaultDescription
objectall editable fieldsEdit page configuration

Example

{
  User: {
    edit: {
      fields: ['email', 'firstName', 'lastName', 'role'],
      actions: {
        resetPassword: {
          label: 'Reset Password',
          confirm: 'Send password reset email?',
          action: async ({ item }) => {
            await sendPasswordResetEmail(item.email);
            return { message: 'Password reset email sent' };
          }
        }
      }
    }
  }
}

show

show?: {
  fields?: string[];
  actions?: ActionConfig;
  content?: ContentBlockConfig[];
}
TypeDefaultDescription
objectall fieldsShow page configuration

fields

Array of field names to display.

actions

Custom actions shown on the show page.

content

Additional content blocks displayed below the main fields.

Example

{
  User: {
    show: {
      fields: ['email', 'firstName', 'lastName', 'role', 'createdAt', 'lastLogin'],
      actions: {
        impersonate: {
          label: 'Impersonate User',
          confirm: 'Log in as this user?',
          action: async ({ item, principal }) => {
            const token = generateImpersonationToken(item.id);
            return { redirect: `/app?token=${token}` };
          }
        }
      },
      content: [
        {
          title: 'Activity Log',
          content: async ({ item, entityUI }) => {
            const logs = await getActivityLogs(item.id);
            return `<ul>${logs.map(l => `<li>${l.action} - ${l.timestamp}</li>`).join('')}</ul>`;
          }
        },
        {
          title: 'Location',
          content: async ({ item }) => ({
            map: { latitude: item.latitude, longitude: item.longitude }
          })
        }
      ]
    }
  }
}

assocFrom

assocFrom?: {
  fields?: string[];
  actions?: ActionConfig;
}
TypeDefaultDescription
objectdefault fieldsConfiguration for association tables

When this entity appears as a related entity table (AssocBy), use this configuration.

Example

{
  Order: {
    assocFrom: {
      fields: ['orderNumber', 'total', 'status', 'createdAt']
    }
  }
}
 
// In User show page, when showing user's orders,
// only these fields will be displayed

Content Blocks

Content blocks allow you to add custom content to the show page.

type ContentBlockConfig = {
  title: string | ((params: ContentBlockParams) => string);
  content: (params: ContentBlockParams) => Promise<string | RenderContent>;
}
 
type ContentBlockParams = {
  item: any;
  entityUI: EntityUI;
  principal: Principal;
}
 
type RenderContent =
  | string
  | { map: { latitude: number; longitude: number } };

Text Content

{
  title: 'Summary',
  content: async ({ item }) => {
    return `<p>Total orders: ${item.orderCount}</p>`;
  }
}

Map Content

{
  title: 'Location',
  content: async ({ item }) => ({
    map: {
      latitude: item.latitude,
      longitude: item.longitude
    }
  })
}

Dynamic Title

{
  title: ({ item }) => `Orders (${item.orders.length})`,
  content: async ({ item, entityUI }) => {
    return entityUI.entityTable({ items: item.orders });
  }
}

Polymorphic Entities

For entities implementing interfaces:

{
  Vehicle: {
    // Base interface configuration
    index: { fields: ['name', 'type'] }
  },
  Car: {
    // Implementing entity configuration
    index: { fields: ['name', 'wheels', 'doors'] }
  },
  Motorcycle: {
    index: { fields: ['name', 'wheels', 'engineCC'] }
  }
}

The Admin UI will:

  • Show type selector in new/edit forms
  • Link to implementing entities from base entity
  • Allow separate indices for each type

Complete Example

{
  entities: {
    Product: {
      collectionTitle: () => 'Products',
      itemTitle: (entityUI, item) => item.name,
 
      fields: {
        name: { label: 'Product Name' },
        sku: { label: 'SKU' },
        price: {
          label: 'Price',
          hint: 'Price in EUR'
        },
        description: { label: 'Description' },
        image: { label: 'Product Image' },
        category: { label: 'Category' },
        inStock: { label: 'In Stock' },
        createdAt: false // Hide this field
      },
 
      index: {
        fields: ['image', 'name', 'sku', 'price', 'inStock', 'category'],
        actions: {
          export: {
            label: 'Export to CSV',
            action: async () => ({ redirect: '/api/products/export' })
          }
        }
      },
 
      new: {
        fields: ['name', 'sku', 'price', 'description', 'image', 'category', 'inStock']
      },
 
      edit: {
        fields: ['name', 'sku', 'price', 'description', 'image', 'category', 'inStock'],
        actions: {
          duplicate: {
            label: 'Duplicate',
            action: async ({ item, entityUI }) => {
              const newItem = { ...item, id: undefined, name: `${item.name} (Copy)` };
              const saved = await entityUI.entity.save(newItem);
              return { redirect: entityUI.editPath(saved.id) };
            }
          }
        }
      },
 
      show: {
        fields: ['name', 'sku', 'price', 'description', 'image', 'category', 'inStock'],
        actions: {
          viewOnSite: {
            label: 'View on Website',
            action: `/products/${item.slug}`,
            newWindow: true
          }
        },
        content: [
          {
            title: 'Sales Statistics',
            content: async ({ item }) => {
              const stats = await getSalesStats(item.id);
              return `
                <div class="stats">
                  <p>Total sold: ${stats.totalSold}</p>
                  <p>Revenue: €${stats.revenue}</p>
                </div>
              `;
            }
          }
        ]
      },
 
      assocFrom: {
        fields: ['name', 'price', 'inStock']
      }
    }
  }
}