---
phase: screens
kind: level
level: list
---

# Level — List view (SmartListView)

Use this level when the user wants to **see / browse / search** a collection of
entities. Mental model: a single page with a filters bar at the top, a table (or
card grid) below, and row-level / collection-level action buttons. Filters and
table are **always one screen** — never split.

## When to use

Trigger words: "list", "grid", "browse", "show all", "view all", "table",
"liste", "voir", "afficher".

Mandatory when:
- The section code ends in `-list`, `-grid`, `-browse`, `-search` (or is `list`).
- The bound use cases (the section's `use-case.md`) include verbs like "view",
  "browse", "find", "filter", "search", "review".

## What to produce

Produce **exactly one** `SmartListView` per list section. (When the entity has a
`status` enum with ≥3 states, the section ALSO carries a `SmartKanban` as a second
screen — a second *layout* of the same list, not a second list; see
`levels/kanban-screens.md`.) It carries:

- **Filtres** — the filter bar above the table (0+ fields; a list with only a
  global search input is fine).
- **Colonnes** — the table columns (4 to 7).
- **Indicateurs** *(optional)* — up to 4 KPI tiles rendered ABOVE the filter
  bar (a StatCard row): the headline figures a user scans before touching the
  table (« Total », « Ouvertes », « En retard »…). Author them only when a use
  case actually cites the figure.
- **Tri par défaut** *(optional)* — the initial sort of the table
  (`hireDate (desc)`): author it when the natural reading order is NOT the
  declaration order (most-recent-first lists, alphabetical directories).
- **État vide** *(optional)* — the business wording of the empty list:
  `titre ; description ; icône ; bouton créer` (e.g. « Aucun employé ; Ajoutez
  votre premier employé ; users ; créer »). Absent → the generic « Vide » line.
- **Segments** *(optional)* — one-click cohort tabs above the filter bar
  (« Tous | Actives | Archivées »), each a preset of ONE declared filter
  (`Tous | Actives (status = active) | Archivées (status = archived)`). First
  segment = the unfiltered « Tous » default. Use when the use cases show users
  living in 2-4 well-named cohorts of the same list; the segment wins over the
  filter input on its field.
- **Actions** — collection-level (top toolbar) and row-level buttons, each gated
  by a permission code from the module's `rbac.md`.
- **Navigation** — the row click target: the matching `SmartForm` detail screen
  `code`, so the generator wires row-click navigation.

This one-page model mirrors the React page `scaffold-component` generates
(`<FiltersBar>` + `<DataTable>` in a single `ListPage.tsx`).

`screen.md` entry:

```markdown
### SCR-HR-EMPLOYEES-LIST-001 — Liste des employés (SmartListView)
- **Entité** : Employee (ENT-003)
- **Permission** : `hr.employees.read`
- **Cas d'usage liés** : UC-HR-EMPLOYEES-LIST-001, UC-HR-EMPLOYEES-LIST-002
- **Colonnes** : code (text), fullName (text, tri), department.name (lookup → Department), status (badge: active/inactive/onLeave), hireDate (date)
- **Filtres** : fullName (text/recherche), status (select), departmentId (lookup → Department), hireDate (date-range) — orientation horizontale
- **Indicateurs** : total (count), actifs (count, status = active), enConge (count, status = onLeave)
- **Segments** : Tous | Actifs (status = active) | Sortis (status = exited)
- **Tri par défaut** : hireDate (desc)
- **État vide** : Aucun employé ; Ajoutez votre premier employé ; users ; créer
- **Actions** : create (header, `hr.employees.create`), export (header, `hr.employees.export`), edit (row, `hr.employees.update`), delete (row, `hr.employees.delete`, scope « own »)
- **Navigation** : clic ligne → SCR-HR-EMPLOYEES-DETAIL-001
```

The richer per-field config-as-data shape (filter `type`, column `type`,
`options`, `entity` on lookups) is documented in `references/smartcomponents.md`.

## Choosing filters

Match what the user filters by **most often**. Pick 3 to 6 fields:

1. **Always** a global text search (the primary display attribute — `fullName`,
   `name`, `code`).
2. **Always** the workflow status filter if the entity has a status enum.
3. The most-cited foreign key — named by its **FK property** (`departmentId`,
   `categoryId`, `customerId`), never by the relation (`department`). The filter
   `field` is the state key, the DTO property, the query param and the backend
   `[FromQuery] Guid?` all at once; a relation-named filter matches none of them
   and degrades to a dead free-text box over a Guid.
4. The primary date as a date-range (`createdAt`, `hireDate`, `dueDate`).
5. 1-2 more filters the use cases reference explicitly.

**Order matters**: the rendering shows the global search + the FIRST 2-3
filters as the always-visible "primary" row; everything after collapses behind
« Plus de filtres » (the PRD can re-tier per filter with `tier`, but with no
tiers authored the promotion is heuristic — status/select first, then the
first FK, then the first date-range). List the decisive cuts first. Beyond 8
filters the audit warns (SCR-017) — trim to what the use cases cite.

Orientation is `horizontal` by default; use `vertical` only for portals or
screens with ≥6 filters where a left sidebar makes sense.

## Choosing indicators (Indicateurs)

Optional — a KPI row only earns its place when the FIGURE itself is business
information (a pipeline count, an overdue count), not decoration. Rules:

1. **At most 4** (one grid row; beyond 4 the audit warns — SCR-017).
2. Each is a **count** of the list's own entity: `label (count)` for the global
   count, `label (count, <filterField> = <value>)` for a scoped count — the
   scoping field MUST be one of the screen's declared **Filtres** (that is what
   makes it wirable server-side; anything else ships as a visibly dead « — »).
