# Filament Actions UX Standard

> Canonical rules for row actions, bulk actions, header actions, and inspect affordances on all Filament table surfaces.
> This standard consolidates the Action Surface Contract from the constitution and `docs/ui/action-surface-contract.md`.

**Last reviewed**: 2026-03-30

---

## Governing Principle

**Filament-native first.**
Action patterns use native Filament action APIs.
The constitution's Action Surface Contract (in `.specify/memory/constitution.md`) is the authoritative source for safety and RBAC rules.
This document provides the quick-reference standard for implementation.

---

## Required Surfaces

Every list/table must define:

- **Header Actions** — primary CTA (create, sync, etc.)
- **Row Actions** — inspect + contextual operations
- **Bulk Actions** — where batch operations exist
- **Empty-State CTA** — exactly 1 primary action, RBAC-gated

---

## Inspect Affordance (Required)

Every list-style surface must provide a way to open a record.

### Default: clickable rows for list-first and registry surfaces

```php
$table->recordUrl(fn ($record) => /* route to view/edit */)
```

Use this default for:

- CRUD / List-first resources
- Read-only registries / reports
- Reporting and evidence registers such as Review Register and Evidence Overview

### Explicit inspect for queue and audit surfaces

Use an `Inspect` row action or equivalent same-page selected detail when chronology or queue context must remain visible.

- Audit / history pages use explicit inspect
- Queue / review pages use explicit inspect
- These surfaces should not also expose row click as a competing open path

### Alternative: primary link column

Use only when clickable rows are impractical on a clickable-row surface.

- `PrimaryLinkColumn` requires a non-empty declaration reason
- It is not a fallback for queue / review or audit surfaces

### Rule: no lone "View" button

If an open action is the only row action on a clickable-row surface, prefer row click and set `actions([])` to avoid an unnecessary Actions column.

---

## Row Action Limits

- **Max 2 visible** row actions (typically View/Edit or Edit/Delete)
- Everything else goes into an `ActionGroup` labeled "More"
- Destructive actions must never be the primary visible action
- On clickable-row surfaces, prefer zero inline row actions unless a single justified shortcut materially improves the workflow

---

## Bulk Actions

- Bulk actions must be grouped via `BulkActionGroup`
- Destructive bulk actions require confirmation
- Typed confirmation may be required for large/bulk changes
- Do not leave an empty `BulkActionGroup` placeholder visible once filters, record state, or RBAC remove every effective action

---

## Destructive Action Safety

- All destructive actions must use `->requiresConfirmation()`
- Destructive actions must not be styled as primary
- Confirmation text must clearly describe the consequence
- Server-side authorization must still enforce the action regardless of UI confirmation

This is a constitution-level non-negotiable (RBAC-UX-005).

---

## Action Consistency

### Standard action labels

| Action | Label | Icon guidance |
|---|---|---|
| Inspect record | "Inspect" | heroicon-o-eye |
| Edit record | "Edit" | heroicon-o-pencil-square |
| Delete record | "Delete" | heroicon-o-trash |
| Archive record | "Archive" | heroicon-o-archive-box |
| Restore record | "Restore" | heroicon-o-arrow-uturn-left |
| Force delete | "Force Delete" | heroicon-o-trash |

Notes:
- Clickable-row surfaces normally do not render a `View` or `Inspect` row action at all.
- Reporting/evidence registers keep row click primary and should not add a duplicate `View review`-style action.

### Action ordering in "More" group

1. Navigation or inspect helpers first
2. Non-destructive workflow or lifecycle actions next
3. Destructive actions last
4. Do not render an empty `ActionGroup` placeholder

Representative expectations:

- `Policies`: `Export`, then `Sync`, then destructive ignore/delete actions
- `Backup schedules`: `Run now` / `Retry` before archive or force delete
- `Workspaces`: row click stays primary and the secondary `Edit` shortcut lives under `More`

---

## RBAC Enforcement

- Non-member access: abort(404), do not leak existence
- Member without capability: visible but disabled with tooltip
- Server-side must enforce via `Gate::authorize(...)` or Policy method
- Missing server-side authorization is a P0 security bug

---

## Spec / DoD Gate

Every spec with UI changes must include a **UI Action Matrix** listing:

- Which actions exist on which surfaces
- Which are destructive and how confirmation is handled
- Which capabilities gate each action

A change is not "Done" unless the Action Surface Contract is met.

---

## Canonical Sources

- Constitution: `.specify/memory/constitution.md` → "Filament UI — Action Surface Contract"
- Detailed reference: `docs/ui/action-surface-contract.md`
