---
phase: screens
kind: reference
---

# SmartComponents — config-as-data vocabulary

This document defines the per-type **config-as-data** shape (filters, columns,
fields, widgets, quickLinks, related tabs) behind each screen's `screen.md`
bullets. When you write a screen entry, the bullets summarise this config in the
user's language; this reference is the source of truth for the field-level
vocabulary (a column `type`, a widget `aggregation`, a filter `entity`, …) and
for what each SmartComponent type requires. The same vocabulary maps 1:1 onto
the generated React component (see `react-templates.md`).

The JSON snippets below describe each `config` shape per `type`. Each screen is
scoped by its position in the `.smartstack/ba/` tree — the application, module
and section folders ARE its `applicationCode` / `moduleCode` / `sectionCode`; a
home page's scope is encoded in its `SCR-…` code (no module/section segment for
`SmartAppHome`, no section segment for `SmartModuleHome`).

---

## SmartListView

A full list page — filter bar at the top, sortable data grid below, action
buttons (top-level for collection actions, row-level for per-row actions),
optional row click navigation. Replaces the legacy split between a table screen
and a filter screen: filters and table now live in one screen.

```json
{
  "code": "SCR-HR-EMP-LIST-001",
  "title": "Employees",
  "applicationCode": "hr",
  "moduleCode": "employees",
  "sectionCode": "employees-list",
  "type": "SmartListView",
  "entity": "Employee",
  "permission": "hr.employees.read",
  "linkedUseCases": ["UC-001"],
  "config": {
    "filters": {
      "orientation": "horizontal",
      "fields": [
        { "field": "fullName",   "label": "Search",     "type": "text" },
        { "field": "status",     "label": "Status",     "type": "select", "options": ["active","inactive","onLeave"] },
        { "field": "department", "label": "Department", "type": "lookup", "entity": "Department" },
        { "field": "hireDate",   "label": "Hired",      "type": "daterange" }
      ]
    },
    "columns": [
      { "field": "code",            "label": "Code",       "type": "text",   "sortable": true, "width": "100px" },
      { "field": "fullName",        "label": "Name",       "type": "text",   "sortable": true },
      { "field": "department.name", "label": "Department", "type": "lookup", "entity": "Department" },
      { "field": "status",          "label": "Status",     "type": "badge",  "options": ["active","inactive","onLeave"] },
      { "field": "hireDate",        "label": "Hire date",  "type": "date" }
    ],
    "actions": [
      { "key": "create", "label": "New employee", "permission": "hr.employees.create" },
      { "key": "edit",   "label": "Edit",         "permission": "hr.employees.update", "rowLevel": true },
      { "key": "delete", "label": "Delete",       "permission": "hr.employees.delete", "rowLevel": true, "condition": "own" }
    ],
    "pageSize": 25,
    "rowClickTarget": "SCR-HR-EMP-DETAIL-001"
  }
}
```

**Required**: `config.columns` (≥ 1).
**Optional**: `config.filters` (object with `fields[]` and `orientation`),
`actions[]`, `pageSize`, `rowClickTarget`.

### `actions[]` — standard verbs vs custom actions

The `actions[]` array of a SmartListView mixes **standard CRUD** items (`create`,
`edit`, `delete`, `read`, `export`, `import`) — which the downstream pipeline
handles natively from the bare `{ key, label, permission, rowLevel? }` shape
above — with **custom actions** declared in `screen.md` under the dedicated
`- **Actions personnalisées** :` bullet. The full custom-action schema (`code`,
`kind: api|navigate`, `endpoint`, `httpMethod`, `targetScreen`, `permission`,
`UC`, …) lives in `SKILL.md` § Custom actions — format structuré and the
canonical Zod schema is in `lib/page-spec-actions.ts`. Example JSON shape of one
custom api-action once propagated to the pageSpec :

```json
{
  "code": "syncFromPce",
  "kind": "api",
  "scope": "header",
  "endpoint": "sync-from-proconcept",
  "httpMethod": "POST",
  "labelKey": "list.actions.syncFromPce",
  "permission": "referentiels.types-affaire.execute",
  "variant": "secondary",
  "ucReference": "UC-APP-REF-TYPEAFFAIRE-007"
}
```

