TenantAtlas/docs/audits/filter-audit-comprehensive.md
2026-03-09 00:16:50 +01:00

28 KiB
Raw Permalink Blame History

TenantPilot — Comprehensive Filter Audit

Stack: Laravel 12 · Filament v5 · Livewire v4 · PostgreSQL Scope: Every table surface across Admin Panel, System Console, Dashboard Widgets & Livewire Pickers Date: 2025-07-11


1. Executive Summary

The platform has 36 table surfaces across 18 Resources, 6 RelationManagers, 9 custom/system pages, 2 dashboard widgets, and 3 Livewire picker components.

Key findings:

Metric Value
Surfaces with ≥1 filter 16 of 36 (44 %)
Surfaces with zero filters 20 of 36 (56 %)
Total filter instances ~54
Surfaces with persistFiltersInSession() 7 of 36 (19 %)
Surfaces with filter defaults 6
Filter types used SelectFilter (dominant), Filter::make (boolean), TrashedFilter, TernaryFilter, DatePicker range
Highest filter count on one surface 9 (FindingResource)
Resources with zero filters that need them 5+ (identified below)

Critical inconsistencies:

  • Session persistence is applied to only 7 of 16 filtered surfaces — the other 9 filtered surfaces lose user state on navigation.
  • Filter defaults vary wildly: some surfaces pre-filter to "active" records, others show everything.
  • No date range filter exists on surfaces that clearly need one (Findings, AlertDelivery, RestoreRun).
  • Status/enum filters use inconsistent option sets and labeling across the platform.
  • The TrashedFilter (Active/All/Archived) pattern is consistent where used but only applied to 4 resources.

2. Complete Surface Inventory

2A. Resources (18)

# Resource Domain Scope Filters Persist? Default
1 PolicyResource Inventory Tenant 4 (visibility, policy_type, category, platform) visibility=active
2 FindingResource Governance Tenant 9 (open, overdue, high_severity, my_assigned, status, finding_type, evidence_fidelity, scope_key, run_ids) open=true
3 OperationRunResource Monitoring Workspace 6 (tenant_id, type, status, outcome, initiator_name, created_at) tenant=active, created_at=30d
4 TenantResource Settings Workspace 3 (trashed, environment, app_status) trashed=active
5 BackupScheduleResource Backups Tenant 2 (trashed, enabled_state)
6 BackupSetResource Backups Tenant 1 (trashed)
7 ProviderConnectionResource Settings Workspace 5 (tenant, provider, status, health_status, default_only) tenant=scoped
8 PolicyVersionResource Inventory Tenant 1 (trashed)
9 RestoreRunResource Backups Tenant 1 (trashed)
10 InventoryItemResource Inventory Tenant 2 (policy_type, category)
11 ReviewPackResource Reporting Tenant 1 (status)
12 AlertDeliveryResource Monitoring Workspace 3 (status, event_type, alert_destination_id)
13 EntraGroupResource Directory Tenant 2 (stale, group_type)
14 AlertRuleResource Monitoring Workspace 0
15 AlertDestinationResource Monitoring Workspace 0
16 BaselineProfileResource Governance Workspace 0
17 BaselineSnapshotResource Governance Workspace 0
18 WorkspaceResource Settings Workspace 0

2B. RelationManagers (6)

# RelationManager Parent Filters Notes
1 VersionsRelationManager PolicyResource 0 (->filters([])) Explicit empty
2 BackupItemsRelationManager BackupSetResource 0 (->filters([])) Explicit empty
3 BackupScheduleOperationRunsRelationManager BackupScheduleResource 0 (->filters([])) Explicit empty
4 BaselineTenantAssignmentsRelationManager BaselineProfileResource 0 No filters block
5 TenantMembershipsRelationManager TenantResource 0 No filters block
6 WorkspaceMembershipsRelationManager WorkspaceResource 0 No filters block

