## Summary - unify empty-state UX across the six in-scope Filament list pages - move empty-state ownership toward resource `table()` definitions while preserving existing RBAC behavior - add focused Pest coverage for empty-state rendering, CTA outcomes, populated-state regression behavior, and action-surface compliance - add the Spec 122 planning artifacts and product discovery documents used for this pass ## Changed surfaces - `PolicyResource` - `BackupSetResource` - `RestoreRunResource` - `BackupScheduleResource` - `WorkspaceResource` - `AlertDeliveryResource` ## Tests - `vendor/bin/sail artisan test --compact tests/Feature/Filament/EmptyStateConsistencyTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/Alerts/AlertDeliveryViewerTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/CreateCtaPlacementTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/PolicySyncStartSurfaceTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/BackupSetUiEnforcementTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/RestoreRunUiEnforcementTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Guards/ActionSurfaceContractTest.php` - `vendor/bin/sail bin pint --dirty --format agent` ## Notes - Filament v5 / Livewire v4.0+ compliance is preserved. - Panel provider registration remains unchanged in `bootstrap/providers.php`. - No new globally searchable resources were added. - Destructive actions were not introduced by this pass. - Alert Deliveries is documented as the explicit no-header-action exemption for the empty-state CTA relocation rule. - Manual light/dark visual QA evidence is still expected in the PR/review artifact set for the remaining checklist items (`T018`, `T025`). Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #148
177 lines
14 KiB
Markdown
177 lines
14 KiB
Markdown
# Feature Specification: Empty State Consistency Pass
|
||
|
||
**Feature Branch**: `122-empty-state-consistency`
|
||
**Created**: 2026-03-08
|
||
**Status**: Ready for Implementation
|
||
**Input**: Spec 122 — Empty State Consistency Pass (Unified empty states across all primary list pages)
|
||
|
||
## Clarifications
|
||
|
||
### Session 2026-03-08
|
||
|
||
- Q: What should the single empty-state CTA be for the read-only Alert Deliveries list? → A: Use “View alert rules” as the single CTA.
|
||
- Q: How should empty-state CTA visibility behave for members who lack the required capability? → A: Preserve existing per-resource behavior (disabled or hidden), but require explanation when shown disabled.
|
||
- Q: Where should the final empty-state configuration live? → A: Prefer resource `table()` definitions for empty-state heading/description/icon/action, and allow page helpers only when technically required.
|
||
- Q: How should screenshots and visual QA evidence be handled for this feature? → A: Attach screenshots / visual QA evidence to the PR or review, not as committed repo artifacts.
|
||
|
||
## Spec Scope Fields *(mandatory)*
|
||
|
||
- **Scope**: workspace
|
||
- **Primary Routes**:
|
||
- Admin UI list pages: Policies, Backup Sets, Restore Runs, Backup Schedules, Workspaces, Alert Deliveries
|
||
- **Data Ownership**:
|
||
- Tenant-owned lists: Policies, Backup Sets, Restore Runs, Backup Schedules
|
||
- Workspace-owned / workspace-context lists: Workspaces
|
||
- Workspace-context monitoring history list: Alert Deliveries (may include tenant-bound rows; tenant entitlement still applies)
|
||
- **RBAC**:
|
||
- Membership isolation: non-members are deny-as-not-found (404 semantics)
|
||
- Capability gating: members without the required capability receive authorization denial (403 on execution); UI may show disabled actions with explanatory tooltip
|
||
- Empty-state primary actions MUST remain capability-aware and MUST use the existing UI enforcement helpers already used by each module
|
||
|
||
## User Scenarios & Testing *(mandatory)*
|
||
|
||
### User Story 1 - First-run clarity on empty lists (Priority: P1)
|
||
|
||
As a first-time admin, I want to immediately understand what each list page represents when it has no data yet, and what my next best action is.
|
||
|
||
**Why this priority**: This is the first impression for new tenants/workspaces and for demos.
|
||
|
||
**Independent Test**: For each in-scope list page, with zero records, the page renders a complete empty state (icon + heading + description + one primary action).
|
||
|
||
**Acceptance Scenarios**:
|
||
|
||
1. **Given** I open an in-scope list page with no records, **When** the table renders, **Then** I see an icon, heading, description, and a single primary action.
|
||
2. **Given** I click the primary empty-state action, **When** I have the required permission, **Then** I am taken to the intended next step (or the intended operation is queued) without errors.
|
||
|
||
---
|
||
|
||
### User Story 2 - Enterprise-grade consistency during evaluation (Priority: P2)
|
||
|
||
As a product evaluator, I want empty pages to feel intentional and guided, with consistent hierarchy and tone.
|
||
|
||
**Why this priority**: In low-data environments, evaluators spend disproportionate time on empty states.
|
||
|
||
**Independent Test**: Visual and UI assertions confirm consistent presence and ordering of empty-state elements across all in-scope pages.
|
||
|
||
**Acceptance Scenarios**:
|
||
|
||
1. **Given** I visit each in-scope list page with no records, **When** I compare the empty states, **Then** they share consistent structure and enterprise-appropriate wording (no scaffold-like presentation).
|
||
|
||
---
|
||
|
||
### User Story 3 - Permission-aware guidance (Priority: P3)
|
||
|
||
As a workspace/tenant member without a required capability, I want to understand what I’m missing without the UI leaking access outside my membership scope.
|
||
|
||
**Why this priority**: Capability-aware UX reduces support load and aligns with RBAC-UX.
|
||
|
||
**Independent Test**: With membership but missing capability, the empty-state action is not actionable and explains why; server-side authorization remains enforced.
|
||
|
||
**Acceptance Scenarios**:
|
||
|
||
1. **Given** I am a valid member but lack the capability for the primary action, **When** I view the empty page, **Then** the CTA is disabled (or hidden where that’s the existing pattern) and communicates the missing permission.
|
||
2. **Given** I am not entitled to the workspace/tenant scope, **When** I attempt to access the list page, **Then** the app responds as not found (404 semantics).
|
||
|
||
### Edge Cases
|
||
|
||
- Empty state must still render correctly in dark mode and light mode.
|
||
- Empty state must behave correctly when the tenant context is missing/invalid (records are not shown and the page does not leak data).
|
||
- Alert Deliveries list can be empty even when alerts are configured (e.g., no alerts fired yet); copy must set that expectation.
|
||
|
||
## Requirements *(mandatory)*
|
||
|
||
**Constitution alignment (required):** This feature introduces no new external integrations and no new background work. It only adjusts how existing list pages communicate “no records” states and which single next-step action is presented.
|
||
|
||
**Constitution alignment (OPS-UX):** This feature MUST NOT change operation UX semantics. Where an empty-state action already queues an operation (e.g., sync), it continues to follow the existing 3-surface feedback contract and uses the canonical operation UX presenter.
|
||
|
||
**Constitution alignment (RBAC-UX):** This feature MUST NOT relax authorization. It reuses existing capability gating and server-side authorization. Membership remains deny-as-not-found (404 semantics); missing capability remains 403 on execution.
|
||
|
||
**Constitution alignment (Filament Action Surfaces):** This feature modifies list empty states and therefore MUST satisfy the Action Surface Contract for the “ListEmptyState” slot for each in-scope resource.
|
||
|
||
**Constitution alignment (UX-001 — Layout & Information Architecture):** Each in-scope list page’s empty state must have a specific title, an explanation, and exactly 1 CTA.
|
||
|
||
### Functional Requirements
|
||
|
||
- **FR-001**: Each in-scope primary list page MUST render a complete empty state when there are zero records.
|
||
- **FR-002**: Each empty state MUST include: icon, heading, description, and exactly one primary action.
|
||
- **FR-003**: Empty-state copy MUST be contextual to the module and explain both (a) why it’s empty and (b) the next best action.
|
||
- **FR-004**: Empty-state primary actions MUST remain capability-aware and MUST respect existing UI enforcement rules.
|
||
- **FR-004a**: The feature MUST preserve each resource’s existing CTA visibility model for capability enforcement rather than forcing all empty-state actions into a single visible-disabled or hidden-only pattern.
|
||
- **FR-004b**: When an empty-state CTA is shown disabled for a valid member, it MUST explain the missing permission using the resource’s existing helper text or tooltip pattern.
|
||
- **FR-005**: Empty-state rendering MUST remain correct in both light and dark mode.
|
||
- **FR-006**: Empty-state configuration MUST be defined in a single, consistent place per list page (to prevent drift and scaffold-like inconsistencies).
|
||
- **FR-006a**: Where Filament supports it, empty-state heading, description, icon, and action MUST be defined in the resource `table()` configuration.
|
||
- **FR-006b**: Page-level empty-state helpers MAY be used only when technically required, and MUST NOT split responsibility in a way that causes drift between heading/description/icon/action definitions.
|
||
- **FR-007**: Out-of-scope resources MUST remain unchanged, including: Finding, Entra Group, Policy Version, Operation Run.
|
||
- **FR-008**: The Alert Deliveries empty state MUST use a single navigational CTA labeled “View alert rules” to direct operators to the configuration surface most likely to explain or resolve an empty history.
|
||
- **FR-008a**: Alert Deliveries is the sole explicit UX-001 relocation exemption for this feature: the `View alert rules` CTA appears only in the empty state and MUST NOT persist as a table-header action once deliveries exist, because the surface remains intentionally read-only and header-action free.
|
||
|
||
### Module-specific empty-state copy (baseline direction)
|
||
|
||
- Policies: “No policies synced yet. Sync your first tenant to see Intune policies here.”
|
||
- Backup Sets: “No backup sets. Create a backup set to start protecting your configurations.”
|
||
- Restore Runs: “No restore runs. Start a restoration from a backup set.”
|
||
- Backup Schedules: “No schedules configured. Set up automated backups.”
|
||
- Workspaces: “No workspaces. Create your first workspace.”
|
||
- Alert Deliveries: “No alert deliveries. Deliveries appear automatically when alert rules fire.”
|
||
|
||
### Clarified CTA direction
|
||
|
||
- Alert Deliveries: use a single CTA labeled “View alert rules”.
|
||
|
||
## UI Action Matrix *(mandatory when Filament is changed)*
|
||
|
||
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|
||
|---|---|---|---|---|---|---|---|---|---|---|
|
||
| Policy list | Admin UI — Policies list page | “Sync from Intune” | View action | View + “More” (as-is) | As-is | “Sync from Intune” | As-is | N/A (read-only details) | No | CTA continues to queue the existing sync operation; no change to operation UX semantics. |
|
||
| Backup Sets list | Admin UI — Backup Sets list page | “Create” (existing behavior) | View action | View + “More” | Yes | “Create backup set” | As-is | Create/Edit as-is | Yes (existing audit logger) | Empty state becomes contextual and guided; CTA remains capability gated. |
|
||
| Restore Runs list | Admin UI — Restore Runs list page | “Create” (existing behavior) | As-is | As-is | Yes | “New restore run” | As-is | Create wizard as-is | Yes (existing audit logger) | Empty state becomes contextual and guided; CTA remains capability gated. |
|
||
| Backup Schedules list | Admin UI — Backup Schedules list page | “New backup schedule” (existing behavior) | Clickable row | View/Edit as-is | Yes | “New backup schedule” | Edit page header as-is | Save/Cancel as-is | No | CTA remains capability-aware; disabled state explains missing permission. |
|
||
| Workspaces list | Admin UI — Workspaces list page | “New workspace” (existing behavior) | View action | View/Edit as-is | No | “New workspace” | As-is | Save/Cancel as-is | No | Workspace membership isolation remains deny-as-not-found. |
|
||
| Alert Deliveries list | Monitoring — Alert Deliveries list page | None (read-only) | Clickable row | None | None | “View alert rules” (navigation) | View page (read-only) | N/A | No | Adds guided empty state despite read-only list; CTA is a next-step navigation to alert rule configuration, not a mutation. Explicit UX-001 exemption: this CTA does not relocate to the header when records exist. |
|
||
|
||
### RBAC clarification
|
||
|
||
- Preserve each resource’s current capability-aware CTA behavior (disabled vs hidden) rather than standardizing all empty-state actions to one pattern.
|
||
|
||
### Placement clarification
|
||
|
||
- Prefer resource `table()` definitions for empty-state heading, description, icon, and action; use page helpers only when technically required.
|
||
|
||
### Visual QA evidence clarification
|
||
|
||
- Screenshot and dark-mode visual QA evidence for affected resources must be attached to the PR or review record, not committed into the repository.
|
||
|
||
## Explicit exemptions
|
||
|
||
- **Alert Deliveries header-action exemption**: Alert Deliveries remains a read-only monitoring history surface with no persistent header action. Its single CTA (`View alert rules`) exists only while the list is empty. Once deliveries exist, the CTA does not relocate to the header; this is an explicit UX-001 exemption for this feature and must remain documented in the PR or review.
|
||
|
||
## Acceptance Criteria / Definition of Done
|
||
|
||
1. All in-scope resources render a full empty state when the table has no records.
|
||
2. Each empty state includes icon, heading, description, and exactly one primary CTA.
|
||
3. Empty-state actions preserve existing RBAC/UI-enforcement behavior.
|
||
4. Visual review confirms centered, intentional hierarchy consistent with the baseline reference pattern.
|
||
5. Dark mode renders correctly for every affected list page.
|
||
6. Populated-table behavior remains unchanged.
|
||
7. Screenshot or visual QA evidence for all affected resources is attached to the PR or review artifact set, rather than committed to the repository.
|
||
|
||
## Testing Requirements
|
||
|
||
- Feature/UI test per in-scope resource for empty table rendering.
|
||
- Feature/UI test coverage for each in-scope CTA outcome so empty-state actions either navigate to the intended next step or queue the intended existing operation.
|
||
- Permission test coverage to ensure CTA visibility or disabled state remains aligned with existing resource behavior.
|
||
- Manual visual verification in light and dark mode for every affected list page.
|
||
- Automated regression coverage for representative populated-table behavior, including CTA relocation for create-capable surfaces and the documented no-header-action exemption for Alert Deliveries.
|
||
- Smoke test confirming populated resources still render their standard tables and existing actions.
|
||
- PR/review attachment checklist confirming screenshots or equivalent visual QA evidence were captured for all affected resources.
|
||
|
||
## Success Criteria *(mandatory)*
|
||
|
||
### Measurable Outcomes
|
||
|
||
- **SC-001**: 100% of in-scope list pages display icon + heading + description + 1 CTA when empty.
|
||
- **SC-002**: In a first-run demo with no records, an evaluator can correctly identify the purpose and next action on each in-scope page without guidance.
|
||
- **SC-003**: Users without capability can still understand the required permission from the disabled/hidden state messaging, without any cross-scope leakage.
|
||
- **SC-004**: No regressions: populated tables continue to render their existing columns, actions, and behaviors unchanged.
|