# Components Reference

The CMS is built from composable React components organized by function. Every component reads metadata from the schema registry and adapts its behavior based on the current role.

## Layout

### Sidebar

The main navigation sidebar. Reads the schema registry to build a collapsible feature-grouped table list.

```typescript
interface SidebarProps {
  // No props — reads schema registry directly
}
```

| Behavior | Description |
|----------|-------------|
| Feature grouping | Tables grouped by feature with collapsible sections |
| Feature icons | Each feature gets a contextual icon (e.g., Calculator for accounting) |
| Active state | Current table highlighted with primary color |
| Internal filter | Tables with `internal: true` are hidden |
| Footer stats | Shows total feature and table counts |

### Header

Top bar with tagline, organization switcher, and the signed-in user's role.

### Role context

The current role is not user-selectable. `RoleProvider` resolves it from the
user's membership in the active organization and publishes it on `RoleContext`;
components read it with the `useRole()` hook.

```typescript
type Role = "owner" | "admin" | "member";

// useRole() provides: { role, setRole, session }
```

## Table

### DataTable

Standard browse table with clickable rows and sortable columns.

```typescript
interface DataTableProps {
  data: Record[];
  columns: ColumnDef[];
  onRowClick?: (row: Record) => void;
}
```

| Prop | Description |
|------|-------------|
| `data` | Array of records to display |
| `columns` | Column definitions (built by `ColumnFactory`) |
| `onRowClick` | Handler when a row is clicked (navigates to detail view) |

### SpreadsheetTable

Excel/Google Sheets-like editable table with cell selection and keyboard navigation.

```typescript
interface SpreadsheetTableProps {
  data: Record[];
  columns: ColumnDef[];
  onCellEdit: (recordId: string, field: string, value: unknown) => Promise<void>;
  editableFields: Set<string>;
  onFKSearch: (targetTable: string, query: string) => Promise<FKOption[]>;
}
```

| Prop | Description |
|------|-------------|
| `data` | Array of records to display |
| `columns` | Column definitions |
| `onCellEdit` | Called when a cell value is saved (auto-saves on blur/Enter) |
| `editableFields` | Set of field names that can be edited (from guards) |
| `onFKSearch` | Async function for FK typeahead search |

### Toolbar

Search bar, view selector, view mode toggle, and refresh button.

```typescript
interface ToolbarProps {
  table: TableMeta;
  search: string;
  onSearchChange: (search: string) => void;
  onRefresh: () => void;
  selectedView?: string;
  onViewChange: (view: string | undefined) => void;
  viewMode: "table" | "dataTable";
  onViewModeChange: (mode: "table" | "dataTable") => void;
}
```

| Prop | Description |
|------|-------------|
| `table` | Table metadata (used to build view dropdown) |
| `search` | Current search query |
| `onSearchChange` | Handler for search input changes |
| `onRefresh` | Refreshes the table data |
| `selectedView` | Currently selected view name (undefined = all fields) |
| `onViewChange` | Handler for view selection changes |
| `viewMode` | Current view mode |
| `onViewModeChange` | Handler for toggling between Table and Data Table |

### Pagination

Page navigation controls with range indicator.

```typescript
interface PaginationProps {
  page: number;
  pageSize: number;
  total: number;
  onPageChange: (page: number) => void;
}
```

| Prop | Description |
|------|-------------|
| `page` | Current page number (1-indexed) |
| `pageSize` | Records per page |
| `total` | Total record count |
| `onPageChange` | Handler for page changes |

### RowActions

Three-dot dropdown menu on each table row with view, edit, delete, and custom actions.

```typescript
interface RowActionsProps {
  table: TableMeta;
  record: Record;
  onDelete?: (id: string) => void;
  onAction?: (action: ActionMeta, record: Record) => void;
}
```

| Prop | Description |
|------|-------------|
| `table` | Table metadata (used for CRUD access checks and action filtering) |
| `record` | The row's record data |
| `onDelete` | Handler for delete action |
| `onAction` | Handler for custom action selection |

### ColumnFactory

Utility function (not a component) that builds column definitions from table metadata.

```typescript
function buildColumns(
  table: TableMeta,
  role: Role,
  options?: { viewFields?: string[] }
): ColumnDef[];
```

