---
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": "departmentId", "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[]`), `actions[]`,
`stats[]`, `rowClickTarget`.

> **Dead vocabulary — do not author**: `pageSize` and `filters.orientation`
> have NO downstream consumer (the pager is user-controlled server-side; the
> FilterBar is always horizontal with progressive disclosure). They are kept
> parseable for legacy documents only.

**`stats[]` — the KPI row** (from the `- **Indicateurs** :` bullet, ≤ 4): each
entry is `{ "key", "labelKey", "type": "kpi|counter", "filter"?: { "field",
"value" }, "icon"?, "permission"? }`. A stat is a COUNT of the list's own
entity; `filter.field` must be one of the screen's declared filters (that is
the server-bound wire param) — anything else renders visibly unwired ("—").
Canonical Zod shape: `PageStatMinSchema` in scaffold-component `types.ts`.

**Responsive columns**: the grid is rendered responsively — the `code` + name/
`libellé` columns stay visible at every width and the rest appear as the screen
widens (long values are clipped with a tooltip). This is automatic, so a column
needs no extra attribute. Override only when the default order is wrong, by adding
to a `columns[]` entry: `"minBreakpoint": "sm|md|lg|xl"` (the min width at which the
column shows), `"priority": "always"` (pin it visible at every width) or
`"truncate": false` (opt out of the ellipsis+tooltip).

### `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.fields[]` may be empty for list pages without filters.
(`filters.orientation` is dead vocabulary — see above.)

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.

**A `lookup` filter's `field` is the FK PROPERTY, not the relation name** —
`departmentId`, never `department`. That string is not a label: downstream it
becomes the filter state key, the DTO property the filter matches, the `?…=`
query param and the backend's `[FromQuery] Guid?`. They only line up when it is
the property name. A relation-named filter matched nothing anywhere and shipped
as a free-text box over a Guid — no lookup, no filtering, and no error. The
`entity` property still names the target; `create-prd` resolves it into the
pagespec's `fkTo` block.

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 a flat `fields[]` list, a sectioned
one-page layout via `sections[]` (titled field groups, all visible in one
scroll), or a tabbed layout via `tabs[]` (one of the three is mandatory).
In `edit`/`detail` mode, the form may also expose related collections
through `relatedTabs[]`.

`app` names the business application the related entity lives in — written ONLY
when it is not the screen's own (the bullet form is `, app FACTURATION,`). It
travels to the pagespec as `relatedApp`, and the generated page keys its hook and
routes imports by it; left unsaid, a cross-application tab builds paths that do not
exist. It is usually deduced from the relation's `scope cross-module (APP/MOD)` —
write it when the relation cannot tell. Consequence: any tab leaving its page's own
module is rendered under the tenant-catalogue guard, so it disappears for a client
that was never given the target module.

```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", "displayMode": "table", "screenTarget": "SCR-CRM-INVOICES-LIST-001", "permission": "crm.invoices.read" },
      { "key": "contacts", "label": "Contacts", "relatedEntity": "Contact", "relationship": "clientId", "displayMode": "cards", "screenTarget": "SCR-CRM-CONTACTS-LIST-001" },
      { "key": "claims",   "label": "Litiges",  "relatedEntity": "Claim",   "relationship": "clientId", "displayMode": "summary", "screenTarget": "SCR-CRM-CLAIMS-LIST-001" },
      { "key": "factures", "label": "Factures", "relatedEntity": "Invoice", "relationship": "clientId", "displayMode": "table", "app": "FACTURATION", "screenTarget": "SCR-FACTURATION-FACTURES-LIST-001", "permission": "facturation.factures.read" }
    ],
    "submitPermission": "crm.clients.update"
  }
}
```

> Per-field column spans (`col`) are not consumed downstream — the layout is
> decided by `formLayout` + the generator's odd-run balancing (`/ui-design`
> directives). Do not author them.

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

### `sections[]` — one page, titled groups

The third layout: `sections[]` entries of shape `{ "key", "label", "fields": [...] }`
group the fields into titled cards on ONE page — array order = on-screen
order; 2 to 4 sections, each ≥ 2 fields. Never combine `sections[]` and
`tabs[]` on the same screen (Section-inside-Tab nesting is not v1). The
canonical machine schema lives in `lib/page-spec-sections.ts`.

```json
"sections": [
  { "key": "identity", "label": "Identité", "fields": ["userId", "birthDate"] },
  { "key": "contract", "label": "Contrat",  "fields": ["hireDate", "contractEndDate"] }
]
```

**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 entity IN RELATION (`BaDataModelEntity`) |
| `relationship`  | yes      | FK attribute on `relatedEntity` pointing toward this screen's entity |
| `displayMode`   | no       | `table \| cards \| summary` — default `table` (see below) |
| `screenTarget`  | yes      | Code of an existing `SmartListView` (or `SmartCard`) for `relatedEntity` |
| `permission`    | no       | Optional permission gating the tab visibility (default: `{relatedModule}.{relatedSection}.read`) |

`relatedTabs[]` is forbidden in `mode: "create"` (the record has no id yet to
relate through). The canonical machine schema (and the alias mapping
`relationship`→`relationFk`, `screenTarget`→`targetScreen` used by the
pagespec) lives in `lib/page-spec-related-tabs.ts`.

#### Choosing `displayMode` — the tab's content follows the NATURE of the relation

| Mode | Renders | Pick when |
|------|---------|-----------|
| `table` | embedded server-paged list (row-click → detail, pre-filled « Créer ») | transactional volumes: factures, échanges, commandes, tâches |
| `cards` | card grid (click → detail, pre-filled « Créer ») | visual / low-volume records: adresses, contacts, sites |
| `summary` | count cartouche + « voir tout » link — renders in the BAND ROW above the strip by default, occupying NO tab | the relation matters as a signal, not a working list: litiges ouverts, 1:1 relations, satellites sans création ni fiche |

A `summary` tab does not consume a strip trigger: the generator places its
cartouche in a band row between the detail summary band and the tab strip
(pagespec `placement` field, derived default `'band'`; author `placement:
"tab"` on the pagespec entry to force it back into the strip — rare). This is
what keeps a 360 fiche's bar short while losing NO relation.

`report` (embedded reporting) is deliberately NOT part of v1 — there is no
reporting surface to render yet; declaring it fails the PRD audit (PRD-104).
The `/ba-create-screen` CLI `derive-related-tabs` SUGGESTS a mode per
candidate with its reasoning; the conversation (the intelligence) decides.

---

## 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-area | chart-pie | list | counter`.
Optional per-widget (plan UI 3.4): `"stacked": true` (bar/area — stack the
series), `"sparkline": true` (kpi/counter — mini trend when the result carries
points), `"drillTo": "SCR-{MODULE}-{SECTION}-{VIEW}-{n}"` (the whole widget
opens that list on click) + `"drillParams": { "f.<field>": "<value>", "seg":
"<key>" }` (preset URL-state on arrival — the useListState contract).

