# Filament Table Standard

> **Status:** Active  
> **Last reviewed:** 2026-04-30  
> **Use for:** Standard list-surface rules for production Filament tables  
> **Do not use for:** Overriding product-specific needs without an explicit documented exception

## Standard

TenantPilot standardizes production Filament list surfaces with a convention-first model:

- Primary: searchable identifier or record title that anchors the row.
- Context: status, ownership, recency, and counts needed for a normal scan.
- Detail: technical IDs, secondary timestamps, verbose metadata, and low-frequency troubleshooting fields.

## Required Rules

- Every production table defines an explicit default sort unless a documented exception exists.
- Every production table provides a domain-specific empty state heading and description.
- General-purpose tables should expose seven or fewer columns by default unless density is part of the job.
- Primary identifiers should be searchable and sortable when query-safe.
- Technical identifiers and secondary metadata should be toggleable and hidden by default where practical.
- Resource lists in the critical set persist search, sort, and filters in session.
- Existing action surfaces, RBAC behavior, confirmations, and centralized badge semantics stay unchanged.

## Pagination Profiles

| Surface | Page sizes | Default |
|---|---|---|
| Resource | `25, 50, 100` | `25` |
| Relation manager | `10, 25, 50` | `10` |
| Widget | `10` | `10` |
| Picker | `25, 50, 100` | `25` |
| Custom page | `25, 50, all` | `25` unless a page overrides it explicitly |

Implementation uses [TablePaginationProfiles.php](/Users/ahmeddarrazi/Documents/projects/TenantAtlas/app/Support/Filament/TablePaginationProfiles.php).

## Timestamp, Null, and ID Rules

- Prefer `->since()` for scan-first timestamps unless exact chronology is the primary task.
- Use `—` for missing values unless the domain needs a more specific placeholder.
- Long identifiers should remain copyable and readable without dominating the default layout.
- Prefer monospaced styling and tooltips for truncated technical identifiers.

## Review Checklist

- Primary column is obvious and query-safe.
- Default sort matches the table’s operational purpose.
- Empty-state copy explains what the list is waiting for.
- Hidden detail can be revealed in one toggle or one drill-in.
- Pagination profile matches the table class.
- Critical resource lists declare session persistence.
- No destructive action lost its confirmation or authorization guard.

## Rollout Audit

### Persistence Surfaces

The following resource lists must persist search, sort, and filters in session:

- `TenantResource`
- `PolicyResource`
- `BackupSetResource`
- `BackupScheduleResource`
- `ProviderConnectionResource`
- `FindingResource`
- `OperationRunResource`

### Documented Exceptions

- `RecentDriftFindings` and `RecentOperations` do not add table search because dashboard widgets are glance surfaces, not investigative workbenches.
- `Directory/Workspaces` keeps computed health and recent-failure metrics non-sortable and non-searchable because those values are derived per row.
- `InventoryCoverage` uses the custom-page pagination profile but keeps the broader `all` option and a `50`-row default because operators sometimes need a full matrix pass.
- Picker tables keep workflow-local search only; they do not persist state in session.

### Surface Inventory

| Surface | Class | Pagination | Default sort | Empty state | Persistence | Notes |
|---|---|---|---|---|---|---|
| Tenant list | `resource` | resource | `name asc` | yes | yes | Workspace-scoped create CTA remains in list page |
| Policy list | `resource` | resource | `display_name asc` | yes | yes | Sync CTA remains list-local |
| Backup set list | `resource` | resource | `created_at desc` | yes | yes | Create CTA remains list-local |
| Backup schedule list | `resource` | resource | `next_run_at asc` | yes | yes | Create CTA remains list-local |
| Provider connections | `resource` | resource | `display_name asc` | yes | yes | Empty-state CTA remains tenant-aware |
| Findings | `resource` | resource | `created_at desc` | yes | yes | Open filter remains the default |
| Monitoring operations | `resource-backed page` | resource | `created_at desc` | yes | yes | Canonical operations view uses `OperationRunResource::table()` |
| Entra groups | `resource` | resource | `display_name asc` | yes | no | Directory browse remains read-only |
| Alert deliveries | `resource` | resource | `id desc` | yes | no | Delivery history stays read-only |
| Alert rules | `resource` | resource | `name asc` | yes | no | Rule actions remain explicit per row |
| Alert destinations | `resource` | resource | `name asc` | yes | no | Destination test/send actions remain unchanged |
| Baseline profiles | `resource` | resource | `name asc` | yes | no | Create CTA remains list-local |
| Baseline snapshots | `resource` | resource | `captured_at desc` | yes | no | Snapshot browsing remains read-only |
| Inventory items | `resource` | resource | `last_seen_at desc` | yes | no | Scan-first recency view |
| Policy versions resource | `resource` | resource | `captured_at desc` | yes | no | Version history remains inspectable and immutable |
| Review packs | `resource` | resource | `created_at desc` | yes | no | Review workflow actions unchanged |
| Workspace resource | `resource` | resource | `name asc` | yes | no | Workspace create CTA remains list-local |
| Backup items | `relation_manager` | relation manager | `policy.display_name asc` | yes | no | Action semantics unchanged |
| Policy versions | `relation_manager` | relation manager | `version_number desc` | yes | no | Existing relation query preserved |
| Backup schedule operation runs | `relation_manager` | relation manager | `created_at desc` | yes | no | Existing record view preserved |
| Tenant memberships | `relation_manager` | relation manager | `created_at desc` | yes | no | Role management unchanged |
| Workspace memberships | `relation_manager` | relation manager | `created_at desc` | yes | no | Role management unchanged |
| Baseline tenant assignments | `relation_manager` | relation manager | `created_at desc` | yes | no | Assignment action unchanged |
| Inventory coverage | `custom_page` | custom page | `label asc` | yes | no | Keeps `all` pagination option |
| System directory tenants | `custom_page` | custom page | `name asc` | yes | no | Search stays on meaningful identity fields |
| System directory workspaces | `custom_page` | custom page | `name asc` | yes | no | Computed metrics remain exceptions |
| Ops runs | `custom_page` | custom page | `id desc` | yes | no | Platform triage actions unchanged |
| Ops failures | `custom_page` | custom page | `id desc` | yes | no | Platform triage actions unchanged |
| Ops stuck | `custom_page` | custom page | `id desc` | yes | no | Platform triage actions unchanged |
| Access logs | `custom_page` | custom page | `recorded_at desc` | yes | no | Read-only operational surface |
| Repair workspace owners | `custom_page` | custom page | `name asc` | yes | no | Repair action unchanged |
| Recent drift findings | `widget` | widget | `created_at desc` | yes | no | No search by design |
| Recent operations | `widget` | widget | `created_at desc` | yes | no | No search by design |
| Backup set policy picker | `picker` | picker | `display_name asc` | yes | no | Workflow-local search only |
| Entra group picker | `picker` | picker | `display_name asc` | yes | no | Workflow-local search only |
| Settings catalog table | `picker` | picker | `definition asc` | yes | no | Workflow-local search only |
