# 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.