Docs / SDK / Entity System

Entity System

Entities are the data objects in your workspace -- contacts, invoices, tasks, projects. The entity system lets you define their types, fields, statuses, relations, and inspector panels as pure data.

Defining an entity type

Use the entityType() builder to define an entity. Every entity type has a name, icon, accent color, display field, and optional statuses, relations, and actions:

import { entityType } from "@substrateui/substrate/entities";

const contact = entityType("contact", {
  name: "Contact",
  namePlural: "Contacts",
  icon: "user",
  accent: "blue",
  module: "crm",
  displayField: "name",
  secondaryField: "company",
  statusField: "status",
  searchFields: ["name", "email", "company"],

  statuses: [
    { id: "active", label: "Active", color: "green", default: true },
    { id: "inactive", label: "Inactive", color: "neutral" },
    { id: "lead", label: "Lead", color: "yellow" },
    { id: "churned", label: "Churned", color: "red", terminal: true },
  ],

  relations: [
    { target: "company", type: "belongs-to", label: "Company", showInInspector: true },
    { target: "deal", type: "has-many", label: "Deals", navigable: true },
    { target: "activity", type: "has-many", label: "Activities" },
  ],

  actions: [
    { id: "email", label: "Send Email", icon: "mail", shortcut: "E" },
    { id: "call", label: "Log Call", icon: "phone" },
    { id: "archive", label: "Archive", icon: "archive", confirm: "Archive this contact?" },
    { id: "delete", label: "Delete", icon: "trash", danger: true, confirm: true },
  ],
});

Statuses

Each status has an id, display label, and color (any AccentColor). Mark one status as default: true for new entities, and use terminal: true for closed/completed states that remove the entity from active views.

Relations

Relations connect entity types to each other. The target field references another entity type's ID, and type can be has-many, belongs-to, many-to-many, or has-one. Set showInInspector: true to display the relation in the entity inspector, and navigable: true to allow clicking through to related entities.

Entity inspector

The inspector is a detail panel that displays when an entity is selected. Define it with entityInspector(). It supports a header, DNA card (key metrics at a glance), and tabbed sections:

import {
  entityInspector,
  inspectorTab,
  fieldSection,
  entityField,
  metric,
} from "@substrateui/substrate/entities";

const contactInspector = entityInspector("contact", {
  header: {
    showIcon: true,
    showStatus: true,
    titleField: "name",
    subtitleField: "company",
    editableTitle: true,
    useEntityAccent: true,
  },

  dna: {
    metrics: [
      metric("deals", "Open Deals", "dealCount", { format: "number", accent: "blue" }),
      metric("value", "Pipeline Value", "totalValue", { format: "currency", accent: "green" }),
      metric("last-contact", "Last Contact", "lastContactDate", { format: "date" }),
    ],
    layout: "horizontal",
  },

  tabs: [
    inspectorTab("details", "Details", "details", {
      sections: [
        fieldSection("contact-info", [
          entityField("email", "email", { format: "email", copyable: true }),
          entityField("phone", "phone", { format: "phone" }),
          entityField("title", "jobTitle", { label: "Job Title" }),
          entityField("company", "companyId", { format: "entity-link", linkedEntityType: "company" }),
        ], { label: "Contact Info", columns: 2 }),

        fieldSection("address", [
          entityField("street", "address.street"),
          entityField("city", "address.city"),
          entityField("state", "address.state"),
          entityField("zip", "address.zip"),
        ], { label: "Address", collapsible: true, defaultExpanded: false }),
      ],
    }),

    inspectorTab("deals", "Deals", "related", {
      relatedConfig: { entityType: "deal", displayMode: "table" },
      badge: 3,
    }),

    inspectorTab("activity", "Activity", "activity"),
  ],

  inlineEditing: true,
  crossNavigation: true,
  activityFeed: { enabled: true, showTimestamps: true },
  width: 360,
});

DNA card

The DNA card shows key metrics at the top of the inspector. Each metric references a field path and can display as a number, currency, percentage, date, duration, or text. Add a trendField to show change direction. Layout can be horizontal, vertical, or grid.

Entity picker

The entity picker is a modal or popover for selecting entities. Use it in relation fields, assignment selectors, or anywhere you need to reference an entity:

import { entityPicker } from "@substrateui/substrate/entities";

const contactPicker = entityPicker({
  entityTypes: ["contact"],
  mode: "combobox",
  search: { placeholder: "Search contacts...", fields: ["name", "email"] },
  showRecent: true,
  maxRecent: 5,
  createNew: true,
  multiple: false,
});

Builder reference

All entity builders are exported from @substrateui/substrate/entities:

FunctionReturns
entityType(id, config)EntityTypeDef
entityInspector(entityTypeId, config)EntityInspectorConfig
entityPicker(config?)EntityPickerConfig
entityHub(config?)EntityHubConfig
entityRegistry(types, groups?)EntityRegistryConfig
metric(id, label, field, config?)EntityMetric
inspectorTab(id, label, type, config?)EntityInspectorTab
fieldSection(id, fields, config?)EntityFieldSection
entityField(id, field, config?)EntityFieldDef