---

## SmartKanban

A workflow board: one column per status enum value, draggable cards. The
`sectionCode` is the entity's `*-list` section and `code` is its **second** screen
(`-002`): the board coexists with the SmartListView in that section — never its own
`*-board` section. Downstream it produces **no pagespec/route of its own**: the
PRD folds this config into the section's LIST pagespec (`kanban` block +
`viewModes`, via `derive-kanban-spec`) and the board renders as a viewMode of
the list page — sharing its FilterBar. Column `key`s must be the status enum
values VERBATIM (entité.md); DnD governance comes from the module's `workflow`
business rules (`Flow`), not from this block.

```json
{
  "code": "SCR-HR-LEAVE-LIST-002",
  "title": "Time-off requests board",
  "applicationCode": "hr",
  "moduleCode": "leave",
  "sectionCode": "leave-list",
  "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. MANDATORY for every section hosting sibling resources with no
section-level screen (SCR-020, err — the seeded root key must resolve a page);
also for sections explicitly described as a launchpad/overview. A section
whose own list is the entry point needs none.

```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-area | 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) |

---

## Bullet grammar the CLI parsers read — `screen-grammar:v1`

The bullets below are the ONLY `- **…**` lines of a screen block that a
deterministic CLI parser reads today (`audit-ba/rules/screen-blocks.ts` for the
field lists, groups and actions; `lib/ba-screens.ts` for the heading-level
fields, the related-tab and the kanban grammars). Every other bullet the level
files teach (`Segments`, `Tri par défaut`, `État vide`, `Résumé`, `Widgets`,
`QuickLinks`, `Sources`, `Mobile`, the list-level `Navigation`) is read by the
PRD synthesis only. Canonical source: `lib/screen-grammar.ts` — this table is
drift-locked against it and against both parsers (`lib/__tests__/screen-grammar-drift.test.ts`):
teaching a new bullet means adding its row here, in the SSOT, and in the parser
that reads it — in the same change.

<!-- screen-grammar:v1 -->
| Puce | Clé repliée | Alias tolérés | Lecteurs | Types | Référence |
|------|-------------|---------------|----------|-------|-----------|
| `**Entité**` | `entite` | — | screen-blocks, ba-screens | * | SKILL.md |
| `**Permission**` | `permission` | — | ba-screens | * | SKILL.md |
| `**Cas d'usage liés**` | `cas d'usage lies` | `cas dusage lies` | screen-blocks | * | SKILL.md |
| `**Mode**` | `mode` | — | screen-blocks, ba-screens | SmartForm | levels/form-screens.md |
| `**Colonnes**` | `colonnes` | — | screen-blocks, ba-screens | SmartListView, SmartKanban | levels/list-screens.md, levels/kanban-screens.md |
| `**Filtres**` | `filtres` | — | screen-blocks | SmartListView | levels/list-screens.md |
| `**Indicateurs**` | `indicateurs` | — | screen-blocks | SmartListView | levels/list-screens.md |
| `**Champs**` | `champs` | — | screen-blocks | SmartForm | levels/form-screens.md |
| `**Section « X »**` | `section` | — | screen-blocks | SmartForm | levels/form-screens.md |
| `**Onglet « X »**` | `onglet` | — | screen-blocks, ba-screens | SmartForm | levels/form-screens.md |
| `**Onglet lié « X »**` | `onglet lie` | — | ba-screens | SmartForm | levels/form-screens.md |
| `**Sans onglets liés**` | `sans onglets lies` | — | ba-screens | SmartForm | levels/form-screens.md |
| `**Cycle de vie**` | `cycle de vie` | — | screen-blocks | SmartForm | levels/form-screens.md |
| `**Actions**` | `actions` | — | screen-blocks | * | SKILL.md |
| `**Actions personnalisées**` | `actions personnalisees` | — | screen-blocks | * | SKILL.md |
| `**Champ statut**` | `champ statut` | — | ba-screens | SmartKanban | levels/kanban-screens.md |
| `**Carte**` | `carte` | — | ba-screens | SmartKanban | levels/kanban-screens.md |
| `**Navigation**` | `navigation` | — | ba-screens | SmartKanban | levels/kanban-screens.md |
<!-- /screen-grammar:v1 -->