2C. Custom & System Pages (9)

# Page Panel Filters Notes
1 InventoryCoverage Admin 1-2 (category, restore mode) Dynamic — restore filter only if options exist
2 Monitoring/Operations Admin Reuses OperationRunResource::table() Inherits 6 filters + persistence
3 System/Ops/Runs System 0 No filters on platform ops listing
4 System/Ops/Failures System 0 Pre-filtered query (failed only), no user filters
5 System/Ops/Stuck System 0 Pre-filtered via StuckRunClassifier, no user filters
6 System/Directory/Tenants System 0 No filters
7 System/Directory/Workspaces System 0 No filters
8 System/Security/AccessLogs System 0 No filters
9 System/RepairWorkspaceOwners System 0 No filters

2D. Dashboard Widgets (2)

# Widget Filters Notes
1 RecentDriftFindings 0 Polling-based, no filter UI
2 RecentOperations 0 Polling-based, no filter UI

2E. Livewire Picker Components (3)

# Component Filters Notes
1 BackupSetPolicyPickerTable 5 (policy_type, platform, synced_within, ignored, has_versions) Most filters of any picker; synced_within default=7, ignored default=false
2 EntraGroupCachePickerTable 2 (stale, group_type) Mirrors EntraGroupResource filters
3 SettingsCatalogSettingsTable 0 Read-only inspection table; purely search-driven

3. Metrics Dashboard

3A. Filter Type Distribution

Filter Type Count Surfaces Using It
SelectFilter ~35 PolicyResource, FindingResource, OperationRunResource, TenantResource, BackupScheduleResource, ProviderConnectionResource, InventoryItemResource, ReviewPackResource, AlertDeliveryResource, EntraGroupResource, InventoryCoverage, BackupSetPolicyPicker, EntraGroupCachePicker
Filter::make (boolean/custom) ~8 FindingResource (4: open, overdue, high_severity, my_assigned), ProviderConnectionResource (1: default_only), FindingResource (2: scope_key, run_ids)
TrashedFilter 4 TenantResource, BackupScheduleResource, BackupSetResource, PolicyVersionResource, RestoreRunResource
TernaryFilter 1 BackupSetPolicyPickerTable (ignored)
Date range (DatePicker) 1 OperationRunResource (created_at)

3B. Persistence Coverage

Category With Persistence Without Total
Resources with filters 7 6 13
RelationManagers 0 6 6
Custom/System Pages 1 (via reuse) 8 9
Widgets 0 2 2
Livewire Pickers 0 3 3

3C. Default Filter Coverage

Surface Filter Default Value Impact
PolicyResource visibility active Hides ignored policies on first load
FindingResource open true Shows only open findings on first load
OperationRunResource tenant_id Active tenant Scopes to current tenant context
OperationRunResource created_at Last 30 days Time-bounds initial view
TenantResource trashed true (Active) Hides archived tenants
BackupSetPolicyPickerTable synced_within 7 (7 days) Only recently synced policies
BackupSetPolicyPickerTable ignored false Hides ignored policies
ProviderConnectionResource tenant Scoped tenant external_id Pre-scopes to current tenant

4. Detailed Findings Per Table

4.1 PolicyResource

Filters (4):

SelectFilter('visibility') → default('active'), custom query (active hides ignored_at)
SelectFilter('policy_type') → from config, no searchable
SelectFilter('category') → custom query mapping types to categories
SelectFilter('platform') → from distinct DB values

Implicit filter: modifyQueryUsing hides policies not synced in 7 days.

Assessment:

  • Persistence: full (search, sort, filters)
  • Default: sensible (filters to active policies)
  • ⚠️ policy_type options come from config — could be ->searchable() for large lists
  • ⚠️ platform options use raw DB distinct — no label formatting
  • ⚠️ No date range filter (last_synced_at is sortable but not filterable)

4.2 FindingResource

Filters (9) — highest in the product:

