TenantAtlas/specs/125-table-ux-standardization/research.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

7.1 KiB
Raw Permalink Blame History

Research: Filament Table UX Standardization & List Consistency

Decision 1: Use native Filament table configuration as the primary implementation path

  • Decision: Standardize behavior directly in each tables existing table() definition and page-level empty-state hooks, using native Filament methods as the default approach.
  • Rationale: The repo already defines table behavior locally across resources, relation managers, widgets, system pages, and Livewire picker tables. Keeping the standard inline preserves clarity, aligns with the specs convention-first goal, and avoids introducing a second configuration language on top of Filament.
  • Alternatives considered:
    • Build a generic table DSL with primary/context/detail declarations: rejected because it would hide normal Filament behavior and create a parallel framework to maintain.
    • Make macros the default rollout path: rejected because the current inconsistencies are mostly judgment and information-architecture issues, not missing framework capability.

Decision 2: Keep shared support intentionally tiny and mechanical

  • Decision: Do not introduce a large shared base class or trait hierarchy. Only allow tiny shared support where duplication is purely mechanical, with pagination-profile helpers as the most likely candidate.
  • Rationale: The workspace currently has no shared StandardTableDefaults or equivalent helper. Introducing a broad helper layer at the same time as a repo-wide cleanup would increase migration risk and make reviews harder.
  • Alternatives considered:
    • Add a StandardTableDefaults trait and force every table through it: rejected because it would centralize too many domain-specific decisions and make exceptions harder to reason about.
    • Keep every pagination and persistence setting fully duplicated forever: rejected because a very small helper for page-size arrays may be justified once the first rollout wave proves the pattern is stable.

Decision 3: Treat resource-list persistence as mandatory and relation-manager persistence as optional

  • Decision: Enable session persistence for search, sort, and filters on resource list tables in the first rollout wave. Leave relation managers, widgets, picker tables, and custom pages on an opt-in basis where the behavior fits naturally.
  • Rationale: The audit gap is strongest on resource lists, and Filament-native session persistence maps directly to that need. Extending persistence to every table surface immediately would expand scope and create more state-management edge cases than the spec requires.
  • Alternatives considered:
    • Add persistence to every table surface immediately: rejected because it increases rollout complexity without matching the strongest user pain first.
    • Skip persistence and rely only on query-string state: rejected because the spec explicitly targets keeping list context across refreshes.

Decision 4: Use a documented Primary / Context / Detail model rather than code-level column metadata

  • Decision: Express Primary, Context, and Detail as a repo review convention backed by examples, not as a new code abstraction.
  • Rationale: The missing consistency is mostly a design-review problem. A documented tier model gives reviewers and implementers a common language while letting each table remain explicit and readable.
  • Alternatives considered:
    • Add a primaryColumn() / detailColumn() API wrapper: rejected because it would obscure normal Filament column configuration and encourage over-abstraction.
    • Leave visibility choices fully ad hoc: rejected because that is the exact drift pattern the spec is meant to stop.

Decision 5: Standardize timestamps, nulls, and IDs using native column methods

  • Decision: Use native Filament column behavior for relative timestamps, placeholders, toggle-hidden detail fields, copyable identifiers, truncation, and tooltips instead of custom renderers wherever possible.
  • Rationale: The repo already uses since(), dateTime(), toggleable(), copyable(), and empty-state APIs in several places. Extending those patterns is lower risk than inventing custom rendering helpers.
  • Alternatives considered:
    • Introduce custom Blade column views for standard timestamp and ID rendering: rejected because it would be more fragile and harder to apply consistently across many surfaces.
    • Leave timestamp and null formatting untouched during rollout: rejected because inconsistent rendering is one of the audited usability defects.

Decision 6: Preserve existing badge, action, and RBAC architecture

  • Decision: Do not redesign actions, badge mappings, or authorization mechanics as part of this feature. Keep actions and empty-state CTAs table-local, preserve centralized badge rendering through BadgeCatalog and BadgeRenderer, and maintain existing UI enforcement helpers.
  • Rationale: The repo already has centralized badge infrastructure and explicit action-level UI enforcement patterns. The spec explicitly excludes action redesign, and mixing that work into the rollout would create unnecessary risk.
  • Alternatives considered:
    • Standardize row actions and header actions in the same feature: rejected because actions are comparatively consistent and would create avoidable scope creep.
    • Rebuild badges as part of a broader “table facelift”: rejected because BADGE-001 requires centralized semantics and the current badge layer already exists.

Decision 7: Use performance exceptions deliberately and document them per table

  • Decision: Any new sort or search behavior on relation-backed or computed columns requires explicit query review, and some tables may keep documented exceptions instead of forcing full compliance.
  • Rationale: Direct code inspection already shows query-sensitive surfaces. app/Filament/System/Pages/Directory/Workspaces.php computes health and recent failures per row, and app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php already mixes operational actions with a dense relation-backed list. The standard must not worsen those hotspots.
  • Alternatives considered:
    • Force every meaningful-looking column to become sortable or searchable: rejected because that would create hidden N+1 or aggregate query regressions.
    • Exclude risk tables from the rollout entirely: rejected because the spec requires broad alignment, but exceptions can be documented where needed.

Decision 8: Roll out by table class and business criticality, not alphabetically

  • Decision: Implement the standard in phases: documentation and baseline, first-wave critical resource tables, then remaining resources and relation managers, then widgets, custom pages, and picker tables, followed by performance hardening.
  • Rationale: The current table surface spans roughly three dozen screens with uneven complexity. A phased rollout lets the project prove the standard on high-value tables before applying it repo-wide.
  • Alternatives considered:
    • Update every table in one large pass: rejected because it would be hard to review and too risky for query behavior.
    • Limit the effort to a small set of flagship resources: rejected because the spec is explicitly repo-wide and aims to stop future drift.