Generates columns with appropriate formatters for each field type: dates, money, booleans, enums, FK references, masked values, and plain text. Respects view field projections when provided.

## Record

### RecordDetail

Grouped field display for a single record. Auto-groups fields by category (Identity, Contact, Financial, References, Settings, Dates, Audit).

```typescript
interface RecordDetailProps {
  table: TableMeta;
  record: Record;
  role: Role;
}
```

| Prop | Description |
|------|-------------|
| `table` | Table metadata (columns, masking, validation) |
| `record` | The record data to display |
| `role` | Current user role (affects masking) |

### FieldDisplay

Renders a single field value with appropriate formatting: masking, booleans, enums, FK links, dates, money, percentages, and numbers.

```typescript
interface FieldDisplayProps {
  column: ColumnMeta;
  value: unknown;
  role: Role;
  masking?: Record<string, MaskingRule>;
  validation?: Record<string, ValidationRule>;
}
```

| Prop | Description |
|------|-------------|
| `column` | Column metadata (type, mode, name) |
| `value` | The raw value to display |
| `role` | Current role (for masking checks) |
| `masking` | Masking rules for the table |
| `validation` | Validation rules (used for enum detection) |

### ActionBar

Card displaying available actions as buttons for the current record.

```typescript
interface ActionBarProps {
  table: TableMeta;
  record: Record;
  onAction: (action: ActionMeta) => void;
}
```

| Prop | Description |
|------|-------------|
| `table` | Table metadata |
| `record` | Current record (used for access condition evaluation) |
| `onAction` | Handler when an action button is clicked |

### RelatedRecords

Card showing incoming FK relationships with record counts. Lists tables that reference the current record.

```typescript
interface RelatedRecordsProps {
  table: TableMeta;
  recordId: string;
}
```

| Prop | Description |
|------|-------------|
| `table` | Table metadata (used to discover incoming FK relationships) |
| `recordId` | Current record ID (used to count related records) |

## Form

### AutoForm

Auto-generated create/edit form built from the table's guard configuration.

```typescript
interface AutoFormProps {
  table: TableMeta;
  mode: "create" | "edit";
  initialData?: Record;
  onSubmit: (data: Record) => Promise<void>;
  onCancel: () => void;
}
```

| Prop | Description |
|------|-------------|
| `table` | Table metadata (guards, columns, validation) |
| `mode` | `"create"` uses createable fields, `"edit"` uses updatable fields |
| `initialData` | Pre-filled values for edit mode |
| `onSubmit` | Handler for form submission |
| `onCancel` | Handler for cancel button |

Features:

- Fields determined from guards (createable for create, updatable for edit)
- Immutable fields shown as disabled with lock icon
- Protected fields shown as disabled with "Updated via actions only" note
- Client-side validation from schema rules
- Boolean fields grouped in a "Settings" section
- Required fields marked with red asterisk

### FieldInput

Single form input that adapts to the column type and validation rules.

```typescript
interface FieldInputProps {
  column: ColumnMeta;
  validation?: ValidationRule;
  value: unknown;
  onChange: (value: unknown) => void;
  error?: string;
  disabled?: boolean;
}
```

| Prop | Description |
|------|-------------|
| `column` | Column metadata (determines input type) |
| `validation` | Validation rules (enum values, min/max, email) |
| `value` | Current field value |
| `onChange` | Handler for value changes |
| `error` | Error message to display |
| `disabled` | Whether the input is disabled |

Renders as: text input, number input, email input, URL input, date picker, select dropdown (for enums), textarea (for long text), or checkbox (for booleans).

## Actions

### ActionDialog

Modal dialog for executing a custom action with auto-generated input fields.

```typescript
interface ActionDialogProps {
  action: ActionMeta;
  record: Record;
  tableName: string;
  onClose: () => void;
  onExecute: (input: Record) => Promise<void>;
}
```

| Prop | Description |
|------|-------------|
| `action` | Action metadata (name, description, inputFields, sideEffects) |
| `record` | The record the action applies to |
| `tableName` | Table name (for context) |
| `onClose` | Handler to close the dialog |
| `onExecute` | Handler to execute the action with form data |

Features:

- Auto-generates input fields from action schema
- Destructive actions get red styling and warning icon
- File response actions get download icon
- Side effects warning banner for `sideEffects: "sync"`
- Loading state during execution
- Error display on failure