Filter::make('open') → boolean quick, default(true)
Filter::make('overdue') → boolean quick
Filter::make('high_severity') → boolean quick
Filter::make('my_assigned') → boolean quick
SelectFilter('status') → 8 manual options
SelectFilter('finding_type') → 3 manual options
SelectFilter('evidence_fidelity') → 2 manual options
Filter::make('scope_key') → TextInput form
Filter::make('run_ids') → 2 TextInputs (baseline + current run)

Assessment:

  • Persistence: full
  • Default: sensible (open=true)
  • ⚠️ 9 filters may overwhelm users — consider filter groups or collapsible sections
  • ⚠️ scope_key and run_ids are advanced/developer filters — should be in an "Advanced" section
  • ⚠️ No date range filter (created_at, due_at exist but are not filterable)
  • ⚠️ status has 8 manual options — should use enum for consistency

4.3 OperationRunResource

Filters (6):

SelectFilter('tenant_id') → dynamic default=active tenant, relationship options, searchable
SelectFilter('type') → from distinct DB values
SelectFilter('status') → from OperationRunStatus enum
SelectFilter('outcome') → from OperationRunOutcome enum with labels
SelectFilter('initiator_name') → from distinct DB, searchable
Filter::make('created_at') → DatePicker from/until, default=last 30 days

Assessment:

  • Persistence: full
  • Defaults: excellent (tenant + date range scoped)
  • Best filter UX in the product — good model for others
  • ⚠️ type uses raw DB distinct instead of OperationCatalog labels
  • Date range filter — only surface that has one

4.4 TenantResource

Filters (3):

TrashedFilter::make() → labels (Active/All/Archived), default(true)
SelectFilter('environment') → 4 options
SelectFilter('app_status') → 4 options

Assessment:

  • Persistence: full
  • TrashedFilter pattern with default
  • Good but minimal

4.5 BackupScheduleResource

Filters (2):

TrashedFilter::make() → Active/All/Archived
SelectFilter('enabled_state') → custom query

Assessment:

  • Persistence: full
  • ⚠️ Only 2 filters for a moderately complex resource

4.6 BackupSetResource

Filters (1):

TrashedFilter::make() → Active/All/Archived

Assessment:

  • Persistence: full
  • ⚠️ Minimal filtering — no status, no date range, no policy type summary filter

4.7 ProviderConnectionResource

Filters (5):

SelectFilter('tenant') → default=scoped tenant external_id, custom query
SelectFilter('provider') → 1 option (microsoft)
SelectFilter('status') → 4 options
SelectFilter('health_status') → 4 options
Filter::make('default_only') → boolean

Assessment:

  • Persistence: full
  • Good filter coverage for the domain
  • ⚠️ provider filter has only 1 option — may be premature; could be hidden until >1 provider exists

4.8 PolicyVersionResource

Filters (1):

TrashedFilter::make() → Active/All/Archived

Assessment:

  • No persistence
  • ⚠️ Missing: policy_type, platform, date range (captured_at)
  • ⚠️ Gap: this is a high-traffic resource with version history — more filters needed

4.9 RestoreRunResource

Filters (1):

TrashedFilter::make() → Active/All/Archived

Assessment:

  • No persistence
  • ⚠️ Missing: status, outcome, date range (started_at), dry_run flag
  • ⚠️ Gap: operators need to quickly find failed or dry-run restores

4.10 InventoryItemResource

Filters (2):

SelectFilter('policy_type') → searchable
SelectFilter('category') → searchable

Assessment:

  • No persistence — critical gap since this is a frequently navigated list
  • ⚠️ Missing: platform, sync freshness, version count
  • ⚠️ Both filters are searchable — good, but should match PolicyResource pattern

4.11 ReviewPackResource

Filters (1):

SelectFilter('status') → from ReviewPackStatus enum

Assessment:

  • No persistence
  • ⚠️ Single filter is reasonable for current scope

