# 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) ```text 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) ```text 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.