3. Sums/averages and cross-entity figures belong on a `SmartDashboard`, not on
   the list — the generator will not wire them here.

## Choosing columns

Pick 4 to 7 columns:

1. **Always** the primary display attribute — the entité.md `**Affichage**`
   field (`name`, `fullName`, `title`).
2. A `code` column (before the display attribute) ONLY when the entity is
   actually coded — allocated `**Code pattern**` or typed referential code.
   Most entities are NOT: their name IS the handle. Never `id` as a visible
   column — a GUID identifies nothing for a human.
3. A `status` badge if the entity has a status enum.
4. The most-cited foreign key.
5. The primary date.
6. 1-2 more attributes the use cases reference.

Avoid: long text fields (use `textarea` in the form), heavy computed fields,
more than 7 columns (stops fitting on a normal screen).

**Order matters here too**: the rendering shows at most ~7 columns by default
— the rest stays available behind the column picker (« Colonnes »). The PRD
carries the real judgment (`priority: always|high|medium|low` per column), but
when no priorities are authored the fallback keeps the FIRST 7 in declaration
order: the columns you list first ARE the default table. Beyond 10 declared
columns the audit warns (SCR-017).

## Choosing actions

Map permissions to top-level (collection) and row-level actions:

| Action key | Where | Permission needed      |
|------------|-------|------------------------|
| `create`   | top   | `*.create`             |
| `export`   | top   | `*.export`             |
| `edit`     | row   | `*.update`             |
| `delete`   | row   | `*.delete`             |
| `approve`  | row   | `*.approve` (workflow) |
| `view`     | row   | `*.read` (default)     |

Always include row-level `edit` when a matching detail `SmartForm` exists, and
name that form as the row click target so navigation is wired.

### Custom (non-CRUD) actions — `sync`, `archive`, `approve`, `analyzeImpact`, …

Anything beyond the standard CRUD verbs above is a **custom action** and MUST be
declared under a separate `- **Actions personnalisées** :` bullet using the
structured format defined in `SKILL.md` (`## Custom actions — format structuré`).
Free-text mentions are silently dropped by `ba-create-prd`.

Two flavours :

- `kind: api` — emits a backend endpoint + service method + React Query hook +
  button. Use for `sync`, `archive`, `approve`, `mapToPce`, `analyzeImpact`, …
- `kind: navigate` — emits ONLY a button that calls `navigate(targetRoute)`.
  Use for row-level open / detail clicks that lead to another screen and
  **must not produce an axios call** (this is the fix for the legacy pattern
  `POST /{id}/open → 405`).

Append the sub-bullet to the list screen exactly as shown in the SKILL example.
The audit (`/ba-audit-screens` SCR-011/012/013) verifies the metadata is
complete and the endpoints/targets are unique and resolvable.

## When to use SmartCard instead

If the section is a customer-facing portal or the entity is best shown as tiles
(gallery, mobile-first), use `SmartCard` instead. Cards embed a search input in
their toolbar rather than a full filter bar — see `references/smartcomponents.md`
for the SmartCard shape.

## Common mistakes

- **Two screens (one filter + one table) for the same section** → deprecated.
  One section = one `SmartListView`.
- **Forgetting `entity` on lookup columns** → fails post-check rule 5.
- **No row click target** → the generated page is not navigable (audit SCR-008
  also fires if the named target is broken).
- **Computed fields in the form's create mode** → fails the type-mapping rule.
- **More than 7 columns** → trim to the most important ones.
