## Summary - make `/admin` the canonical workspace-level home instead of implicitly forcing tenant context - add a new Filament workspace overview page with bounded workspace-safe widgets, quick actions, and empty states - align panel routing, middleware, redirect helpers, and tests with the new workspace-home semantics - add Spec 129 design artifacts, contracts, and focused Pest coverage for landing, navigation, content, operations, and authorization ## Validation - `vendor/bin/sail artisan test --compact tests/Feature/Filament/AdminHomeRedirectsToChooseTenantWhenWorkspaceSelectedTest.php tests/Feature/Filament/LoginRedirectsToChooseWorkspaceWhenMultipleWorkspacesTest.php tests/Feature/Filament/WorkspaceOverviewLandingTest.php tests/Feature/Filament/WorkspaceOverviewNavigationTest.php tests/Feature/Filament/WorkspaceOverviewContentTest.php tests/Feature/Filament/WorkspaceOverviewEmptyStatesTest.php tests/Feature/Filament/WorkspaceOverviewOperationsTest.php tests/Feature/Filament/WorkspaceOverviewAuthorizationTest.php tests/Feature/Filament/WorkspaceOverviewPermissionVisibilityTest.php tests/Feature/Filament/ChooseTenantRequiresWorkspaceTest.php tests/Feature/Guards/AdminWorkspaceRoutesGuardTest.php` - `vendor/bin/sail bin pint --dirty --format agent` ## Notes - Livewire v4.0+ compliance is preserved through Filament v5 usage. - Panel provider registration remains in `bootstrap/providers.php` for Laravel 12. - This feature adds a workspace overview page for the admin panel home; it does not introduce destructive actions. - No new Filament assets were added, so there is no additional `filament:assets` deployment requirement for this branch. - Manual browser QA for the quickstart scenarios was not completed in this session because the local browser opened at the Microsoft login flow without an authenticated test session. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #157
203 lines
7.7 KiB
Markdown
203 lines
7.7 KiB
Markdown
# Data Model: Workspace Home & Admin Landing
|
|
|
|
**Feature**: 129-workspace-admin-home | **Date**: 2026-03-09
|
|
|
|
## Overview
|
|
|
|
This feature introduces no new database tables. It adds a new workspace-level read model and UI composition layer over existing workspace-owned and tenant-owned records.
|
|
|
|
The design relies on existing persisted entities plus a few computed overview concepts:
|
|
|
|
1. a canonical workspace overview page,
|
|
2. capability-safe summary metrics,
|
|
3. bounded recent and needs-attention entries,
|
|
4. quick actions to existing canonical destinations.
|
|
|
|
## Existing Persistent Entities
|
|
|
|
### Workspace
|
|
|
|
| Attribute | Type | Notes |
|
|
|-----------|------|-------|
|
|
| `id` | int | Primary workspace identity |
|
|
| `name` | string | Primary visible workspace label on the overview |
|
|
| `slug` | string nullable | Used in some workspace routes |
|
|
| `archived_at` | timestamp nullable | Archived workspaces must not be selectable |
|
|
|
|
**Relationships**:
|
|
- has many `WorkspaceMembership`
|
|
- has many `Tenant`
|
|
|
|
**Validation / usage rules**:
|
|
- A workspace overview may render only when the workspace is currently selected and the actor remains a valid member.
|
|
- Archived or stale workspace context must resolve back through the chooser flow.
|
|
|
|
### WorkspaceMembership
|
|
|
|
| Attribute | Type | Notes |
|
|
|-----------|------|-------|
|
|
| `workspace_id` | int | Workspace isolation boundary |
|
|
| `user_id` | int | Actor membership |
|
|
| `role` | string | Drives capability resolution |
|
|
|
|
**Usage rules**:
|
|
- Non-members must receive 404 deny-as-not-found for workspace home access.
|
|
- Role and capability checks determine which overview surfaces and quick actions may appear.
|
|
|
|
### Tenant
|
|
|
|
| Attribute | Type | Notes |
|
|
|-----------|------|-------|
|
|
| `id` | int | Internal identity |
|
|
| `external_id` | string | Route key for tenant destinations |
|
|
| `workspace_id` | int | Tenant belongs to exactly one workspace |
|
|
| `name` | string | Used on chooser and downstream destinations |
|
|
| `status` | string | Only active, accessible tenants count toward overview metrics |
|
|
|
|
**Usage rules**:
|
|
- The workspace overview never requires a selected tenant.
|
|
- Tenant counts and tenant-linked actions must reflect only the current user's accessible tenant subset within the active workspace.
|
|
|
|
### OperationRun
|
|
|
|
| Attribute | Type | Notes |
|
|
|-----------|------|-------|
|
|
| `workspace_id` | int | Workspace-bound operational scope |
|
|
| `tenant_id` | int nullable | Nullable for some workspace-context monitoring cases |
|
|
| `status` | string | Used for active/recent operations summaries |
|
|
| `outcome` | string nullable | Used for needs-attention or recent-failure signals |
|
|
| `created_at` / `updated_at` | timestamps | Used for recency ordering |
|
|
|
|
**Usage rules**:
|
|
- The overview may show only a bounded, capability-safe subset of operation runs.
|
|
- The workspace home must not create, mutate, or transition `OperationRun` records.
|
|
|
|
### Alert / Finding Surfaces
|
|
|
|
| Concept | Notes |
|
|
|--------|-------|
|
|
| Alerts | May contribute an alert summary count or urgent list items if a safe workspace-scoped aggregate exists |
|
|
| Findings | May contribute one needs-attention metric or list entries if already available safely and cheaply |
|
|
|
|
**Usage rules**:
|
|
- The overview must not leak existence of unauthorized alerts or findings through counts or list rows.
|
|
- These surfaces are optional at render time: if safe aggregation is unavailable, they should be hidden or degrade to empty state.
|
|
|
|
## New Computed Read Models
|
|
|
|
### WorkspaceOverviewSurface
|
|
|
|
| Field | Type | Description |
|
|
|------|------|-------------|
|
|
| `workspace` | Workspace | Active workspace context shown on the page |
|
|
| `tenant_context_active` | bool | Informational only; must not change page scope |
|
|
| `summary_metrics` | list<WorkspaceSummaryMetric> | Small KPI set shown at top of page |
|
|
| `attention_items` | list<WorkspaceAttentionItem> | Bounded urgent items |
|
|
| `recent_operations` | list<WorkspaceRecentOperationItem> | Bounded latest relevant operations |
|
|
| `quick_actions` | list<WorkspaceQuickAction> | Existing canonical destinations permitted for the actor |
|
|
|
|
**Rules**:
|
|
- Must render without a selected tenant.
|
|
- Must not imply tenant context when `tenant_context_active` is false.
|
|
- Must remain complete even when most sub-surfaces are empty.
|
|
|
|
### WorkspaceSummaryMetric
|
|
|
|
| Field | Type | Description |
|
|
|------|------|-------------|
|
|
| `key` | string enum | `accessible_tenants`, `active_operations`, `alerts`, `needs_attention` |
|
|
| `label` | string | UI label |
|
|
| `value` | int | Capability-safe aggregate value |
|
|
| `destination_url` | string nullable | Canonical existing route when drill-down is allowed |
|
|
| `visible` | bool | Hidden when aggregate is not safe or not authorized |
|
|
|
|
**Rules**:
|
|
- Value must be cheap to compute and workspace-bounded.
|
|
- No metric may reveal unauthorized tenant-owned data.
|
|
|
|
### WorkspaceAttentionItem
|
|
|
|
| Field | Type | Description |
|
|
|------|------|-------------|
|
|
| `kind` | string enum | `failed_operation`, `high_alert`, `high_finding`, or equivalent supported type |
|
|
| `title` | string | Human-readable urgent label |
|
|
| `subtitle` | string nullable | Cheap contextual metadata |
|
|
| `severity` | string nullable | Rendered through existing badge semantics where applicable |
|
|
| `destination_url` | string | Canonical route |
|
|
| `occurred_at` | datetime | Ordering field |
|
|
|
|
**Rules**:
|
|
- Results must be bounded and ordered by recency or severity.
|
|
- Empty state is valid and must render intentionally.
|
|
|
|
### WorkspaceRecentOperationItem
|
|
|
|
| Field | Type | Description |
|
|
|------|------|-------------|
|
|
| `operation_run_id` | int | Canonical run identity |
|
|
| `title` | string | Operation label |
|
|
| `status` | string | Existing operation status |
|
|
| `outcome` | string nullable | Existing outcome label |
|
|
| `tenant_label` | string nullable | Optional, only when safe to show |
|
|
| `destination_url` | string | Canonical run detail or operations destination |
|
|
| `created_at` | datetime | Recency ordering |
|
|
|
|
**Rules**:
|
|
- Must not spoof tenant context if no tenant is selected.
|
|
- Result set must be capped.
|
|
|
|
### WorkspaceQuickAction
|
|
|
|
| Field | Type | Description |
|
|
|------|------|-------------|
|
|
| `key` | string enum | `choose_tenant`, `operations`, `alerts`, `switch_workspace`, `manage_workspaces` |
|
|
| `label` | string | Visible CTA text |
|
|
| `url` | string | Canonical existing destination |
|
|
| `visible` | bool | Capability-aware rendering flag |
|
|
| `kind` | string enum | `context`, `navigation`, `administration` |
|
|
|
|
**Rules**:
|
|
- `switch_workspace` and `manage_workspaces` must remain separate entries.
|
|
- No quick action may introduce a new workflow in this feature.
|
|
|
|
## State and Transition Notes
|
|
|
|
### Workspace access state
|
|
|
|
```text
|
|
No selected workspace
|
|
-> redirect to choose-workspace
|
|
|
|
Selected workspace + valid membership
|
|
-> render workspace overview at /admin
|
|
|
|
Selected workspace + stale membership
|
|
-> if the actor still has at least one valid workspace membership, clear stale session and redirect to choose-workspace
|
|
-> if the actor is no longer entitled to the active workspace scope, return 404 deny-as-not-found
|
|
```
|
|
|
|
### Tenant context behavior
|
|
|
|
```text
|
|
Selected workspace + no selected tenant
|
|
-> render workspace overview normally
|
|
|
|
Selected workspace + selected tenant
|
|
-> still render workspace overview at /admin
|
|
-> tenant-specific work remains explicit through choose-tenant or /admin/t/{tenant}
|
|
```
|
|
|
|
## Validation Rules
|
|
|
|
| Rule | Result |
|
|
|------|--------|
|
|
| `/admin` means workspace home, not tenant branching | Required |
|
|
| Workspace home must render without tenant context | Required |
|
|
| Non-members receive 404 semantics | Required |
|
|
| In-scope unauthorized actions or destinations remain hidden or 403 at target | Required |
|
|
| Overview metrics and lists are capability-safe and bounded | Required |
|
|
| Empty states remain intentional and complete | Required |
|
|
|
|
## Schema Impact
|
|
|
|
No schema migration is expected for this feature. |