feat: workspace-first managed tenants + RBAC membership UI fixes (072) #87

Merged
ahmido merged 13 commits from feat/072-managed-tenants-workspace-enforcement into dev 2026-02-02 23:54:23 +00:00
7 changed files with 9 additions and 17 deletions
Showing only changes of commit 37a5587a45 - Show all commits

View File

@ -15,14 +15,14 @@
class WorkspaceResource extends Resource
{
protected static bool $isDiscovered = false;
protected static ?string $model = Workspace::class;
protected static bool $isScopedToTenant = false;
protected static ?string $recordTitleAttribute = 'name';
protected static bool $shouldRegisterNavigation = false;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-squares-2x2';
protected static string|UnitEnum|null $navigationGroup = 'Settings';

View File

@ -32,10 +32,6 @@ public function handle(Request $request, Closure $next): Response
return $next($request);
}
if (str_starts_with($path, '/admin/workspaces')) {
return $next($request);
}
if (in_array($path, ['/admin/no-access', '/admin/choose-workspace'], true)) {
return $next($request);
}

View File

@ -8,7 +8,6 @@
use App\Filament\Pages\NoAccess;
use App\Filament\Pages\Tenancy\RegisterTenant;
use App\Filament\Pages\TenantDashboard;
use App\Filament\Resources\Workspaces\WorkspaceResource;
use App\Models\Tenant;
use App\Support\Middleware\DenyNonMemberTenantAccess;
use Filament\Facades\Filament;
@ -40,7 +39,6 @@ public function panel(Panel $panel): Panel
->path('admin')
->login(Login::class)
->authenticatedRoutes(function (Panel $panel): void {
WorkspaceResource::registerRoutes($panel);
ChooseWorkspace::registerRoutes($panel);
ChooseTenant::registerRoutes($panel);
NoAccess::registerRoutes($panel);

View File

@ -40,7 +40,6 @@ public function handle(Request $request, Closure $next): Response
}
$tenantParameter = $request->route()->parameter('tenant');
$tenant = $panel->getTenant($tenantParameter);
if (! $tenant instanceof Tenant) {
@ -73,7 +72,6 @@ public function handle(Request $request, Closure $next): Response
}
Filament::setTenant($tenant, true);
$this->configureNavigationForRequest($panel);
return $next($request);

View File

@ -88,6 +88,10 @@ public function resolveInitialWorkspaceFor(User $user, ?Request $request = null)
if (! $workspace instanceof Workspace || ! $this->isWorkspaceSelectable($workspace) || ! $this->isMember($user, $workspace)) {
$user->forceFill(['last_workspace_id' => null])->save();
} else {
$session->put(self::SESSION_KEY, (int) $workspace->getKey());
return $workspace;
}
}

View File

@ -54,7 +54,6 @@ class="cursor-pointer rounded-lg border border-gray-200 p-4 dark:border-gray-800
<form x-ref="form" method="POST" action="{{ route('admin.select-tenant') }}" class="flex flex-col gap-3">
@csrf
<input type="hidden" name="tenant_id" value="{{ (int) $tenant->id }}" />
<div class="font-medium text-gray-900 dark:text-gray-100">
{{ $tenant->name }}
</div>

View File

@ -26,7 +26,6 @@
Route::get('/admin/consent/start', TenantOnboardingController::class)
->name('admin.consent.start');
// Panel root override: keep the app's workspace-first flow.
// Avoid Filament's tenancy root redirect which otherwise sends users to /admin/register-tenant
// when no default tenant can be resolved.
@ -50,7 +49,6 @@
return redirect()->to('/admin/choose-tenant');
})
->name('admin.home');
// Fallback route: Filament's layout generates this URL when tenancy registration is enabled.
// In this app, package route registration may not always define it early enough, which breaks
// rendering on tenant-scoped routes.
@ -125,7 +123,6 @@
Route::middleware(['web', 'auth', 'ensure-correct-guard:web', 'ensure-workspace-selected'])
->post('/admin/select-tenant', SelectTenantController::class)
->name('admin.select-tenant');
Route::bind('workspace', function (string $value): Workspace {
/** @var WorkspaceResolver $resolver */
$resolver = app(WorkspaceResolver::class);
@ -140,15 +137,15 @@
Route::middleware(['web', 'auth', 'ensure-workspace-member'])
->prefix('/admin/w/{workspace}')
->group(function (): void {
Route::get('/', fn () => redirect('/admin/tenants'))
Route::get('/', fn () => redirect('/admin/choose-tenant'))
->name('admin.workspace.home');
Route::get('/ping', fn () => response()->noContent())->name('admin.workspace.ping');
Route::get('/managed-tenants', fn () => redirect('/admin/tenants'))
Route::get('/managed-tenants', fn () => redirect('/admin/choose-tenant'))
->name('admin.workspace.managed-tenants.index');
Route::get('/managed-tenants/onboarding', fn () => redirect('/admin/tenants/create'))
Route::get('/managed-tenants/onboarding', fn () => redirect('/admin/register-tenant'))
->name('admin.workspace.managed-tenants.onboarding');
});