## Pages

### PageRenderer

Top-level component that receives a `PageMeta` object and delegates to the appropriate layout component.

```typescript
interface PageRendererProps {
  page: PageMeta;
}
```

Currently supports the `split-panel` layout type. Falls back to an error message for unknown layout types.

### SplitPanelLayout

Renders a two-panel layout with drag-and-drop support (via `@dnd-kit/core`), matching engine integration, and page action dialogs.

```typescript
interface SplitPanelLayoutProps {
  page: PageMeta;
}
```

| Behavior | Description |
|----------|-------------|
| Panel rendering | Renders left and right `DataPanel` components side by side |
| Drag-and-drop | Enables DnD between panels based on panel `features` |
| Match scoring | Computes match suggestions using `computeMatches()` and passes scores to panels |
| Action trigger | Opens `PageActionDialog` when a record is dragged from one panel to another |
| Refresh | Refreshes both panels after a successful action |

### DataPanel

A single panel within a page layout. Fetches data from the API, displays rows with column headers, and supports search.

```typescript
interface DataPanelProps {
  panel: PagePanel;
  dataSource: PageDataSource;
  matchScores?: Map<string, number>;
  onDataLoaded?: (records: Record_[]) => void;
  refreshKey?: number;
}
```

| Prop | Description |
|------|-------------|
| `panel` | Panel metadata (id, title, position, features) |
| `dataSource` | Data source config (table, filters, sort, display columns) |
| `matchScores` | Map of record ID to best match score (for confidence badges) |
| `onDataLoaded` | Callback when records are fetched (used by parent for match computation) |
| `refreshKey` | Incrementing key to trigger a re-fetch |

### PanelRow

A single row within a `DataPanel`. Integrates with `@dnd-kit` for drag-source and drop-target behavior.

```typescript
interface PanelRowProps {
  record: Record_;
  columns: string[];
  panelId: string;
  features?: string[];
  matchScore?: number;
}
```

| Prop | Description |
|------|-------------|
| `record` | The record data for this row |
| `columns` | Column names to display |
| `panelId` | Parent panel ID (used for DnD context) |
| `features` | Panel features (`"drag-source"`, `"drop-target"`) |
| `matchScore` | Match confidence score (renders `ConfidenceBadge` when present) |

### PageActionDialog

Modal dialog triggered by drag-and-drop. Resolves input mappings from source and target records and executes a page action.

```typescript
interface PageActionDialogProps {
  pageAction: PageAction;
  sourceRecord: Record_;
  targetRecord: Record_;
  sourceDataSource: string;
  targetDataSource: string;
  onClose: () => void;
  onSuccess: () => void;
}
```

| Prop | Description |
|------|-------------|
| `pageAction` | The page action definition (table, action, inputMapping, label, icon, confirm) |
| `sourceRecord` | The dragged record |
| `targetRecord` | The drop-target record |
| `sourceDataSource` | Data source name for the source record |
| `targetDataSource` | Data source name for the target record |
| `onClose` | Close handler |
| `onSuccess` | Called after successful action execution (triggers panel refresh) |

### ConfidenceBadge

Displays a match confidence score as a colored percentage badge.

```typescript
interface ConfidenceBadgeProps {
  score: number;  // 0-1
}
```

| Score Range | Color |
|-------------|-------|
| 80%+ | Green |
| 50-79% | Yellow |
| Below 50% | Orange |

### computeMatches (utility)

Not a component — a utility function that runs the matching engine. Compares left and right record arrays using the page's matching rules and returns sorted match suggestions.

```typescript
function computeMatches(
  leftRecords: Record_[],
  rightRecords: Record_[],
  page: PageMeta
): MatchSuggestion[];

interface MatchSuggestion {
  leftId: string;
  rightId: string;
  score: number;
  ruleScores: { rule: string; score: number }[];
}
```

Returns an array of suggestions sorted by score (highest first). Only includes matches above the page's `confidenceThreshold`.

## Next Steps

- **[Schema Format Reference](/ui/admin/schema-format)** — TypeScript types consumed by these components
- **[Table Views](/ui/admin/table-views)** — How DataTable and SpreadsheetTable are used
- **[Inline Editing](/ui/admin/inline-editing)** — SpreadsheetTable editing details