4.12 AlertDeliveryResource

Filters (3):

SelectFilter('status') → 6 options
SelectFilter('event_type') → dynamic from DB
SelectFilter('alert_destination_id') → dynamic from DB

Assessment:

  • No persistence
  • ⚠️ Missing: date range (created_at), tenant filter if multi-tenant scope
  • ⚠️ Status options are manual strings — should use enum

4.13 EntraGroupResource

Filters (2):

SelectFilter('stale') → config-based staleness cutoff query
SelectFilter('group_type') → complex JSON/boolean query

Assessment:

  • No persistence
  • ⚠️ Complex custom queries — well-implemented but should document the staleness logic
  • ⚠️ Mirrors EntraGroupCachePickerTable filters — good consistency

4.144.18 Resources with ZERO filters

Resource Assessment
AlertRuleResource ⚠️ Missing: is_active toggle, event type, severity
AlertDestinationResource ⚠️ Missing: type/channel filter
BaselineProfileResource ⚠️ Missing: status filter
BaselineSnapshotResource ⚠️ Missing: state filter (gaps/complete)
WorkspaceResource Acceptable — small dataset, search suffices

5. Cross-Cutting Inconsistencies

5.1 Persistence Gap

7 resources have persistence. 11 resources + all 6 relation managers + 8 system pages do not.

The guard test (FilamentTableStandardsGuardTest) only enforces persistence on 7 "critical" resources:

  • TenantResource, PolicyResource, BackupSetResource, BackupScheduleResource
  • ProviderConnectionResource, FindingResource, OperationRunResource

Missing from guard: PolicyVersionResource, RestoreRunResource, InventoryItemResource, AlertDeliveryResource, EntraGroupResource, ReviewPackResource.

5.2 Filter Default Inconsistency

Pattern Active-by-default Behavior Surfaces
SelectFilter('visibility')->default('active') Custom query PolicyResource
Filter::make('open')->default() Boolean toggle FindingResource
TrashedFilter->default(true) Hides archived TenantResource
SelectFilter('tenant_id')->default(...) Scopes to context OperationRunResource
No default Shows everything 10+ surfaces

Problem: Users expect consistent behavior across similar resources. Some show "active" records by default, some show everything.

5.3 Status Filter Inconsistency

Status filtering is implemented differently across surfaces:

Surface Approach
OperationRunResource Enum → OperationRunStatus::class
FindingResource Manual array of 8 strings
AlertDeliveryResource Manual array of 6 strings
ReviewPackResource Enum → ReviewPackStatus::class
PolicyVersion/RestoreRun TrashedFilter (soft-delete based)

Recommendation: Always source status options from the model's status enum or a centralized catalog.

5.4 Date Range Filter Gap

Only OperationRunResource has a date range filter (created_at with DatePicker form).

Surfaces that should have one but don't:

  • FindingResource — has created_at, due_at
  • AlertDeliveryResource — has created_at
  • RestoreRunResource — has started_at, completed_at
  • PolicyVersionResource — has captured_at
  • BackupScheduleResource — has execution history

5.5 TrashedFilter Labeling

All 4 resources using TrashedFilter apply the same label pattern — this is consistent :

TrashedFilter::make()
    ->label('Archived')
    ->placeholder('Active')
    ->trueLabel('All')
    ->falseLabel('Archived')

5.6 Tenant Scoping in Filters

Surface Approach
OperationRunResource Explicit SelectFilter('tenant_id') with dynamic default
ProviderConnectionResource Explicit SelectFilter('tenant') with external_id default
AlertDeliveryResource No tenant filter (scoped in query only)
System pages No filters at all (but cross-tenant by design)

6. Repeated Patterns (Extractable)

Pattern A: TrashedFilter Standard

TrashedFilter::make()
    ->label('Archived')
    ->placeholder('Active')
    ->trueLabel('All')
    ->falseLabel('Archived')