The `endpoint` (kebab-case) is the URL segment used VERBATIM on both sides — it
appears in the controller's `[HttpPost("sync-from-proconcept")]` and in the
service's `apiClient.post('/api/.../sync-from-proconcept')`. Frontend and
backend cannot drift because there is only one name.

`filters.orientation` allowed values: `horizontal | vertical`. Default
`horizontal`. `filters.fields[]` may be empty for list pages without filters.

Filter field `type` allowed values: `text | select | lookup | daterange |
number | boolean`. When `type` is `lookup`, the `entity` property is required.
When `type` is `select`, the `options` property is required.

Column `type` allowed values: `text | number | date | datetime | badge | lookup
| boolean | currency`. When `type` is `lookup`, the `entity` property is
required. When `type` is `badge`, the `options` property is required.

`rowClickTarget` MUST reference a screen `code` that exists in this analysis
(typically the matching `SmartForm` detail page).

---

## SmartForm

A create/edit/detail form. Supports either a flat `fields[]` list or a
tabbed layout via `tabs[]` (one of the two is mandatory). In `edit`/`detail`
mode, the form may also expose related collections through `relatedTabs[]`.

```json
{
  "code": "SCR-CRM-CLIENT-DETAIL-001",
  "title": "Fiche client",
  "applicationCode": "crm",
  "moduleCode": "clients",
  "sectionCode": "clients-detail",
  "type": "SmartForm",
  "entity": "Client",
  "permission": "crm.clients.read",
  "config": {
    "mode": "detail",
    "tabs": [
      {
        "key": "info",
        "label": "Information",
        "fields": [
          { "field": "code",     "label": "Code",     "type": "text",   "readonly": true, "col": 4 },
          { "field": "name",     "label": "Raison sociale", "type": "text", "required": true, "col": 8 },
          { "field": "category", "label": "Catégorie","type": "lookup", "entity": "ClientCategory", "col": 6 },
          { "field": "status",   "label": "Status",   "type": "enum",   "options": ["active","inactive"] }
        ]
      }
    ],
    "relatedTabs": [
      { "key": "invoices", "label": "Factures", "relatedEntity": "Invoice", "relationship": "clientId", "screenTarget": "SCR-CRM-INVOICES-LIST-001", "permission": "crm.invoices.read" },
      { "key": "contacts", "label": "Contacts", "relatedEntity": "Contact", "relationship": "clientId", "screenTarget": "SCR-CRM-CONTACTS-LIST-001" },
      { "key": "claims",   "label": "Litiges",  "relatedEntity": "Claim",   "relationship": "clientId", "screenTarget": "SCR-CRM-CLAIMS-LIST-001" }
    ],
    "submitPermission": "crm.clients.update"
  }
}
```

**Required**: `config.fields[]` OR `config.tabs[]` (at least one).
**Optional**: `config.relatedTabs[]` (only allowed in `edit`/`detail` mode),
`submitPermission`.

**Field types**: `text | textarea | number | date | datetime | boolean | enum |
lookup | currency | email | password`.

When `type` is `lookup`, `entity` is required.
When `type` is `enum`, `options` is required.

`mode` defaults to `edit` (allowed: `create | edit | detail`).
`computed` holds an expression evaluated client-side by the generated app.

### `relatedTabs[]` schema

| Field           | Required | Description |
|-----------------|---------:|-------------|
| `key`           | yes      | Unique tab key inside this screen |
| `label`         | yes      | Tab label (user language) |
| `relatedEntity` | yes      | Code of the related `BaDataModelEntity` |
| `relationship`  | yes      | FK attribute on `relatedEntity` pointing back to the parent |
| `screenTarget`  | yes      | Code of an existing `SmartListView` for `relatedEntity` |
| `permission`    | no       | Optional permission gating the tab visibility |

`relatedTabs[]` is forbidden in `mode: "create"` (no parent ID exists yet).

---

## SmartDashboard

KPI widgets and chart placeholders. The only collection screen type that may
omit `entity` on the screen itself — each widget can target its own entity.
Use `SmartDashboard` for **free-form analytical pages** (BI, custom KPIs).
For navigational landing pages (app/module/section home), use the
`SmartHome*` types instead.

