TenantAtlas/specs/107-workspace-chooser/quickstart.md
ahmido e15eee8f26 fix: consolidate tenant creation + harden selection flows (#131)
## Summary
- Removes the legacy Tenant CRUD create page (`/admin/tenants/create`) so tenant creation is handled exclusively via the onboarding wizard.
- Updates tenant selection flows and pages to prevent Livewire polling/notification-related 404s on workspace-scoped routes.
- Aligns empty-state UX with enterprise patterns (avoid duplicate CTAs).

## Key changes
- Tenant creation
  - Removed `CreateTenant` page + route from `TenantResource`.
  - `TenantResource::canCreate()` now returns `false` (CRUD creation disabled).
  - Tenants list now surfaces an **Add tenant** action that links to onboarding (`admin.onboarding`).
- Onboarding wizard
  - Removed redundant legacy step-cards from the blade view (Wizard schema is the source of truth).
  - Disabled topbar on the onboarding page to avoid lazy-loaded notifications.
- Choose tenant
  - Enterprise UI redesign + workspace context.
  - Uses Livewire `selectTenant()` instead of a form POST.
  - Disabled topbar and gated BODY_END hook to avoid background polling.
- Baseline profiles
  - Hide header create action when table is empty to avoid duplicate CTAs.

## Tests
- `vendor/bin/sail artisan test --compact --filter='Onboarding|ManagedTenantOnboarding'`
- `vendor/bin/sail artisan test --compact --filter='ManagedTenantsLivewireUpdate'`
- `vendor/bin/sail artisan test --compact --filter='TenantSetup|TenantResourceAuth|TenantAdminAuth|ListTenants'`
- `vendor/bin/sail artisan test --compact --filter='BaselineProfile'`
- `vendor/bin/sail artisan test --compact --filter='ChooseTenant|TenantMake|TenantScoping|AdminTenantScoped|AdminHomeRedirect|WorkspaceContext'`

## Notes
- Filament v5 / Livewire v4 compatible.
- No new assets introduced; no deploy pipeline changes required.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #131
2026-02-22 19:54:24 +00:00

3.5 KiB

Quickstart: Workspace Chooser v1

Feature: 107-workspace-chooser | Date: 2026-02-22

Prerequisites

  • Branch: 107-workspace-chooser checked out
  • Sail running: vendor/bin/sail up -d
  • Existing workspace + user fixtures (factory-based)

Implementation Order

Phase A: Foundation (no visible changes)

  1. Add AuditActionId enum casesWorkspaceAutoSelected, WorkspaceSelected
  2. Extract WorkspaceRedirectResolver — shared tenant-count branching helper (DRY the 4 current copies)
  3. Tests for redirect resolver — verify 0/1/>1 tenant branching

Phase B: Middleware Refactor (core behavior change)

  1. Refactor EnsureWorkspaceSelected — implement 7-step algorithm from spec
    • Step 1: workspace-optional path bypass (keep existing isWorkspaceOptionalPath())
    • Step 2: ?choose=1 handling (new)
    • Step 3: stale session detection + flash warning (enhanced)
    • Step 4-5: single membership auto-resume + audit (new)
    • Step 6: last_workspace_id auto-resume + audit (new)
    • Step 7: fallback to chooser (existing)
  2. Middleware tests — all 7 steps covered

Phase C: Chooser Page Upgrade (UI changes)

  1. Refactor ChooseWorkspace page:
    • Remove "Create workspace" header action
    • Add withCount('tenants') to query
    • Load membership roles keyed by workspace_id
    • Expose getWorkspaceRole() and getWorkspaceMemberships() for Blade
  2. Update choose-workspace.blade.php:
    • Add role badge per card
    • Add tenant count per card
    • Add "Manage workspaces" link (capability-gated)
    • Update empty state (spec copy)
    • Replace form POST with wire:click="selectWorkspace({{ $workspace->id }})"
  3. Add audit logging in selectWorkspace() — emit workspace.selected with metadata
  4. Chooser page tests — metadata display, empty state, manage link visibility, audit events

Phase D: User Menu Integration

  1. Register "Switch workspace" in AdminPanelProvideruserMenuItems() with visibility condition
  2. User menu tests — visible when >1 workspace, hidden when 1

Phase E: Cleanup & Verification

  1. Replace inline tenant-branching in SwitchWorkspaceController and routes/web.php with WorkspaceRedirectResolver; add WorkspaceAuditLogger::log() for context_bar switch path in SwitchWorkspaceController
  2. Run full test suite — verify no regressions
  3. Pint formattingvendor/bin/sail bin pint --dirty
  4. Commit + push

Key Files to Understand First

File Why
app/Http/Middleware/EnsureWorkspaceSelected.php The middleware being refactored
app/Filament/Pages/ChooseWorkspace.php The page being upgraded
app/Support/Workspaces/WorkspaceContext.php The workspace session manager
app/Services/Audit/WorkspaceAuditLogger.php Where audit events are emitted
app/Support/Audit/AuditActionId.php Where enum cases are added
app/Http/Controllers/SwitchWorkspaceController.php POST switch (redirect resolver integration)
routes/web.php (lines 36-82) /admin route with duplicated branching

Verification Commands

# Run workspace-related tests
vendor/bin/sail artisan test --compact tests/Feature/Workspaces/

# Run specific middleware test
vendor/bin/sail artisan test --compact --filter=EnsureWorkspaceSelected

# Run chooser page test
vendor/bin/sail artisan test --compact --filter=ChooseWorkspacePage

# Format
vendor/bin/sail bin pint --dirty

# Full suite
vendor/bin/sail artisan test --compact