Used in: TenantResource, BackupScheduleResource, BackupSetResource, PolicyVersionResource, RestoreRunResource Extraction: Could be a helper FilterPresets::trashedArchive().

Pattern B: Config-Sourced SelectFilter

SelectFilter::make('policy_type')
    ->label('Policy type')
    ->options($options) // from config or DB
    ->searchable()

Used in: PolicyResource, InventoryItemResource, BackupSetPolicyPickerTable Note: Options source varies (config vs DB distinct vs static array).

Pattern C: Dynamic DB Distinct Options

SelectFilter::make('platform')
    ->options(fn (): array => Policy::query()
        ->where(...)
        ->whereNotNull('platform')
        ->distinct()
        ->orderBy('platform')
        ->pluck('platform', 'platform')
        ->all())

Used in: PolicyResource (platform), OperationRunResource (type, initiator_name), BackupSetPolicyPickerTable (platform), AlertDeliveryResource (event_type, alert_destination_id) Risk: N+1 on every filter render — should cache or use ->options() with static method.

Pattern D: Date Range with DatePicker (ONLY ONE INSTANCE)

Filter::make('created_at')
    ->form([
        Forms\Components\DatePicker::make('from')->label('From'),
        Forms\Components\DatePicker::make('until')->label('Until'),
    ])
    ->query(...)
    ->indicateUsing(...)

Used only in: OperationRunResource Should be in: FindingResource, AlertDeliveryResource, RestoreRunResource, PolicyVersionResource

Pattern E: Boolean Quick Filters

Filter::make('open')
    ->query(fn (Builder $query): Builder => $query->where('status', 'open'))
    ->default()

Used in: FindingResource (4 boolean quick filters) Note: Filament v5's ToggleFilter or TernaryFilter may provide better UX than Filter::make with ->default().

Pattern F: Staleness Cutoff Filter

SelectFilter::make('stale')
    ->options(['1' => 'Stale', '0' => 'Fresh'])
    ->query(function (Builder $query, array $data) use ($cutoff): Builder { ... })

Used in: EntraGroupResource, EntraGroupCachePickerTable (consistent )


7.1 Tier System

Tier Surface Type Filter Requirements
Tier 1 — Critical High-traffic Resource lists (Policy, Finding, OperationRun, Tenant, InventoryItem) Full persistence, smart defaults, date range where applicable
Tier 2 — Important Moderate-traffic Resources (BackupSchedule, BackupSet, RestoreRun, PolicyVersion, ProviderConnection, AlertDelivery, EntraGroup) Persistence, domain-appropriate filters, defaults for soft-delete
Tier 3 — Standard Low-traffic Resources (ReviewPack, AlertRule, AlertDestination, BaselineProfile, BaselineSnapshot, Workspace) Optional persistence, basic status filter if applicable
Tier 4 — Embedded RelationManagers, Widgets, System pages No persistence needed, filters only if >50 typical records
Tier 5 — Pickers Modal picker tables Task-specific filters, smart defaults to reduce noise

7.2 Mandatory Filter Rules

  1. Persistence: All Tier 1 and Tier 2 surfaces MUST have ->persistFiltersInSession(), ->persistSearchInSession(), ->persistSortInSession().
  2. Soft-delete: Every resource with SoftDeletes MUST have the standard TrashedFilter with Active/All/Archived labels.
  3. Status enums: All status filters MUST source options from the model's enum — no manual string arrays.
  4. Date range: Surfaces with time-series data (created_at, captured_at, started_at) in Tier 12 SHOULD have a date range filter.
  5. Defaults: Surfaces that commonly have "inactive" records (ignored, archived, completed) SHOULD default to showing only "active" records.

7.3 Filter Catalog (Extractable Presets)