```json
{
  "code": "SCR-HR-ANALYTICS-001",
  "title": "HR analytics",
  "applicationCode": "hr",
  "moduleCode": "employees",
  "sectionCode": "hr-analytics",
  "type": "SmartDashboard",
  "permission": "hr.employees.read",
  "config": {
    "layout": "grid",
    "widgets": [
      { "key": "active",   "label": "Active employees", "type": "kpi",         "entity": "Employee", "aggregation": "count", "field": "status=active", "col": 3 },
      { "key": "onLeave",  "label": "On leave",         "type": "counter",     "entity": "Employee", "aggregation": "count", "field": "status=onLeave", "col": 3 },
      { "key": "byDept",   "label": "By department",    "type": "chart-pie",   "entity": "Employee", "field": "department.name", "col": 6 },
      { "key": "hireTrend","label": "Hires per month",  "type": "chart-line",  "entity": "Employee", "field": "hireDate", "col": 12 }
    ]
  }
}
```

**Required**: `config.widgets[]` (≥ 1).
**Widget types**: `kpi | chart-line | chart-bar | chart-pie | list | counter`.

---

## SmartKanban

A workflow board: one column per status enum value, draggable cards.

```json
{
  "code": "SCR-HR-LEAVE-BOARD-001",
  "title": "Time-off requests board",
  "applicationCode": "hr",
  "moduleCode": "leave",
  "sectionCode": "leave-board",
  "type": "SmartKanban",
  "entity": "LeaveRequest",
  "permission": "hr.leave.read",
  "config": {
    "statusField": "status",
    "columns": [
      { "key": "draft",     "label": "Draft",     "color": "gray" },
      { "key": "submitted", "label": "Submitted", "color": "blue" },
      { "key": "approved",  "label": "Approved",  "color": "green" },
      { "key": "rejected",  "label": "Rejected",  "color": "red" }
    ],
    "titleField": "code",
    "subtitleField": "employeeName",
    "cardFields": ["startDate", "endDate", "days"],
    "rowClickTarget": "SCR-HR-LEAVE-DETAIL-001"
  }
}
```

**Required**: `statusField`, `columns[]` (≥ 2), `titleField`.

---

## SmartCard

A compact card view. Useful for portals, tile galleries or mobile-first lists.
Filters and search live inside the card grid's toolbar (no separate filter
bar — use `SmartListView` if you need a full filter panel).

```json
{
  "code": "SCR-PORTAL-EMP-LIST-001",
  "title": "Employee directory",
  "applicationCode": "portal",
  "moduleCode": "directory",
  "sectionCode": "directory-list",
  "type": "SmartCard",
  "entity": "Employee",
  "permission": "portal.directory.read",
  "config": {
    "titleField": "fullName",
    "subtitleField": "position",
    "metaFields": ["department.name", "email"],
    "iconField": "profileImage",
    "statusField": "status",
    "clickTarget": "SCR-PORTAL-EMP-DETAIL-001"
  }
}
```

**Required**: `titleField`.

---

## SmartAppHome

Application landing page — KPIs spanning multiple modules + quickLinks to
each module. Emitted once per application (when the app has ≥2 modules).

```json
{
  "code": "SCR-CRM-HOME-001",
  "title": "CRM",
  "applicationCode": "crm",
  "type": "SmartAppHome",
  "permission": "crm.access",
  "config": {
    "widgets": [
      { "key": "open-deals", "label": "Open deals", "type": "kpi",     "entity": "Opportunity", "aggregation": "count", "field": "status=open",  "col": 3 },
      { "key": "won-amount", "label": "Won YTD",    "type": "kpi",     "entity": "Opportunity", "aggregation": "sum",   "field": "amount;status=won", "col": 3 }
    ],
    "quickLinks": [
      { "key": "to-prospects", "label": "Prospects", "icon": "users",    "screenTarget": "SCR-CRM-PROSPECTS-HOME-001" },
      { "key": "to-clients",   "label": "Clients",   "icon": "building", "screenTarget": "SCR-CRM-CLIENTS-HOME-001" }
    ]
  }
}
```

**Required**: `config.widgets[]` (≥ 1) and `config.quickLinks[]` (≥ 1).
**Conditional fields**: `moduleCode` and `sectionCode` are FORBIDDEN on this
type (the screen sits above the module level).

---

## SmartModuleHome

Module landing page — KPIs scoped to the module + quickLinks to its sections.
Emitted once per module (when the module has ≥2 sections).

