# 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 table’s 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 spec’s 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.
