TenantAtlas/specs/058-tenant-ui-polish/research.md
2026-01-21 08:12:46 +01:00

3.3 KiB

Research — Tenant UI Polish (Dashboard + Inventory Hub + Operations)

Goal

Deliver a drift-first, tenant-scoped UI polish pass that is:

  • DB-only on render and on any auto-refresh.
  • Calm (polling only when needed; no modal churn).
  • Consistent IA (Inventory hub sub-navigation; canonical Operations).

Existing Code & Patterns (to reuse)

Operations

  • Canonical list/detail already exist via OperationRunResource (/operations).
  • Tenant scoping already enforced in OperationRunResource::getEloquentQuery().
  • Detail view already uses conditional polling with safeguards (tab hidden / modal open) via RunDetailPolling.

Inventory

  • Inventory entry page exists as InventoryLanding.
  • Inventory “Items” and “Sync Runs” are currently resources (InventoryItemResource, InventorySyncRunResource).
  • Inventory sync “start surface” already follows constitution rules: authorize → create/reuse OperationRun → enqueue job → “View run”.

Monitoring DB-only + Tenant isolation tests

  • Monitoring/Operations has DB-only tests and tenant scope tests.
  • Inventory landing + coverage have basic smoke tests.

Key Decisions

Decision: Use Filament clusters to implement the Inventory “hub” navigation

  • Decision: Create an Inventory cluster and attach:
    • InventoryLanding (page)
    • Inventory items resource
    • Inventory sync runs resource
    • InventoryCoverage (page)
  • Rationale: Filament clusters are designed for “common sub-navigation between pages”, including mixing pages and resources.
  • Notes:
    • Requires enabling cluster discovery in the panel provider.
    • Sub-navigation position will be set to Start to achieve left-side navigation.

Decision: Implement KPI headers as widgets (StatsOverviewWidget / TableWidget)

  • Decision: Use Filament widgets for KPI headers on:
    • Tenant dashboard (drift + ops)
    • Inventory hub (inventory KPIs)
    • Operations index (ops KPIs)
  • Rationale: Widgets are first-class, composable, and can optionally poll (with $pollingInterval) while remaining DB-only.

Decision: “Calm UI” auto-refresh strategy

  • Decision:
    • Dashboard + Operations index: enable polling only while active runs exist.
    • Widgets/tables: polling is disabled when no active runs exist.
    • No polling inside modals.
  • Rationale: Matches FR-012 and avoids background churn.
  • Implementation approach:
    • Use Filament polling mechanisms:
      • Widgets: $pollingInterval = null | '10s' depending on “active runs exist”.
      • Tables: enable $table->poll('10s') only when “active runs exist”.

Decision: No Graph / remote dependencies

  • Decision: All queries for this feature are Eloquent/PostgreSQL queries.
  • Rationale: Matches constitution and SC-005.

Alternatives Considered

  • Custom Blade layouts for hub navigation: Rejected because clusters provide consistent sub-nav across resources/pages without fragile view overrides.
  • Always-on polling: Rejected to comply with calm UI rules and avoid waste.
  • Keep Monitoring/Operations as canonical: Rejected because OperationRunResource is already the canonical Operations surface with correct routing and detail pages.

Open Questions

None — all “NEEDS CLARIFICATION” items are resolved for planning.