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

7.7 KiB

Implementation Plan: Tenant UI Polish (Dashboard + Inventory Hub + Operations)

Branch: 058-tenant-ui-polish | Date: 2026-01-20 | Spec: specs/058-tenant-ui-polish/spec.md Input: Feature specification from specs/058-tenant-ui-polish/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.

Summary

  • Build a drift-first, tenant-scoped dashboard with “Needs Attention” and recent lists.
  • Make Inventory a hub using a Filament cluster to provide consistent left-side sub-navigation across Items / Sync Runs / Coverage.
  • Upgrade Operations index to “orders-style” with KPIs + status tabs filtering the existing OperationRunResource table.
  • Enforce DB-only renders (and DB-only polling) and a calm UI: polling only while active runs exist, and no polling churn in modals.

Technical Context

Language/Version: PHP 8.4.15 (Laravel 12.47.0)
Primary Dependencies: Filament v5.0.0, Livewire v4.0.1
Storage: PostgreSQL
Testing: Pest v4 (+ PHPUnit v12 runtime)
Target Platform: Web application (Filament admin panel) Project Type: Web (Laravel monolith)
Performance Goals: Dashboard/Inventory/Operations render quickly (target <2s for typical tenants) with efficient tenant-scoped queries and no N+1.
Constraints: DB-only for all page renders and any polling/auto-refresh; avoid UI churn in modals.
Scale/Scope: Tenant-scoped surfaces; KPI math on existing operation_runs, findings, inventory tables.

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

  • Inventory-first: clarify what is “last observed” vs snapshots/backups
  • Read/write separation: any writes require preview + confirmation + audit + tests
  • Graph contract path: Graph calls only via GraphClientInterface + config/graph_contracts.php
  • Deterministic capabilities: capability derivation is testable (snapshot/golden tests)
  • Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked
  • Run observability: long-running/remote/queued work creates/reuses OperationRun; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log
  • Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter
  • Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens

Status: No constitution violations for this feature (read-only, DB-only, tenant-scoped; no Graph calls added).

Project Structure

Documentation (this feature)

specs/058-tenant-ui-polish/
├── plan.md              # This file (/speckit.plan command output)
├── research.md          # Phase 0 output (/speckit.plan command)
├── data-model.md        # Phase 1 output (/speckit.plan command)
├── quickstart.md        # Phase 1 output (/speckit.plan command)
├── contracts/           # Phase 1 output (/speckit.plan command)
└── tasks.md             # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)

Source Code (repository root)

app/
├── Filament/
│   ├── Clusters/                  # New: Inventory cluster
│   ├── Pages/                     # New/updated: tenant dashboard, inventory landing, coverage
│   ├── Resources/                 # Updated: attach inventory resources to cluster; operations tabs/KPIs
│   └── Widgets/                   # New/updated: KPI header widgets
├── Models/                        # Existing: Tenant, OperationRun, Finding, InventoryItem, InventorySyncRun
└── Providers/Filament/
  └── AdminPanelProvider.php     # Update: discoverClusters(), dashboard page class

resources/
└── views/                         # Optional: partials/views for dashboard sections

tests/
└── Feature/
  ├── Monitoring/                # Existing: Operations DB-only + tenant scope tests
  └── Filament/                  # Existing + new: Inventory/Dashboard page tests

Structure Decision: Laravel monolith + Filament (v5) conventions. Implement UI changes via:

  • Filament Pages (dashboard + inventory pages)
  • Filament Clusters (inventory sub-navigation)
  • Filament Widgets (KPI headers / recent lists)
  • Filament Resource list tabs (operations index filtering)

Complexity Tracking

Fill ONLY if Constitution Check has violations that must be justified

None.

Phase 0 — Outline & Research (complete)

  • Output: specs/058-tenant-ui-polish/research.md
  • Key decisions captured:
    • Use Filament clusters for the Inventory hub sub-navigation.
    • Use Filament widgets for KPI headers.
    • Enable polling only while active runs exist.

Phase 1 — Design & Contracts (complete)

Data model

  • Output: specs/058-tenant-ui-polish/data-model.md
  • No schema changes required.

UI contracts

  • Output: specs/058-tenant-ui-polish/contracts/ui.md
  • Output: specs/058-tenant-ui-polish/contracts/polling.md

Provider registration (Laravel 11+)

  • Panel providers remain registered in bootstrap/providers.php (no changes required for this feature unless adding a new provider).

Livewire / Filament version safety

  • Livewire v4.0+ (required by Filament v5) is in use.

Asset strategy

  • Prefer existing Filament theme CSS and hook classes; avoid publishing Filament internal views.
  • No heavy assets expected; if any new panel assets are added, ensure deployment runs php artisan filament:assets.

Destructive actions

  • None introduced in this feature.

Constitution re-check (post-design)

  • Inventory-first: dashboard uses Inventory/Findings/OperationRun as last-observed state.
  • Read/write separation: this feature is read-only.
  • Graph contract path: no Graph calls added.
  • Tenant isolation: all queries remain tenant-scoped.
  • Run observability: only consumes existing OperationRun records; no new long-running work is introduced.
  • Data minimization: no new payload storage.

Phase 2 — Implementation Plan (next)

Story 1 (P1): Drift-first tenant dashboard

  • Create a custom Filament dashboard page (tenant-scoped) and wire it in AdminPanelProvider instead of the default Dashboard::class.
  • Implement drift + ops KPIs and “Needs Attention” + recent lists using DB-only Eloquent queries.
  • Implement conditional polling (only while active runs exist) using widget polling controls.
  • Tests:
    • Add DB-only coverage tests for the dashboard (no outbound HTTP; no queued jobs on render).
    • Add tenant scope tests for the dashboard.

Story 2 (P2): Inventory becomes a hub

  • Add discoverClusters() to AdminPanelProvider.
  • Create InventoryCluster and assign $cluster on inventory pages/resources.
  • Add a shared inventory KPI header (widget) across the cluster surfaces.
  • Tests:
    • Extend existing inventory page tests to assert cluster pages load and remain tenant-scoped.

Story 3 (P3): Operations index “orders-style”

  • Update OperationRunResource list page to:
    • Add KPI header widgets.
    • Add tabs: All / Active / Succeeded / Partial / Failed.
    • Enable table polling only while active runs exist.
  • Tests:
    • Extend operations tests to assert page renders with tabs and remains DB-only/tenant-scoped.