// Proposed: App\Support\Filament\FilterPresets
class FilterPresets
{
    public static function trashedArchive(): TrashedFilter;
    public static function dateRange(string $column, ?int $defaultDays = null): Filter;
    public static function policyType(?int $tenantId = null): SelectFilter;
    public static function platform(?int $tenantId = null): SelectFilter;
    public static function tenantScope(): SelectFilter;
}

8. Filament-Native Feasibility

Pattern Filament v5 Native? Notes
SelectFilter Core, well-supported
TrashedFilter Built-in, perfect for soft-delete
TernaryFilter Used once (BackupSetPolicyPicker); underutilized
Filter::make + ->default() Works but boolean toggles are better as TernaryFilter
Filter::make + DatePicker form Native pattern
->persistFiltersInSession() Just needs to be added
->indicateUsing() Critical UX — shows active filter badges
Filter groups / sections ⚠️ Not natively supported; would need layout-level workaround
FilterPresets helper Custom Thin wrapper — not against Filament philosophy
Filter guards (tests) Custom Already done via FilamentTableStandardsGuardTest

Verdict: 100% achievable with Filament-native APIs. The only custom code needed is a thin FilterPresets helper for DRY and a guard test expansion.


9. Prioritized Gap List & Refactor Plan

P0 — Critical (Session + Data Integrity)

# Surface Action Effort
1 InventoryItemResource Add persistFiltersInSession() + persistSearchInSession() + persistSortInSession() XS
2 PolicyVersionResource Add full persistence trio XS
3 RestoreRunResource Add full persistence trio XS
4 AlertDeliveryResource Add full persistence trio XS
5 EntraGroupResource Add full persistence trio XS
6 Guard test Expand persistence enforcement list to include all Tier 12 resources S

P1 — High (Missing Essential Filters)

# Surface Action Effort
7 RestoreRunResource Add status, outcome, date range filters S
8 PolicyVersionResource Add policy_type, platform, date range (captured_at) filters S
9 FindingResource Add date range filter (created_at/due_at) S
10 AlertDeliveryResource Add date range filter (created_at) S
11 InventoryItemResource Add platform, sync freshness filters S
12 BaselineProfileResource Add status filter XS

P2 — Medium (Consistency & UX Polish)

# Surface Action Effort
13 FindingResource status Refactor from manual strings to enum options XS
14 AlertDeliveryResource status Refactor from manual strings to enum options XS
15 OperationRunResource type Use OperationCatalog labels instead of raw DB strings XS
16 FilterPresets helper Extract TrashedFilter, dateRange, policyType presets S
17 ProviderConnectionResource Consider hiding provider filter until >1 provider exists XS
18 AlertRuleResource Add is_active, event_type filters S
19 System/Ops/Runs Add basic type/status/workspace filters M
20 System/Directory/Tenants Add workspace, status filters S

P3 — Low (Nice-to-Have)

# Surface Action Effort
21 FindingResource Move scope_key and run_ids filters to "Advanced" section S
22 BackupSetResource Add backup frequency or item count filter S
23 BaselineSnapshotResource Add state filter (has_gaps/complete) S
24 System/Security/AccessLogs Add status and date range filters S
25 ->indicateUsing() Add active filter indicators to all date range filters S

10. Test Strategy

Existing Coverage

  • FilamentTableStandardsGuardTest enforces: defaultSort, emptyStateHeading, persistence (7 resources), TablePaginationProfiles usage.

10.1 Expand Persistence Guard Add all Tier 12 resources to the persistence enforcement list.

10.2 Filter Consistency Guard

it('uses enum-based options for status filters', function () {
    // Scan all files for SelectFilter('status') and verify they use enum classes
});

it('applies TrashedFilter on all soft-deletable resource tables', function () {
    // Cross-reference models with SoftDeletes trait against resource filter lists
});

10.3 Filter Default Guard

it('applies smart defaults on tier-1 resource filters', function () {
    // Verify PolicyResource, FindingResource, OperationRunResource have defaults
});