```json
{
  "code": "SCR-CRM-CLIENTS-HOME-001",
  "title": "Clients",
  "applicationCode": "crm",
  "moduleCode": "clients",
  "type": "SmartModuleHome",
  "permission": "crm.clients.read",
  "config": {
    "widgets": [
      { "key": "total", "label": "Clients", "type": "kpi", "entity": "Client", "aggregation": "count", "col": 3 }
    ],
    "quickLinks": [
      { "key": "to-list",     "label": "Annuaire",      "icon": "list",  "screenTarget": "SCR-CRM-CLIENTS-LIST-001" },
      { "key": "to-segments", "label": "Segmentation",  "icon": "tag",   "screenTarget": "SCR-CRM-SEGMENTS-LIST-001" }
    ]
  }
}
```

**Required**: `config.widgets[]` (≥ 1) and `config.quickLinks[]` (≥ 1).
**Conditional fields**: `moduleCode` is REQUIRED, `sectionCode` is FORBIDDEN.

---

## SmartSectionHome

Section landing page — KPIs scoped to the section + quickLinks to its
resources. Optional, emit only when the section has ≥3 resources or is
explicitly described as a launchpad/overview.

```json
{
  "code": "SCR-HR-EMP-HOME-001",
  "title": "Employés",
  "applicationCode": "hr",
  "moduleCode": "employees",
  "sectionCode": "employees-home",
  "type": "SmartSectionHome",
  "permission": "hr.employees.read",
  "config": {
    "widgets": [
      { "key": "headcount", "label": "Headcount", "type": "kpi", "entity": "Employee", "aggregation": "count", "col": 3 }
    ],
    "quickLinks": [
      { "key": "to-directory", "label": "Annuaire",  "icon": "users",     "screenTarget": "SCR-HR-EMP-LIST-001" },
      { "key": "to-contracts", "label": "Contrats", "icon": "file-text", "screenTarget": "SCR-HR-CONTRACTS-LIST-001" }
    ]
  }
}
```

**Required**: `config.widgets[]` (≥ 1) and `config.quickLinks[]` (≥ 1).
**Conditional fields**: `applicationCode`, `moduleCode`, `sectionCode` all
required.

### `quickLinks[]` schema (shared by all SmartHome* types)

| Field          | Required | Description |
|----------------|---------:|-------------|
| `key`          | yes      | Unique link key inside this screen |
| `label`        | yes      | Button label (user language) |
| `icon`         | no       | Lucide icon name (defaults to a hierarchy icon) |
| `screenTarget` | yes      | Code of an existing screen in this analysis |
| `permission`   | no       | Optional permission gating the link visibility |

### `widgets[]` schema (shared with SmartDashboard)

| Field         | Required | Description |
|---------------|---------:|-------------|
| `key`         | yes      | Unique widget key |
| `label`       | yes      | Widget title |
| `type`        | yes      | `kpi | counter | chart-line | chart-bar | chart-pie | list` |
| `entity`      | yes      | Target `BaDataModelEntity` code |
| `aggregation` | conditional | Required for `kpi`/`counter` (`count|sum|avg|min|max`) |
| `field`       | conditional | Field expression (filter or aggregate target) |
| `col`         | no       | Bootstrap col span 1-12 (defaults to 3 for KPI/counter, 6 for charts, 12 for lists) |
| `permission`  | no       | Optional gate |

---

## Screen-level fields (common to all types)

| Field | Required | Description |
|-------|---------:|-------------|
| `code` | yes | Stable identifier `SCR-NNN`, unique within the analysis |
| `title` | yes | Short human-readable title |
| `applicationCode` | yes | Parent application code from the menu tree |
| `moduleCode` | conditional | Required for every type EXCEPT `SmartAppHome` |
| `sectionCode` | conditional | Required for every type EXCEPT `SmartAppHome` and `SmartModuleHome` |
| `type` | yes | One of `SmartListView | SmartForm | SmartDashboard | SmartKanban | SmartCard | SmartAppHome | SmartModuleHome | SmartSectionHome` |
| `entity` | conditional | Required for `SmartListView`/`SmartForm`/`SmartKanban`/`SmartCard`. Optional for `SmartDashboard`/`SmartHome*` (entities live on each widget). |
| `description` | no | One-sentence purpose |
| `permission` | no | Permission code gating the whole screen |
| `linkedUseCases` | no | Use case codes this screen supports |
| `config` | yes | Polymorphic blob — strict shape per type (see the per-type sections above) |
