TenantAtlas/docs/ui/filament-table-standard.md
ahmido a4f5c4f122 Spec 125: standardize Filament table UX (#152)
## Summary
- standardize Filament table defaults across resources, relation managers, widgets, custom pages, and picker tables
- add shared pagination profiles, calm default column visibility, explicit empty states, and session persistence on designated critical resource lists
- complete Spec 125 artifacts, regression tests, and dashboard widget follow-up for lazy loading, sortable columns, and toggleable detail columns

## Verification
- `docker exec tenantatlas-laravel.test-1 php artisan test --compact --filter=BaselineCompareNow`
- `docker exec tenantatlas-laravel.test-1 php artisan test --compact --filter=TableStandardsBaseline`
- `docker exec tenantatlas-laravel.test-1 php artisan test --compact --filter=TableDetailVisibility`
- `docker exec tenantatlas-laravel.test-1 php artisan test --compact --filter=FilamentTableRiskExceptions`
- full suite run completed: `2017 passed, 10 failed, 8 skipped`
- manual browser QA completed on the tenant dashboard for lazy loading, sortable widget columns, toggleable hidden status columns, badges, and pagination

## Known Failures
The full suite still has 10 pre-existing failures unrelated to this branch:
- `Tests\\Unit\\OpsUx\\SummaryCountsNormalizerTest`
- `Tests\\Feature\\BackupWithAssignmentsConsistencyTest` (2 tests)
- `Tests\\Feature\\BaselineDriftEngine\\CaptureBaselineContentTest`
- `Tests\\Feature\\BaselineDriftEngine\\CompareContentEvidenceTest`
- `Tests\\Feature\\BaselineDriftEngine\\ResolverTest`
- `Tests\\Feature\\Filament\\TenantDashboardDbOnlyTest`
- `Tests\\Feature\\Operations\\ReconcileAdapterRunsJobTrackingTest`
- `Tests\\Feature\\ReviewPack\\ReviewPackRbacTest`
- `Tests\\Feature\\Verification\\VerificationReportRedactionTest`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #152
2026-03-08 22:54:56 +00:00

111 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Filament Table Standard
## 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 tables 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 |