10.4 Functional Filter Tests For each Tier 12 surface, test:

  • Filter applies correct query scope
  • Filter default shows expected subset
  • Filter clears fully when reset
  • Multiple filters compose correctly

11. Spec Recommendations

Proposed Spec: XXX-filter-ux-standardization

Scope: Establish and enforce a repo-wide filter UX standard.

Deliverables:

  1. FilterPresets helper class with extractable patterns
  2. Persistence trio added to all Tier 12 surfaces
  3. Date range filters added to 5 time-series surfaces
  4. Status filters migrated to enum-based options
  5. Guard test expanded with filter-specific assertions
  6. TrashedFilter standardized across all soft-deletable resources

Dependencies:

  • Builds on completed 125-table-ux-standardization spec
  • No breaking changes — purely additive

Estimated effort: ~34 working sessions


Appendix A: Full Filter Inventory Matrix

Surface SelectFilter Filter::make TrashedFilter TernaryFilter DatePicker Total Persist Defaults
PolicyResource 4 0 0 0 0 4 1
FindingResource 3 6 0 0 0 9 1
OperationRunResource 5 1 0 0 1 6 2
TenantResource 2 0 1 0 0 3 1
BackupScheduleResource 1 0 1 0 0 2 0
BackupSetResource 0 0 1 0 0 1 0
ProviderConnectionResource 4 1 0 0 0 5 1
PolicyVersionResource 0 0 1 0 0 1 0
RestoreRunResource 0 0 1 0 0 1 0
InventoryItemResource 2 0 0 0 0 2 0
ReviewPackResource 1 0 0 0 0 1 0
AlertDeliveryResource 3 0 0 0 0 3 0
EntraGroupResource 2 0 0 0 0 2 0
AlertRuleResource 0 0 0 0 0 0 0
AlertDestinationResource 0 0 0 0 0 0 0
BaselineProfileResource 0 0 0 0 0 0 0
BaselineSnapshotResource 0 0 0 0 0 0 0
WorkspaceResource 0 0 0 0 0 0 0
InventoryCoverage 12 0 0 0 0 12 0
BackupSetPolicyPickerTable 3 0 0 1 0 5* 2
EntraGroupCachePickerTable 2 0 0 0 0 2 0
All 6 RelationManagers 0 0 0 0 0 0 0
All 7 System pages 0 0 0 0 0 0 0
All 2 Widgets 0 0 0 0 0 0 0
SettingsCatalogSettingsTable 0 0 0 0 0 0 0

*BackupSetPolicyPickerTable has SelectFilter::make('synced_within') (custom query acting as date filter) + SelectFilter::make('has_versions') in addition to the standard ones.


Appendix B: Quick Reference — Which Files to Touch

P0 (Persistence only — 5 files + 1 test):

  • app/Filament/Resources/InventoryItemResource.php
  • app/Filament/Resources/PolicyVersionResource.php
  • app/Filament/Resources/RestoreRunResource.php
  • app/Filament/Resources/AlertDeliveryResource.php
  • app/Filament/Resources/EntraGroupResource.php
  • tests/Feature/Guards/FilamentTableStandardsGuardTest.php

P1 (New filters — 5 files):

  • app/Filament/Resources/RestoreRunResource.php
  • app/Filament/Resources/PolicyVersionResource.php
  • app/Filament/Resources/FindingResource.php
  • app/Filament/Resources/AlertDeliveryResource.php
  • app/Filament/Resources/InventoryItemResource.php

P2 (Presets + consistency — 4 files + 1 new):

  • app/Support/Filament/FilterPresets.php (new)
  • app/Filament/Resources/FindingResource.php (enum migration)
  • app/Filament/Resources/AlertDeliveryResource.php (enum migration)
  • app/Filament/Resources/OperationRunResource.php (labels)
  • app/Filament/Resources/BaselineProfileResource.php (status filter)