TenantAtlas/specs/122-empty-state-consistency/data-model.md
ahmido 73a3a62451 Spec 122: Empty state consistency pass (#148)
## 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
2026-03-08 02:17:51 +00:00

143 lines
5.3 KiB
Markdown

# Data Model: Empty State Consistency Pass
**Feature**: 122-empty-state-consistency | **Date**: 2026-03-08
## Overview
This feature does not add or modify database tables. It standardizes the UI contract for six existing Filament list surfaces when their datasets are empty.
The feature operates on existing resource/page configuration and existing authorization decisions.
## Existing UI Contract Concepts
### EmptyStateSurface
| Attribute | Type | Notes |
|-----------|------|-------|
| `resource` | string | Filament resource owning the list surface |
| `ownership_scope` | enum | `tenant`, `workspace`, or `workspace-context monitoring` |
| `heading` | string | Specific, contextual title for the empty state |
| `description` | string | Short explanation of why the list is empty and what to do next |
| `icon` | string | Heroicon name matching the module semantics |
| `primary_cta_label` | string | Exactly one user-facing CTA label |
| `primary_cta_type` | enum | `create`, `navigate`, or `queue-operation` |
| `capability_behavior` | enum | `disabled-with-explanation` or `hidden`, preserved per existing resource behavior |
| `definition_source` | enum | Prefer `resource.table`; fallback `list-page helper` only when required |
### EmptyStatePrimaryAction
| Attribute | Type | Notes |
|-----------|------|-------|
| `label` | string | Single guided next-step action |
| `target` | string | Destination page or mounted Filament action |
| `enforcement_helper` | string | Existing `UiEnforcement` / `WorkspaceUiEnforcement` pattern when applicable |
| `capability` | string/null | Existing canonical capability required to execute the action |
| `header_relocation_rule` | string | Same CTA appears in the table header when records exist, unless the surface has an explicit documented exemption |
### VisualQaEvidence
| Attribute | Type | Notes |
|-----------|------|-------|
| `surface` | string | One of the six in-scope list pages |
| `mode` | enum | `light` or `dark` |
| `artifact_location` | string | PR/review attachment reference; not committed repo data |
| `review_result` | enum | `pass` or `follow-up` |
## In-Scope Surface Instances
### Policy list
| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | Policies have not been synced for the tenant yet |
| Primary CTA type | Queue operation |
| Primary CTA label | `Sync from Intune` |
| Existing capability behavior | Capability-aware action using existing tenant sync enforcement |
### Backup Sets list
| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | No backup sets have been created yet |
| Primary CTA type | Create |
| Primary CTA label | `Create backup set` |
| Existing capability behavior | Preserved from current create action behavior |
### Restore Runs list
| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | No restore workflows have been initiated yet |
| Primary CTA type | Create |
| Primary CTA label | `New restore run` |
| Existing capability behavior | Preserved from current create action behavior |
### Backup Schedules list
| Field | Value |
|------|-------|
| Scope | Tenant-owned |
| Empty reason | No automated backup schedules are configured |
| Primary CTA type | Create |
| Primary CTA label | `New backup schedule` |
| Existing capability behavior | Disabled-with-tooltip for valid members without schedule-manage capability |
### Workspaces list
| Field | Value |
|------|-------|
| Scope | Workspace-owned |
| Empty reason | No active workspaces are available to the member |
| Primary CTA type | Create |
| Primary CTA label | `New workspace` |
| Existing capability behavior | Preserved from current workspace create action behavior |
### Alert Deliveries list
| Field | Value |
|------|-------|
| Scope | Workspace-context monitoring |
| Empty reason | No alert deliveries have occurred yet |
| Primary CTA type | Navigate |
| Primary CTA label | `View alert rules` |
| Existing capability behavior | Read-only list; CTA navigates to the configuration surface most likely to explain the empty history |
| Header relocation rule | Explicit exemption: CTA appears only while the list is empty; non-empty state remains header-action free |
## Relationships
```text
EmptyStateSurface
-> has one EmptyStatePrimaryAction
-> belongs to one existing Filament resource list page
-> is validated by programmatic tests
-> is visually confirmed by PR/review evidence in light and dark mode
```
## State Transition
```text
[0 records on list page]
-> render complete empty-state contract
(icon + heading + description + exactly 1 CTA)
-> user follows CTA or waits for data-producing event
[records exist]
-> empty state disappears
-> primary CTA relocates to the table header when applicable
-> Alert Deliveries remains header-action free by explicit exemption
-> populated table behavior remains unchanged
```
## Validation Rules
| Rule | Result |
|------|--------|
| Exactly one empty-state CTA per in-scope list | Required |
| Empty-state CTA must preserve existing authorization behavior | Required |
| Non-members continue to receive 404 semantics | Required |
| Members missing capability continue to get 403 on execution | Required |
| Alert Deliveries CTA is navigational, not mutating | Required |
| No schema, route, or Graph contract changes | Required |